summaryrefslogtreecommitdiff
path: root/imap/src
diff options
context:
space:
mode:
Diffstat (limited to 'imap/src')
-rw-r--r--imap/src/ansilib/memmove.c40
-rw-r--r--imap/src/ansilib/memmove2.c49
-rw-r--r--imap/src/ansilib/memset.c40
-rw-r--r--imap/src/ansilib/strpbrk.c40
-rw-r--r--imap/src/ansilib/strstr.c45
-rw-r--r--imap/src/ansilib/strtok.c67
-rw-r--r--imap/src/ansilib/strtoul.c73
-rw-r--r--imap/src/c-client/auth_ext.c96
-rw-r--r--imap/src/c-client/auth_gss.c423
-rw-r--r--imap/src/c-client/auth_log.c117
-rw-r--r--imap/src/c-client/auth_md5.c495
-rw-r--r--imap/src/c-client/auth_pla.c133
-rw-r--r--imap/src/c-client/c-client.h55
-rw-r--r--imap/src/c-client/env.h45
-rw-r--r--imap/src/c-client/flstring.c91
-rw-r--r--imap/src/c-client/flstring.h30
-rw-r--r--imap/src/c-client/fs.h34
-rw-r--r--imap/src/c-client/ftl.h32
-rw-r--r--imap/src/c-client/imap4r1.c5672
-rw-r--r--imap/src/c-client/imap4r1.h281
-rw-r--r--imap/src/c-client/mail.c6337
-rw-r--r--imap/src/c-client/mail.h1838
-rw-r--r--imap/src/c-client/misc.c475
-rw-r--r--imap/src/c-client/misc.h110
-rw-r--r--imap/src/c-client/netmsg.c104
-rw-r--r--imap/src/c-client/netmsg.h32
-rw-r--r--imap/src/c-client/newsrc.c510
-rw-r--r--imap/src/c-client/newsrc.h43
-rw-r--r--imap/src/c-client/nl.h34
-rw-r--r--imap/src/c-client/nntp.c2224
-rw-r--r--imap/src/c-client/nntp.h56
-rw-r--r--imap/src/c-client/pop3.c1091
-rw-r--r--imap/src/c-client/rfc822.c2373
-rw-r--r--imap/src/c-client/rfc822.h127
-rw-r--r--imap/src/c-client/smanager.c129
-rw-r--r--imap/src/c-client/smtp.c793
-rw-r--r--imap/src/c-client/smtp.h76
-rw-r--r--imap/src/c-client/sslio.h70
-rw-r--r--imap/src/c-client/tcp.h59
-rw-r--r--imap/src/c-client/utf8.c2554
-rw-r--r--imap/src/c-client/utf8.h584
-rw-r--r--imap/src/c-client/utf8aux.c449
-rw-r--r--imap/src/c-client/utf8aux.h44
-rw-r--r--imap/src/charset/big5.c2016
-rw-r--r--imap/src/charset/cns11643.c8590
-rw-r--r--imap/src/charset/decomtab.c2909
-rw-r--r--imap/src/charset/gb_12345.c1114
-rw-r--r--imap/src/charset/gb_2312.c2795
-rw-r--r--imap/src/charset/ibm.c347
-rw-r--r--imap/src/charset/iso_8859.c308
-rw-r--r--imap/src/charset/jis_0208.c1092
-rw-r--r--imap/src/charset/jis_0212.c962
-rw-r--r--imap/src/charset/koi8_r.c48
-rw-r--r--imap/src/charset/koi8_u.c48
-rw-r--r--imap/src/charset/ksc_5601.c2673
-rw-r--r--imap/src/charset/tis_620.c51
-rw-r--r--imap/src/charset/tmap.c1487
-rw-r--r--imap/src/charset/viscii.c67
-rw-r--r--imap/src/charset/widths.c4136
-rw-r--r--imap/src/charset/windows.c228
-rw-r--r--imap/src/dmail/Makefile53
-rw-r--r--imap/src/dmail/dmail.1121
-rw-r--r--imap/src/dmail/dmail.c661
-rw-r--r--imap/src/dmail/dquota.c44
-rw-r--r--imap/src/dmail/dquota.h32
-rw-r--r--imap/src/imapd/Makefile68
-rw-r--r--imap/src/imapd/imapd.848
-rw-r--r--imap/src/imapd/imapd.c4608
-rw-r--r--imap/src/imapd/makefile.nt55
-rw-r--r--imap/src/imapd/makefile.ntk58
-rw-r--r--imap/src/imapd/makefile.w2k56
-rw-r--r--imap/src/ipopd/Makefile58
-rw-r--r--imap/src/ipopd/ipop2d.c711
-rw-r--r--imap/src/ipopd/ipop3d.c1082
-rw-r--r--imap/src/ipopd/ipopd.875
-rw-r--r--imap/src/ipopd/makefile.nt57
-rw-r--r--imap/src/ipopd/makefile.ntk58
-rw-r--r--imap/src/ipopd/makefile.w2k56
-rw-r--r--imap/src/mailutil/Makefile51
-rw-r--r--imap/src/mailutil/mailutil.1264
-rw-r--r--imap/src/mailutil/mailutil.c942
-rw-r--r--imap/src/mailutil/makefile.nt50
-rw-r--r--imap/src/mailutil/makefile.ntk51
-rw-r--r--imap/src/mailutil/makefile.w2k49
-rw-r--r--imap/src/mlock/Makefile51
-rw-r--r--imap/src/mlock/mlock.c175
-rw-r--r--imap/src/mtest/Makefile53
-rw-r--r--imap/src/mtest/makefile.nt49
-rw-r--r--imap/src/mtest/makefile.ntk51
-rw-r--r--imap/src/mtest/makefile.os253
-rw-r--r--imap/src/mtest/makefile.w2k49
-rw-r--r--imap/src/mtest/mtest.c813
-rw-r--r--imap/src/osdep/amiga/Makefile231
-rw-r--r--imap/src/osdep/amiga/ckp_std.c42
-rwxr-xr-ximap/src/osdep/amiga/drivers36
-rw-r--r--imap/src/osdep/amiga/dummy.c809
-rw-r--r--imap/src/osdep/amiga/dummy.h43
-rw-r--r--imap/src/osdep/amiga/env_ami.c1282
-rw-r--r--imap/src/osdep/amiga/env_ami.h95
-rw-r--r--imap/src/osdep/amiga/fdstring.c99
-rw-r--r--imap/src/osdep/amiga/fdstring.h39
-rw-r--r--imap/src/osdep/amiga/fs_ami.c71
-rw-r--r--imap/src/osdep/amiga/ftl_ami.c38
-rw-r--r--imap/src/osdep/amiga/gethstid.c38
-rw-r--r--imap/src/osdep/amiga/gr_waitp.c39
-rw-r--r--imap/src/osdep/amiga/log_std.c44
-rw-r--r--imap/src/osdep/amiga/mbx.c1855
-rw-r--r--imap/src/osdep/amiga/mh.c1283
-rw-r--r--imap/src/osdep/amiga/mix.c2834
-rwxr-xr-ximap/src/osdep/amiga/mkauths40
-rw-r--r--imap/src/osdep/amiga/mmdf.c2549
-rw-r--r--imap/src/osdep/amiga/mtx.c1371
-rw-r--r--imap/src/osdep/amiga/mx.c1287
-rw-r--r--imap/src/osdep/amiga/news.c738
-rw-r--r--imap/src/osdep/amiga/nl_ami.c92
-rw-r--r--imap/src/osdep/amiga/os_ami.c80
-rw-r--r--imap/src/osdep/amiga/os_ami.h54
-rw-r--r--imap/src/osdep/amiga/phile.c553
-rw-r--r--imap/src/osdep/amiga/pmatch.c89
-rw-r--r--imap/src/osdep/amiga/pseudo.c36
-rw-r--r--imap/src/osdep/amiga/pseudo.h30
-rw-r--r--imap/src/osdep/amiga/scandir.c81
-rw-r--r--imap/src/osdep/amiga/ssl_none.c141
-rw-r--r--imap/src/osdep/amiga/tcp_ami.c797
-rw-r--r--imap/src/osdep/amiga/tcp_ami.h48
-rw-r--r--imap/src/osdep/amiga/tenex.c1470
-rw-r--r--imap/src/osdep/amiga/tz_bsd.c38
-rw-r--r--imap/src/osdep/amiga/unix.c2708
-rw-r--r--imap/src/osdep/amiga/unix.h219
-rw-r--r--imap/src/osdep/amiga/write.c59
-rw-r--r--imap/src/osdep/dos/bezrkdos.c901
-rw-r--r--imap/src/osdep/dos/drivers.bat33
-rw-r--r--imap/src/osdep/dos/drivraux.bat28
-rw-r--r--imap/src/osdep/dos/dummy.h43
-rw-r--r--imap/src/osdep/dos/dummydos.c689
-rw-r--r--imap/src/osdep/dos/env_dos.c300
-rw-r--r--imap/src/osdep/dos/env_dos.h68
-rw-r--r--imap/src/osdep/dos/fdstring.c99
-rw-r--r--imap/src/osdep/dos/fdstring.h39
-rw-r--r--imap/src/osdep/dos/fs_dos.c62
-rw-r--r--imap/src/osdep/dos/ftl_dos.c38
-rw-r--r--imap/src/osdep/dos/makefile98
-rw-r--r--imap/src/osdep/dos/mkautaux.bat29
-rw-r--r--imap/src/osdep/dos/mkauths.bat33
-rw-r--r--imap/src/osdep/dos/mtestdbw.bat27
-rw-r--r--imap/src/osdep/dos/mtestdnf.bat27
-rw-r--r--imap/src/osdep/dos/mtestdnv.bat27
-rw-r--r--imap/src/osdep/dos/mtestdpc.bat27
-rw-r--r--imap/src/osdep/dos/mtestdwa.bat27
-rw-r--r--imap/src/osdep/dos/mtestwsk.bat27
-rw-r--r--imap/src/osdep/dos/mtxdos.c875
-rw-r--r--imap/src/osdep/dos/nl_dos.c61
-rw-r--r--imap/src/osdep/dos/os_dbw.c91
-rw-r--r--imap/src/osdep/dos/os_dbw.h43
-rw-r--r--imap/src/osdep/dos/os_dnf.c95
-rw-r--r--imap/src/osdep/dos/os_dnf.h44
-rw-r--r--imap/src/osdep/dos/os_dnv.c95
-rw-r--r--imap/src/osdep/dos/os_dnv.h43
-rw-r--r--imap/src/osdep/dos/os_dpc.c102
-rw-r--r--imap/src/osdep/dos/os_dpc.h41
-rw-r--r--imap/src/osdep/dos/os_dwa.c72
-rw-r--r--imap/src/osdep/dos/os_dwa.h43
-rw-r--r--imap/src/osdep/dos/os_wsk.c45
-rw-r--r--imap/src/osdep/dos/os_wsk.h48
-rw-r--r--imap/src/osdep/dos/pmatch.c89
-rw-r--r--imap/src/osdep/dos/tcp_dos.c434
-rw-r--r--imap/src/osdep/dos/tcp_dos.h51
-rw-r--r--imap/src/osdep/dos/tcp_dwa.c344
-rw-r--r--imap/src/osdep/dos/tcp_dwa.h45
-rw-r--r--imap/src/osdep/dos/tcp_wsk.c818
-rw-r--r--imap/src/osdep/dos/tcp_wsk.h50
-rw-r--r--imap/src/osdep/dos/write.c59
-rw-r--r--imap/src/osdep/mac/dummy.h43
-rw-r--r--imap/src/osdep/mac/dummymac.c295
-rw-r--r--imap/src/osdep/mac/env_mac.c236
-rw-r--r--imap/src/osdep/mac/env_mac.h58
-rw-r--r--imap/src/osdep/mac/fs_mac.c62
-rw-r--r--imap/src/osdep/mac/ftl_mac.c39
-rw-r--r--imap/src/osdep/mac/linkage.c37
-rw-r--r--imap/src/osdep/mac/linkage.h36
-rw-r--r--imap/src/osdep/mac/mtest.sit.hqx171
-rw-r--r--imap/src/osdep/mac/nl_mac.c74
-rw-r--r--imap/src/osdep/mac/os_mac.c82
-rw-r--r--imap/src/osdep/mac/os_mac.h69
-rw-r--r--imap/src/osdep/mac/osdep.h29
-rw-r--r--imap/src/osdep/mac/pmatch.c89
-rw-r--r--imap/src/osdep/mac/tcp_mac.c557
-rw-r--r--imap/src/osdep/mac/tcp_mac.h49
-rwxr-xr-ximap/src/osdep/nt/drivers.bat33
-rwxr-xr-ximap/src/osdep/nt/drivraux.bat30
-rw-r--r--imap/src/osdep/nt/dummy.h43
-rw-r--r--imap/src/osdep/nt/dummynt.c724
-rw-r--r--imap/src/osdep/nt/env_nt.c774
-rw-r--r--imap/src/osdep/nt/env_nt.h68
-rw-r--r--imap/src/osdep/nt/fdstring.c99
-rw-r--r--imap/src/osdep/nt/fdstring.h39
-rw-r--r--imap/src/osdep/nt/fs_nt.c62
-rw-r--r--imap/src/osdep/nt/ftl_nt.c38
-rw-r--r--imap/src/osdep/nt/ip4_nt.c184
-rw-r--r--imap/src/osdep/nt/ip6_nt.c288
-rw-r--r--imap/src/osdep/nt/kerb_mit.c74
-rw-r--r--imap/src/osdep/nt/kerb_w2k.c699
-rw-r--r--imap/src/osdep/nt/mailfile.h29
-rw-r--r--imap/src/osdep/nt/makefile.nt118
-rw-r--r--imap/src/osdep/nt/makefile.ntk118
-rw-r--r--imap/src/osdep/nt/makefile.w2k119
-rw-r--r--imap/src/osdep/nt/mbxnt.c1694
-rwxr-xr-ximap/src/osdep/nt/mkautaux.bat31
-rwxr-xr-ximap/src/osdep/nt/mkauths.bat33
-rw-r--r--imap/src/osdep/nt/mtxnt.c1232
-rw-r--r--imap/src/osdep/nt/nl_nt.c61
-rw-r--r--imap/src/osdep/nt/os_nt.c48
-rw-r--r--imap/src/osdep/nt/os_nt.h60
-rw-r--r--imap/src/osdep/nt/os_ntk.c51
-rw-r--r--imap/src/osdep/nt/os_old.c48
-rw-r--r--imap/src/osdep/nt/os_w2k.c49
-rw-r--r--imap/src/osdep/nt/pmatch.c89
-rw-r--r--imap/src/osdep/nt/pseudo.c36
-rw-r--r--imap/src/osdep/nt/pseudo.h30
-rwxr-xr-ximap/src/osdep/nt/setproto.bat29
-rw-r--r--imap/src/osdep/nt/ssl_none.c141
-rw-r--r--imap/src/osdep/nt/ssl_nt.c721
-rw-r--r--imap/src/osdep/nt/ssl_old.c625
-rw-r--r--imap/src/osdep/nt/ssl_w2k.c683
-rw-r--r--imap/src/osdep/nt/tcp_nt.c916
-rw-r--r--imap/src/osdep/nt/tcp_nt.h51
-rw-r--r--imap/src/osdep/nt/tenexnt.c1310
-rw-r--r--imap/src/osdep/nt/unixnt.c2297
-rw-r--r--imap/src/osdep/nt/unixnt.h161
-rw-r--r--imap/src/osdep/nt/write.c59
-rw-r--r--imap/src/osdep/nt/yunchan.c286
-rw-r--r--imap/src/osdep/nt/yunchan.h86
-rw-r--r--imap/src/osdep/os2/auths.cmd48
-rw-r--r--imap/src/osdep/os2/drivers.cmd45
-rw-r--r--imap/src/osdep/os2/dummy.h43
-rw-r--r--imap/src/osdep/os2/dummyos2.c714
-rw-r--r--imap/src/osdep/os2/env_os2.c318
-rw-r--r--imap/src/osdep/os2/env_os2.h62
-rw-r--r--imap/src/osdep/os2/fs_os2.c62
-rw-r--r--imap/src/osdep/os2/ftl_os2.c38
-rw-r--r--imap/src/osdep/os2/makefile.os2109
-rw-r--r--imap/src/osdep/os2/mbxnt.c1694
-rw-r--r--imap/src/osdep/os2/mtxnt.c1232
-rw-r--r--imap/src/osdep/os2/nl_os2.c61
-rw-r--r--imap/src/osdep/os2/os_os2.c100
-rw-r--r--imap/src/osdep/os2/os_os2.h38
-rw-r--r--imap/src/osdep/os2/pmatch.c89
-rw-r--r--imap/src/osdep/os2/pseudo.c36
-rw-r--r--imap/src/osdep/os2/pseudo.h30
-rw-r--r--imap/src/osdep/os2/setproto.cmd34
-rw-r--r--imap/src/osdep/os2/tcp_os2.c434
-rw-r--r--imap/src/osdep/os2/tcp_os2.h51
-rw-r--r--imap/src/osdep/os2/tenexnt.c1310
-rw-r--r--imap/src/osdep/os2/unixnt.c2297
-rw-r--r--imap/src/osdep/os2/unixnt.h161
-rw-r--r--imap/src/osdep/os2/write.c59
-rw-r--r--imap/src/osdep/tops-20/build.ctl63
-rw-r--r--imap/src/osdep/tops-20/dummy.h43
-rw-r--r--imap/src/osdep/tops-20/dummyt20.c294
-rw-r--r--imap/src/osdep/tops-20/env_t20.c226
-rw-r--r--imap/src/osdep/tops-20/env_t20.h73
-rw-r--r--imap/src/osdep/tops-20/fs_t20.c62
-rw-r--r--imap/src/osdep/tops-20/ftl_t20.c38
-rw-r--r--imap/src/osdep/tops-20/linkage.c37
-rw-r--r--imap/src/osdep/tops-20/linkage.h36
-rw-r--r--imap/src/osdep/tops-20/log_t20.c80
-rw-r--r--imap/src/osdep/tops-20/nl_t20.c61
-rw-r--r--imap/src/osdep/tops-20/os_t20.c106
-rw-r--r--imap/src/osdep/tops-20/os_t20.h52
-rw-r--r--imap/src/osdep/tops-20/pmatch.c89
-rw-r--r--imap/src/osdep/tops-20/shortsym.h608
-rw-r--r--imap/src/osdep/tops-20/tcp_t20.c365
-rw-r--r--imap/src/osdep/tops-20/tcp_t20.h65
-rw-r--r--imap/src/osdep/unix/Makefile1078
-rw-r--r--imap/src/osdep/unix/Makefile.gss39
-rw-r--r--imap/src/osdep/unix/ckp_1st.c53
-rw-r--r--imap/src/osdep/unix/ckp_2nd.c36
-rw-r--r--imap/src/osdep/unix/ckp_3rd.c32
-rw-r--r--imap/src/osdep/unix/ckp_a41.c52
-rw-r--r--imap/src/osdep/unix/ckp_afs.c71
-rw-r--r--imap/src/osdep/unix/ckp_bsi.c50
-rw-r--r--imap/src/osdep/unix/ckp_cyg.c64
-rw-r--r--imap/src/osdep/unix/ckp_dce.c83
-rw-r--r--imap/src/osdep/unix/ckp_gss.c90
-rw-r--r--imap/src/osdep/unix/ckp_nul.c40
-rw-r--r--imap/src/osdep/unix/ckp_os4.c78
-rw-r--r--imap/src/osdep/unix/ckp_pam.c136
-rw-r--r--imap/src/osdep/unix/ckp_pmb.c128
-rw-r--r--imap/src/osdep/unix/ckp_psx.c101
-rw-r--r--imap/src/osdep/unix/ckp_sce.c51
-rw-r--r--imap/src/osdep/unix/ckp_sec.c50
-rw-r--r--imap/src/osdep/unix/ckp_ssn.c56
-rw-r--r--imap/src/osdep/unix/ckp_std.c42
-rw-r--r--imap/src/osdep/unix/ckp_sv4.c90
-rw-r--r--imap/src/osdep/unix/ckp_svo.c89
-rw-r--r--imap/src/osdep/unix/ckp_ult.c40
-rw-r--r--imap/src/osdep/unix/crx_nfs.c79
-rw-r--r--imap/src/osdep/unix/crx_std.c45
-rwxr-xr-ximap/src/osdep/unix/drivers36
-rw-r--r--imap/src/osdep/unix/dummy.c809
-rw-r--r--imap/src/osdep/unix/dummy.h43
-rw-r--r--imap/src/osdep/unix/env_unix.c1847
-rw-r--r--imap/src/osdep/unix/env_unix.h95
-rw-r--r--imap/src/osdep/unix/fdstring.c99
-rw-r--r--imap/src/osdep/unix/fdstring.h39
-rw-r--r--imap/src/osdep/unix/flockcyg.c92
-rw-r--r--imap/src/osdep/unix/flockcyg.h50
-rw-r--r--imap/src/osdep/unix/flocklnx.c76
-rw-r--r--imap/src/osdep/unix/flocksim.c920
-rw-r--r--imap/src/osdep/unix/flocksim.h117
-rw-r--r--imap/src/osdep/unix/fs_unix.c71
-rw-r--r--imap/src/osdep/unix/fsync.c39
-rw-r--r--imap/src/osdep/unix/ftl_unix.c39
-rw-r--r--imap/src/osdep/unix/gethstid.c38
-rw-r--r--imap/src/osdep/unix/getspnam.c65
-rw-r--r--imap/src/osdep/unix/gr_wait.c46
-rw-r--r--imap/src/osdep/unix/gr_wait4.c39
-rw-r--r--imap/src/osdep/unix/gr_waitp.c39
-rw-r--r--imap/src/osdep/unix/ip4_unix.c184
-rw-r--r--imap/src/osdep/unix/ip6_unix.c288
-rw-r--r--imap/src/osdep/unix/ipo_unix.c181
-rw-r--r--imap/src/osdep/unix/kerb_mit.c111
-rw-r--r--imap/src/osdep/unix/log_bsi.c55
-rw-r--r--imap/src/osdep/unix/log_cyg.c46
-rw-r--r--imap/src/osdep/unix/log_old.c39
-rw-r--r--imap/src/osdep/unix/log_os4.c58
-rw-r--r--imap/src/osdep/unix/log_sec.c44
-rw-r--r--imap/src/osdep/unix/log_std.c44
-rw-r--r--imap/src/osdep/unix/log_sv4.c43
-rw-r--r--imap/src/osdep/unix/mbx.c1855
-rw-r--r--imap/src/osdep/unix/mh.c1283
-rw-r--r--imap/src/osdep/unix/mix.c2834
-rwxr-xr-ximap/src/osdep/unix/mkauths40
-rw-r--r--imap/src/osdep/unix/mmdf.c2549
-rw-r--r--imap/src/osdep/unix/mtx.c1371
-rw-r--r--imap/src/osdep/unix/mx.c1287
-rw-r--r--imap/src/osdep/unix/news.c738
-rw-r--r--imap/src/osdep/unix/nl_unix.c92
-rw-r--r--imap/src/osdep/unix/opendir.c79
-rw-r--r--imap/src/osdep/unix/os_a32.c60
-rw-r--r--imap/src/osdep/unix/os_a32.h50
-rw-r--r--imap/src/osdep/unix/os_a41.c61
-rw-r--r--imap/src/osdep/unix/os_a41.h50
-rw-r--r--imap/src/osdep/unix/os_a52.c63
-rw-r--r--imap/src/osdep/unix/os_a52.h53
-rw-r--r--imap/src/osdep/unix/os_aix.c64
-rw-r--r--imap/src/osdep/unix/os_aix.h47
-rw-r--r--imap/src/osdep/unix/os_aos.c63
-rw-r--r--imap/src/osdep/unix/os_aos.h50
-rw-r--r--imap/src/osdep/unix/os_art.c86
-rw-r--r--imap/src/osdep/unix/os_art.h81
-rw-r--r--imap/src/osdep/unix/os_asv.c70
-rw-r--r--imap/src/osdep/unix/os_asv.h76
-rw-r--r--imap/src/osdep/unix/os_aux.c62
-rw-r--r--imap/src/osdep/unix/os_aux.h51
-rw-r--r--imap/src/osdep/unix/os_bsd.c63
-rw-r--r--imap/src/osdep/unix/os_bsd.h51
-rw-r--r--imap/src/osdep/unix/os_bsf.c54
-rw-r--r--imap/src/osdep/unix/os_bsf.h46
-rw-r--r--imap/src/osdep/unix/os_bsi.c54
-rw-r--r--imap/src/osdep/unix/os_bsi.h43
-rw-r--r--imap/src/osdep/unix/os_cvx.c57
-rw-r--r--imap/src/osdep/unix/os_cvx.h45
-rw-r--r--imap/src/osdep/unix/os_cyg.c71
-rw-r--r--imap/src/osdep/unix/os_cyg.h71
-rw-r--r--imap/src/osdep/unix/os_d-g.c54
-rw-r--r--imap/src/osdep/unix/os_d-g.h56
-rw-r--r--imap/src/osdep/unix/os_do4.c58
-rw-r--r--imap/src/osdep/unix/os_do4.h47
-rw-r--r--imap/src/osdep/unix/os_drs.c57
-rw-r--r--imap/src/osdep/unix/os_drs.h59
-rw-r--r--imap/src/osdep/unix/os_dyn.c64
-rw-r--r--imap/src/osdep/unix/os_dyn.h61
-rw-r--r--imap/src/osdep/unix/os_hpp.c77
-rw-r--r--imap/src/osdep/unix/os_hpp.h63
-rw-r--r--imap/src/osdep/unix/os_isc.c68
-rw-r--r--imap/src/osdep/unix/os_isc.h70
-rw-r--r--imap/src/osdep/unix/os_lnx.c54
-rw-r--r--imap/src/osdep/unix/os_lnx.h67
-rw-r--r--imap/src/osdep/unix/os_lyn.c54
-rw-r--r--imap/src/osdep/unix/os_lyn.h44
-rw-r--r--imap/src/osdep/unix/os_mct.c52
-rw-r--r--imap/src/osdep/unix/os_mct.h44
-rw-r--r--imap/src/osdep/unix/os_mnt.c53
-rw-r--r--imap/src/osdep/unix/os_mnt.h51
-rw-r--r--imap/src/osdep/unix/os_nto.c76
-rw-r--r--imap/src/osdep/unix/os_nto.h75
-rw-r--r--imap/src/osdep/unix/os_nxt.c54
-rw-r--r--imap/src/osdep/unix/os_nxt.h50
-rw-r--r--imap/src/osdep/unix/os_os4.c57
-rw-r--r--imap/src/osdep/unix/os_os4.h51
-rw-r--r--imap/src/osdep/unix/os_osf.c55
-rw-r--r--imap/src/osdep/unix/os_osf.h51
-rw-r--r--imap/src/osdep/unix/os_osx.c54
-rw-r--r--imap/src/osdep/unix/os_osx.h56
-rw-r--r--imap/src/osdep/unix/os_ptx.c115
-rw-r--r--imap/src/osdep/unix/os_ptx.h72
-rw-r--r--imap/src/osdep/unix/os_pyr.c61
-rw-r--r--imap/src/osdep/unix/os_pyr.h58
-rw-r--r--imap/src/osdep/unix/os_qnx.c77
-rw-r--r--imap/src/osdep/unix/os_qnx.h62
-rw-r--r--imap/src/osdep/unix/os_s40.c64
-rw-r--r--imap/src/osdep/unix/os_s40.h33
-rw-r--r--imap/src/osdep/unix/os_sc5.c63
-rw-r--r--imap/src/osdep/unix/os_sc5.h76
-rw-r--r--imap/src/osdep/unix/os_sco.c66
-rw-r--r--imap/src/osdep/unix/os_sco.h79
-rw-r--r--imap/src/osdep/unix/os_sgi.c57
-rw-r--r--imap/src/osdep/unix/os_sgi.h59
-rw-r--r--imap/src/osdep/unix/os_shp.c79
-rw-r--r--imap/src/osdep/unix/os_shp.h63
-rw-r--r--imap/src/osdep/unix/os_slx.c56
-rw-r--r--imap/src/osdep/unix/os_slx.h67
-rw-r--r--imap/src/osdep/unix/os_sol.c71
-rw-r--r--imap/src/osdep/unix/os_soln.h87
-rw-r--r--imap/src/osdep/unix/os_solo.h84
-rw-r--r--imap/src/osdep/unix/os_sos.c57
-rw-r--r--imap/src/osdep/unix/os_sos.h51
-rw-r--r--imap/src/osdep/unix/os_sua.c54
-rw-r--r--imap/src/osdep/unix/os_sua.h50
-rw-r--r--imap/src/osdep/unix/os_sun.c64
-rw-r--r--imap/src/osdep/unix/os_sun.h51
-rw-r--r--imap/src/osdep/unix/os_sv2.c128
-rw-r--r--imap/src/osdep/unix/os_sv2.h120
-rw-r--r--imap/src/osdep/unix/os_sv4.c68
-rw-r--r--imap/src/osdep/unix/os_sv4.h78
-rw-r--r--imap/src/osdep/unix/os_ult.c52
-rw-r--r--imap/src/osdep/unix/os_ult.h42
-rw-r--r--imap/src/osdep/unix/os_vu2.c82
-rw-r--r--imap/src/osdep/unix/os_vu2.h79
-rw-r--r--imap/src/osdep/unix/phile.c553
-rw-r--r--imap/src/osdep/unix/pmatch.c89
-rw-r--r--imap/src/osdep/unix/pseudo.c36
-rw-r--r--imap/src/osdep/unix/pseudo.h30
-rw-r--r--imap/src/osdep/unix/rename.c44
-rw-r--r--imap/src/osdep/unix/scandir.c81
-rw-r--r--imap/src/osdep/unix/setpgrp.c39
-rw-r--r--imap/src/osdep/unix/sig_bsd.c40
-rw-r--r--imap/src/osdep/unix/sig_psx.c51
-rw-r--r--imap/src/osdep/unix/sig_sv4.c40
-rw-r--r--imap/src/osdep/unix/ssl_none.c141
-rw-r--r--imap/src/osdep/unix/ssl_unix.c821
-rw-r--r--imap/src/osdep/unix/sslstdio.c169
-rw-r--r--imap/src/osdep/unix/strerror.c37
-rw-r--r--imap/src/osdep/unix/tcp_unix.c1043
-rw-r--r--imap/src/osdep/unix/tcp_unix.h48
-rw-r--r--imap/src/osdep/unix/tenex.c1470
-rw-r--r--imap/src/osdep/unix/truncate.c43
-rw-r--r--imap/src/osdep/unix/tz_bsd.c38
-rw-r--r--imap/src/osdep/unix/tz_nul.c36
-rw-r--r--imap/src/osdep/unix/tz_sv4.c39
-rw-r--r--imap/src/osdep/unix/unix.c2708
-rw-r--r--imap/src/osdep/unix/unix.h161
-rw-r--r--imap/src/osdep/unix/utime.c45
-rw-r--r--imap/src/osdep/unix/write.c59
-rw-r--r--imap/src/osdep/vms/build.com99
-rw-r--r--imap/src/osdep/vms/clean.com26
-rw-r--r--imap/src/osdep/vms/dummy.h43
-rw-r--r--imap/src/osdep/vms/dummyvms.c295
-rw-r--r--imap/src/osdep/vms/env_vms.c174
-rw-r--r--imap/src/osdep/vms/env_vms.h60
-rw-r--r--imap/src/osdep/vms/fs_vms.c62
-rw-r--r--imap/src/osdep/vms/ftl_vms.c38
-rw-r--r--imap/src/osdep/vms/link.opt1
-rw-r--r--imap/src/osdep/vms/link_mnt.opt1
-rw-r--r--imap/src/osdep/vms/link_nlb.opt1
-rw-r--r--imap/src/osdep/vms/linkage.c37
-rw-r--r--imap/src/osdep/vms/linkage.h36
-rw-r--r--imap/src/osdep/vms/nl_vms.c92
-rw-r--r--imap/src/osdep/vms/os_vms.c76
-rw-r--r--imap/src/osdep/vms/os_vms.h52
-rw-r--r--imap/src/osdep/vms/pmatch.c89
-rw-r--r--imap/src/osdep/vms/tcp_vms.h52
-rw-r--r--imap/src/osdep/vms/tcp_vmsl.c378
-rw-r--r--imap/src/osdep/vms/tcp_vmsm.c479
-rw-r--r--imap/src/osdep/vms/tcp_vmsn.c222
-rw-r--r--imap/src/osdep/wce/drivers.bat33
-rw-r--r--imap/src/osdep/wce/drivraux.bat30
-rw-r--r--imap/src/osdep/wce/dummy.h43
-rw-r--r--imap/src/osdep/wce/dummywce.c301
-rw-r--r--imap/src/osdep/wce/env_wce.c301
-rw-r--r--imap/src/osdep/wce/env_wce.h70
-rw-r--r--imap/src/osdep/wce/fs_wce.c62
-rw-r--r--imap/src/osdep/wce/ftl_wce.c38
-rw-r--r--imap/src/osdep/wce/makefile.wce96
-rw-r--r--imap/src/osdep/wce/mkautaux.bat31
-rw-r--r--imap/src/osdep/wce/mkauths.bat33
-rw-r--r--imap/src/osdep/wce/nl_wce.c61
-rw-r--r--imap/src/osdep/wce/os_wce.c45
-rw-r--r--imap/src/osdep/wce/os_wce.h53
-rw-r--r--imap/src/osdep/wce/pmatch.c89
-rw-r--r--imap/src/osdep/wce/setproto.bat29
-rw-r--r--imap/src/osdep/wce/tcp_wce.c818
-rw-r--r--imap/src/osdep/wce/tcp_wce.h46
-rw-r--r--imap/src/tmail/Makefile53
-rw-r--r--imap/src/tmail/tmail.1205
-rw-r--r--imap/src/tmail/tmail.c800
-rw-r--r--imap/src/tmail/tquota.c45
-rw-r--r--imap/src/tmail/tquota.h32
499 files changed, 162346 insertions, 0 deletions
diff --git a/imap/src/ansilib/memmove.c b/imap/src/ansilib/memmove.c
new file mode 100644
index 00000000..85453bd0
--- /dev/null
+++ b/imap/src/ansilib/memmove.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Memory move
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy memory block
+ * Accepts: destination pointer
+ * source pointer
+ * length
+ * Returns: destination pointer
+ */
+
+void *memmove (void *s,void *ct,size_t n)
+{
+ bcopy (ct,s,n); /* they should have this one */
+ return s;
+}
diff --git a/imap/src/ansilib/memmove2.c b/imap/src/ansilib/memmove2.c
new file mode 100644
index 00000000..52532e21
--- /dev/null
+++ b/imap/src/ansilib/memmove2.c
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Memory move when no bcopy()
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy memory block
+ * Accepts: destination pointer
+ * source pointer
+ * length
+ * Returns: destination pointer
+ */
+
+void *memmove (void *s,void *ct,size_t n)
+{
+ char *dp,*sp;
+ int i;
+ unsigned long dest = (unsigned long) s;
+ unsigned long src = (unsigned long) ct;
+ if (((dest < src) && ((dest + n) < src)) ||
+ ((dest > src) && ((src + n) < dest))) return (void *) memcpy (s,ct,n);
+ dp = (char *) s;
+ sp = (char *) ct;
+ if (dest < src) for (i = 0; i < n; ++i) dp[i] = sp[i];
+ else if (dest > src) for (i = n - 1; i >= 0; --i) dp[i] = sp[i];
+ return s;
+}
diff --git a/imap/src/ansilib/memset.c b/imap/src/ansilib/memset.c
new file mode 100644
index 00000000..fce66806
--- /dev/null
+++ b/imap/src/ansilib/memset.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Set memory
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Set a block of memory
+ * Accepts: destination pointer
+ * value to set
+ * length
+ * Returns: destination pointer
+ */
+
+void *memset (void *s,int c,size_t n)
+{
+ if (c) while (n) s[--n] = c; /* this way if non-zero */
+ else bzero (s,n); /* they should have this one */
+ return s;
+}
diff --git a/imap/src/ansilib/strpbrk.c b/imap/src/ansilib/strpbrk.c
new file mode 100644
index 00000000..d63f7627
--- /dev/null
+++ b/imap/src/ansilib/strpbrk.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String search for break character
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Return pointer to first occurance in string of any delimiter
+ * Accepts: source pointer
+ * vector of delimiters pointer
+ * Returns: pointer to delimiter or NIL if not found
+ */
+
+char *strpbrk (char *cs,char *ct)
+{
+ char *s;
+ /* search for delimiter until end of string */
+ for (; *cs; cs++) for (s = ct; *s; s++) if (*s == *cs) return cs;
+ return NIL; /* not found */
+}
diff --git a/imap/src/ansilib/strstr.c b/imap/src/ansilib/strstr.c
new file mode 100644
index 00000000..5c02569a
--- /dev/null
+++ b/imap/src/ansilib/strstr.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Substring search
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Return pointer to first occurance in string of a substring
+ * Accepts: source pointer
+ * substring pointer
+ * Returns: pointer to substring in source or NIL if not found
+ */
+
+char *strstr (char *cs,char *ct)
+{
+ char *s;
+ char *t;
+ while (cs = strchr (cs,*ct)) {/* for each occurance of the first character */
+ /* see if remainder of string matches */
+ for (s = cs + 1, t = ct + 1; *t && *s == *t; s++, t++);
+ if (!*t) return cs; /* if ran out of substring then have match */
+ cs++; /* try from next character */
+ }
+ return NIL; /* not found */
+}
diff --git a/imap/src/ansilib/strtok.c b/imap/src/ansilib/strtok.c
new file mode 100644
index 00000000..973ba4e7
--- /dev/null
+++ b/imap/src/ansilib/strtok.c
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String return successive tokens
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+/* Find token in string
+ * Accepts: source pointer or NIL to use previous source
+ * vector of token delimiters pointer
+ * Returns: pointer to next token
+ */
+
+static char *state = NIL; /* string to locate tokens */
+
+char *strtok (char *s,char *ct)
+{
+ return strtok_r (s,ct,&state);/* jacket into reentrant routine */
+}
+
+
+/* Find token in string (reentrant)
+ * Accepts: source pointer or NIL to use previous source
+ * vector of token delimiters pointer
+ * returned state pointer
+ * Returns: pointer to next token
+ */
+
+char *strtok_r (char *s,char *ct,char **r)
+{
+ char *t,*ts;
+ if (!s) s = *r; /* use previous token if none specified */
+ *r = NIL; /* default to no returned state */
+ if (!(s && *s)) return NIL; /* no tokens left */
+ /* find any leading delimiters */
+ do for (t = ct, ts = NIL; *t; t++) if (*t == *s) {
+ if (*(ts = ++s)) break; /* yes, restart search if more in string */
+ return NIL; /* else no more tokens */
+ } while (ts); /* continue until no more leading delimiters */
+ /* can we find a new delimiter? */
+ for (ts = s; *ts; ts++) for (t = ct; *t; t++) if (*t == *ts) {
+ *ts++ = '\0'; /* yes, tie off token at that point */
+ *r = ts; /* save subsequent tokens for future call */
+ return s; /* return our token */
+ }
+ return s; /* return final token */
+}
diff --git a/imap/src/ansilib/strtoul.c b/imap/src/ansilib/strtoul.c
new file mode 100644
index 00000000..0646b637
--- /dev/null
+++ b/imap/src/ansilib/strtoul.c
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String to unsigned long
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 14 February 1995
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ * Turn a string unsigned long into the real thing
+ * Accepts: source string
+ * pointer to place to return end pointer
+ * base
+ * Returns: parsed unsigned long integer, end pointer is updated
+ */
+
+unsigned long strtoul (char *t,char **endp,int base)
+{
+ unsigned long value = 0; /* the accumulated value */
+ int negative = 0; /* this a negative number? */
+ unsigned char c,*s = t;
+ if (base && (base < 2 || base > 36)) {
+ errno = EINVAL; /* insist upon valid base */
+ return value;
+ }
+ while (isspace (*s)) s++; /* skip leading whitespace */
+ switch (*s) { /* check for leading sign char */
+ case '-':
+ negative = 1; /* yes, negative #. fall into '+' */
+ case '+':
+ s++; /* skip the sign character */
+ }
+ if (!base) { /* base not specified? */
+ if (*s != '0') base = 10; /* must be decimal if doesn't start with 0 */
+ /* starts with 0x? */
+ else if (tolower (*++s) == 'x') {
+ base = 16; /* yes, is hex */
+ s++; /* skip the x */
+ }
+ else base = 8; /* ...or octal */
+ }
+ do { /* convert to numeric form if digit */
+ if (isdigit (*s)) c = *s - '0';
+ /* alphabetic conversion */
+ else if (isalpha (*s)) c = *s - (isupper (*s) ? 'A' : 'a') + 10;
+ else break; /* else no way it's valid */
+ if (c >= base) break; /* digit out of range for base? */
+ value = value * base + c; /* accumulate the digit */
+ } while (*++s); /* loop until non-numeric character */
+ if (tolower (*s) == 'l') s++; /* ignore 'l' or 'L' marker */
+ if (endp) *endp = s; /* save users endp to after number */
+ /* negate number if needed */
+ return negative ? -value : value;
+}
diff --git a/imap/src/c-client/auth_ext.c b/imap/src/c-client/auth_ext.c
new file mode 100644
index 00000000..61dfc1b8
--- /dev/null
+++ b/imap/src/c-client/auth_ext.c
@@ -0,0 +1,96 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: EXTERNAL authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 April 2005
+ * Last Edited: 30 August 2006
+ */
+
+long auth_external_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_external_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_ext = { /* secure, has full auth, hidden */
+ AU_SECURE | AU_AUTHUSER | AU_HIDE,
+ "EXTERNAL", /* authenticator name */
+ NIL, /* always valid */
+ auth_external_client, /* client method */
+ auth_external_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_external_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ *trial = 65535; /* never retry */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ /* send authorization id (empty string OK) */
+ if ((*responder) (stream,strcpy (user,mb->user),strlen (mb->user))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else ret = LONGT; /* check the authentication */
+ }
+ }
+ return ret;
+}
+
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_external_server (authresponse_t responder,int argc,char *argv[])
+{
+ unsigned long len;
+ char *authid;
+ char *authenid = (char *) mail_parameters (NIL,GET_EXTERNALAUTHID,NIL);
+ char *ret = NIL;
+ /* get authorization identity */
+ if (authenid && (authid = (*responder) ("",0,&len))) {
+ /* note: responders null-terminate */
+ if (*authid ? authserver_login (authid,authenid,argc,argv) :
+ authserver_login (authenid,NIL,argc,argv)) ret = myusername ();
+ fs_give ((void **) &authid);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/auth_gss.c b/imap/src/c-client/auth_gss.c
new file mode 100644
index 00000000..26bf9e50
--- /dev/null
+++ b/imap/src/c-client/auth_gss.c
@@ -0,0 +1,423 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GSSAPI authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 January 1998
+ * Last Edited: 30 August 2006
+ */
+
+
+long auth_gssapi_valid (void);
+long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *stream,char *user,kinit_t ki);
+char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]);
+
+
+AUTHENTICATOR auth_gss = {
+ AU_SECURE | AU_AUTHUSER, /* secure authenticator */
+ "GSSAPI", /* authenticator name */
+ auth_gssapi_valid, /* check if valid */
+ auth_gssapi_client, /* client method */
+ auth_gssapi_server, /* server method */
+ NIL /* next authenticator */
+};
+
+#define AUTH_GSSAPI_P_NONE 1
+#define AUTH_GSSAPI_P_INTEGRITY 2
+#define AUTH_GSSAPI_P_PRIVACY 4
+
+#define AUTH_GSSAPI_C_MAXSIZE 8192
+
+#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
+
+/* Check if GSSAPI valid on this system
+ * Returns: T if valid, NIL otherwise
+ */
+
+long auth_gssapi_valid (void)
+{
+ char tmp[MAILTMPLEN];
+ OM_uint32 smn;
+ gss_buffer_desc buf;
+ gss_name_t name;
+ /* make service name */
+ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ mylocalhost ());
+ buf.length = strlen (buf.value = tmp);
+ /* see if can build a name */
+ if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) !=
+ GSS_S_COMPLETE) return NIL;
+ /* remove server method if no keytab */
+ if (!kerberos_server_valid ()) auth_gss.server = NIL;
+ gss_release_name (&smn,&name);/* finished with name */
+ return LONGT;
+}
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ gss_buffer_desc chal;
+ kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL);
+ long ret = NIL;
+ *trial = 65535; /* never retry */
+ /* get initial (empty) challenge */
+ if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) {
+ if (chal.length) { /* abort if challenge non-empty */
+ mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) {
+ mm_log ("Can't use Kerberos: invalid /authuser",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,ki);
+ }
+ return ret;
+}
+
+/* Client authenticator worker function
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * returned user name
+ * kinit function pointer if should retry with kinit
+ * Returns: T if success, NIL otherwise
+ */
+
+long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *stream,char *user,kinit_t ki)
+{
+ char tmp[MAILTMPLEN];
+ OM_uint32 smj,smn,dsmj,dsmn;
+ OM_uint32 mctx = 0;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc resp,buf;
+ long i;
+ int conf;
+ gss_qop_t qop;
+ gss_name_t crname = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ long ret = NIL;
+ sprintf (tmp,"%s@%s",service,mb->host);
+ buf.length = strlen (buf.value = tmp);
+ /* get service name */
+ if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) !=
+ GSS_S_COMPLETE) {
+ mm_log ("Can't import Kerberos service name",WARN);
+ (*responder) (stream,NIL,0);
+ }
+ else {
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* negotiate with KDC */
+ smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL,
+ GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG |
+ GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+
+ /* while continuation needed */
+ while (smj == GSS_S_CONTINUE_NEEDED) {
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* send response, get next challenge */
+ i = (*responder) (stream,resp.value,resp.length) &&
+ (chal.value = (*challenger) (stream,(unsigned long *) &chal.length));
+ gss_release_buffer (&smn,&resp);
+ if (i) { /* negotiate continuation with KDC */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ switch (smj = /* make sure continuation going OK */
+ gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,
+ crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG |
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,
+ GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL,
+ &resp,NIL,NIL)) {
+ case GSS_S_CONTINUE_NEEDED:
+ case GSS_S_COMPLETE:
+ break;
+ default: /* error, don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ }
+ else { /* error in continuation */
+ mm_log ("Error in negotiating Kerberos continuation",WARN);
+ (*responder) (stream,NIL,0);
+ /* don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ break;
+ }
+ }
+
+ switch (smj) { /* done - deal with final condition */
+ case GSS_S_COMPLETE:
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* get prot mechanisms and max size */
+ if ((*responder) (stream,resp.value ? resp.value : "",resp.length) &&
+ (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
+ (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
+ (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
+ /* make copy of flags and length */
+ memcpy (tmp,resp.value,4);
+ gss_release_buffer (&smn,&resp);
+ /* no session protection */
+ tmp[0] = AUTH_GSSAPI_P_NONE;
+ /* install user name */
+ strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
+ buf.value = tmp; buf.length = strlen (user) + 4;
+ /* successful negotiation */
+ switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) {
+ case GSS_S_COMPLETE:
+ if ((*responder) (stream,resp.value,resp.length)) ret = T;
+ gss_release_buffer (&smn,&resp);
+ break;
+ default:
+ do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ (*responder) (stream,NIL,0);
+ }
+ }
+ /* flush final challenge */
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ break;
+
+ case GSS_S_CREDENTIALS_EXPIRED:
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* retry if application kinits */
+ if (ki && (*ki) (mb->host,"Kerberos credentials expired"))
+ ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,NIL);
+ else { /* application can't kinit */
+ sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
+ mb->host);
+ mm_log (tmp,WARN);
+ (*responder) (stream,NIL,0);
+ }
+ break;
+ case GSS_S_FAILURE:
+ if (chal.value) fs_give ((void **) &chal.value);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE: /* end of message, can kinit? */
+ if (ki && kerberos_try_kinit (smn) &&
+ (*ki) (mb->host,(char *) resp.value)) {
+ gss_release_buffer (&dsmn,&resp);
+ ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,NIL);
+ break; /* done */
+ }
+ else (*responder) (stream,NIL,0);
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,kerberos_try_kinit (smn) ?
+ "Kerberos error: %.80s (try running kinit) for %.80s" :
+ "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ } while (dsmj == GSS_S_CONTINUE_NEEDED);
+ break;
+
+ default: /* miscellaneous errors */
+ if (chal.value) fs_give ((void **) &chal.value);
+ do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ (*responder) (stream,NIL,0);
+ break;
+ }
+ /* finished with credentials name */
+ if (crname) gss_release_name (&smn,&crname);
+ }
+ return ret; /* return status */
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
+ int conf;
+ OM_uint32 smj,smn,dsmj,dsmn,flags;
+ OM_uint32 mctx = 0;
+ gss_name_t crname,name;
+ gss_OID mech;
+ gss_buffer_desc chal,resp,buf;
+ gss_cred_id_t crd;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_qop_t qop = GSS_C_QOP_DEFAULT;
+ /* make service name */
+ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ tcp_serverhost ());
+ buf.length = strlen (buf.value = tmp);
+ /* acquire credentials */
+ if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) ==
+ GSS_S_COMPLETE) {
+ if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL))
+ == GSS_S_COMPLETE) {
+ if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
+ do { /* negotiate authentication */
+ smj = gss_accept_sec_context (&smn,&ctx,crd,&resp,
+ GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
+ &chal,&flags,NIL,NIL);
+ /* don't need response any more */
+ fs_give ((void **) &resp.value);
+ switch (smj) { /* how did it go? */
+ case GSS_S_COMPLETE: /* successful */
+ case GSS_S_CONTINUE_NEEDED:
+ if (chal.value) { /* send challenge, get next response */
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ }
+ break;
+ }
+ }
+ while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED));
+
+ /* successful exchange? */
+ if ((smj == GSS_S_COMPLETE) &&
+ (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) {
+ /* send security and size */
+ memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
+ tmp[0] = AUTH_GSSAPI_P_NONE;
+ if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
+ /* client request valid */
+ if (chal.value && (chal.length > 4) &&
+ (chal.length < (MAILTMPLEN - 1)) &&
+ memcpy (tmp,chal.value,chal.length) &&
+ (tmp[0] & AUTH_GSSAPI_P_NONE)) {
+ /* tie off authorization ID */
+ tmp[chal.length] = '\0';
+ ret = kerberos_login (tmp+4,buf.value,argc,argv);
+ }
+ /* done with user name */
+ gss_release_buffer (&smn,&chal);
+ }
+ /* finished with response */
+ fs_give ((void **) &resp.value);
+ }
+ /* don't need name buffer any more */
+ gss_release_buffer (&smn,&buf);
+ }
+ /* don't need client name any more */
+ gss_release_name (&smn,&name);
+ /* don't need context any more */
+ if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL);
+ }
+ /* finished with credentials */
+ gss_release_cred (&smn,&crd);
+ }
+
+ else { /* can't acquire credentials! */
+ if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE)
+ SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
+ if (smj != GSS_S_FAILURE) do
+ switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ }
+ /* finished with credentials name */
+ gss_release_name (&smn,&crname);
+ }
+ return ret; /* return status */
+}
diff --git a/imap/src/c-client/auth_log.c b/imap/src/c-client/auth_log.c
new file mode 100644
index 00000000..1e1b1b5c
--- /dev/null
+++ b/imap/src/c-client/auth_log.c
@@ -0,0 +1,117 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Login authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 December 1995
+ * Last Edited: 30 August 2006
+ */
+
+long auth_login_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_login_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_log = {
+ AU_HIDE, /* hidden */
+ "LOGIN", /* authenticator name */
+ NIL, /* always valid */
+ auth_login_client, /* client method */
+ auth_login_server, /* server method */
+ NIL /* next authenticator */
+};
+
+#define PWD_USER "User Name"
+#define PWD_PWD "Password"
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_login_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* get user name prompt */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ pwd[0] = NIL; /* prompt user */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* user requested abort */
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ /* send user name */
+ else if ((*responder) (stream,user,strlen (user)) &&
+ (challenge = (*challenger) (stream,&clen))) {
+ fs_give ((void **) &challenge);
+ /* send password */
+ if ((*responder) (stream,pwd,strlen (pwd))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_login_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *user,*pass,*authuser;
+ if (user = (*responder) (PWD_USER,sizeof (PWD_USER),NIL)) {
+ if (pass = (*responder) (PWD_PWD,sizeof (PWD_PWD),NIL)) {
+ /* delimit user from possible admin */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ if (server_login (user,pass,authuser,argc,argv)) ret = myusername ();
+ fs_give ((void **) &pass);
+ }
+ fs_give ((void **) &user);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/auth_md5.c b/imap/src/c-client/auth_md5.c
new file mode 100644
index 00000000..29ab947d
--- /dev/null
+++ b/imap/src/c-client/auth_md5.c
@@ -0,0 +1,495 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: CRAM-MD5 authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 21 October 1998
+ * Last Edited: 30 January 2007
+ */
+
+/* MD5 context */
+
+#define MD5BLKLEN 64 /* MD5 block length */
+#define MD5DIGLEN 16 /* MD5 digest length */
+
+typedef struct {
+ unsigned long chigh; /* high 32bits of byte count */
+ unsigned long clow; /* low 32bits of byte count */
+ unsigned long state[4]; /* state (ABCD) */
+ unsigned char buf[MD5BLKLEN]; /* input buffer */
+ unsigned char *ptr; /* buffer position */
+} MD5CONTEXT;
+
+
+/* Prototypes */
+
+long auth_md5_valid (void);
+long auth_md5_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_md5_server (authresponse_t responder,int argc,char *argv[]);
+char *auth_md5_pwd (char *user);
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
+char *hmac_md5 (char *text,unsigned long tl,char *key,unsigned long kl);
+void md5_init (MD5CONTEXT *ctx);
+void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len);
+void md5_final (unsigned char *digest,MD5CONTEXT *ctx);
+static void md5_transform (unsigned long *state,unsigned char *block);
+static void md5_encode (unsigned char *dst,unsigned long *src,int len);
+static void md5_decode (unsigned long *dst,unsigned char *src,int len);
+
+
+/* Authenticator linkage */
+
+AUTHENTICATOR auth_md5 = {
+ AU_SECURE, /* secure authenticator */
+ "CRAM-MD5", /* authenticator name */
+ auth_md5_valid, /* check if valid */
+ auth_md5_client, /* client method */
+ auth_md5_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Check if CRAM-MD5 valid on this system
+ * Returns: T, always
+ */
+
+long auth_md5_valid (void)
+{
+ struct stat sbuf;
+ /* server forbids MD5 if no MD5 enable file */
+ if (stat (MD5ENABLE,&sbuf)) auth_md5.server = NIL;
+ return T; /* MD5 is otherwise valid */
+}
+
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_md5_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* get challenge */
+ if (challenge = (*challenger) (stream,&clen)) {
+ pwd[0] = NIL; /* prompt user */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* user requested abort */
+ fs_give ((void **) &challenge);
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else { /* got password, build response */
+ sprintf (pwd,"%.65s %.33s",user,hmac_md5 (challenge,clen,
+ pwd,strlen (pwd)));
+ fs_give ((void **) &challenge);
+ /* send credentials, allow retry if OK */
+ if ((*responder) (stream,pwd,strlen (pwd))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password in case not overwritten */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ *
+ * This is much hairier than it needs to be due to the necessary of zapping
+ * the password data.
+ */
+
+static int md5try = MAXLOGINTRIALS;
+
+char *auth_md5_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *p,*u,*user,*authuser,*hash,chal[MAILTMPLEN];
+ unsigned long cl,pl;
+ /* generate challenge */
+ sprintf (chal,"<%lu.%lu@%s>",(unsigned long) getpid (),
+ (unsigned long) time (0),mylocalhost ());
+ /* send challenge, get user and hash */
+ if (user = (*responder) (chal,cl = strlen (chal),NIL)) {
+ /* got user, locate hash */
+ if (hash = strrchr (user,' ')) {
+ *hash++ = '\0'; /* tie off user */
+ /* see if authentication user */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ /* get password */
+ if (p = auth_md5_pwd ((authuser && *authuser) ? authuser : user)) {
+ pl = strlen (p);
+ u = (md5try && !strcmp (hash,hmac_md5 (chal,cl,p,pl))) ? user : NIL;
+ memset (p,0,pl); /* erase sensitive information */
+ fs_give ((void **) &p); /* flush erased password */
+ /* now log in for real */
+ if (u && authserver_login (u,authuser,argc,argv)) ret = myusername ();
+ else if (md5try) --md5try;
+ }
+ }
+ fs_give ((void **) &user);
+ }
+ if (!ret) sleep (3); /* slow down possible cracker */
+ return ret;
+}
+
+/* Return MD5 password for user
+ * Accepts: user name
+ * Returns: plaintext password if success, else NIL
+ *
+ * This is much hairier than it needs to be due to the necessary of zapping
+ * the password data. That's why we don't use stdio here.
+ */
+
+char *auth_md5_pwd (char *user)
+{
+ struct stat sbuf;
+ int fd = open (MD5ENABLE,O_RDONLY,NIL);
+ unsigned char *s,*t,*buf,*lusr,*lret;
+ char *r;
+ char *ret = NIL;
+ if (fd >= 0) { /* found the file? */
+ fstat (fd,&sbuf); /* yes, slurp it into memory */
+ read (fd,buf = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ /* see if any uppercase characters in user */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ /* yes, make lowercase copy */
+ lusr = *s ? lcase (cpystr (user)) : NIL;
+ for (s = strtok_r ((char *) buf,"\015\012",&r),lret = NIL; s;
+ s = ret ? NIL : strtok_r (NIL,"\015\012",&r))
+ /* must be valid entry line */
+ if (*s && (*s != '#') && (t = strchr (s,'\t')) && t[1]) {
+ *t++ = '\0'; /* found tab, tie off user, point to pwd */
+ if (!strcmp (s,user)) ret = cpystr (t);
+ else if (lusr && !lret) if (!strcmp (s,lusr)) lret = t;
+ }
+ /* accept case-independent name */
+ if (!ret && lret) ret = cpystr (lret);
+ /* don't need lowercase copy any more */
+ if (lusr) fs_give ((void **) &lusr);
+ /* erase sensitive information from buffer */
+ memset (buf,0,sbuf.st_size + 1);
+ fs_give ((void **) &buf); /* flush the buffer */
+ close (fd); /* don't need file any longer */
+ }
+ return ret; /* return password */
+}
+
+/* APOP server login
+ * Accepts: challenge
+ * desired user name
+ * purported MD5
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[])
+{
+ int i,j;
+ char *ret = NIL;
+ char *s,*authuser,tmp[MAILTMPLEN];
+ unsigned char digest[MD5DIGLEN];
+ MD5CONTEXT ctx;
+ char *hex = "0123456789abcdef";
+ /* see if authentication user */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ /* get password */
+ if (s = auth_md5_pwd ((authuser && *authuser) ? authuser : user)) {
+ md5_init (&ctx); /* initialize MD5 context */
+ /* build string to get MD5 digest */
+ sprintf (tmp,"%.128s%.128s",chal,s);
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s); /* flush erased password */
+ md5_update (&ctx,(unsigned char *) tmp,strlen (tmp));
+ memset (tmp,0,MAILTMPLEN); /* erase sensitive information */
+ md5_final (digest,&ctx);
+ /* convert to printable hex */
+ for (i = 0, s = tmp; i < MD5DIGLEN; i++) {
+ *s++ = hex[(j = digest[i]) >> 4];
+ *s++ = hex[j & 0xf];
+ }
+ *s = '\0'; /* tie off hash text */
+ memset (digest,0,MD5DIGLEN);/* erase sensitive information */
+ if (md5try && !strcmp (md5,tmp) &&
+ authserver_login (user,authuser,argc,argv))
+ ret = cpystr (myusername ());
+ else if (md5try) --md5try;
+ memset (tmp,0,MAILTMPLEN); /* erase sensitive information */
+ }
+ if (!ret) sleep (3); /* slow down possible cracker */
+ return ret;
+}
+
+/*
+ * RFC 2104 HMAC hashing
+ * Accepts: text to hash
+ * text length
+ * key
+ * key length
+ * Returns: hash as text, always
+ */
+
+char *hmac_md5 (char *text,unsigned long tl,char *key,unsigned long kl)
+{
+ int i,j;
+ static char hshbuf[2*MD5DIGLEN + 1];
+ char *s;
+ MD5CONTEXT ctx;
+ char *hex = "0123456789abcdef";
+ unsigned char digest[MD5DIGLEN],k_ipad[MD5BLKLEN+1],k_opad[MD5BLKLEN+1];
+ if (kl > MD5BLKLEN) { /* key longer than pad length? */
+ md5_init (&ctx); /* yes, set key as MD5(key) */
+ md5_update (&ctx,(unsigned char *) key,kl);
+ md5_final (digest,&ctx);
+ key = (char *) digest;
+ kl = MD5DIGLEN;
+ }
+ memcpy (k_ipad,key,kl); /* store key in pads */
+ memset (k_ipad+kl,0,(MD5BLKLEN+1)-kl);
+ memcpy (k_opad,k_ipad,MD5BLKLEN+1);
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < MD5BLKLEN; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ md5_init (&ctx); /* inner MD5: hash ipad and text */
+ md5_update (&ctx,k_ipad,MD5BLKLEN);
+ md5_update (&ctx,(unsigned char *) text,tl);
+ md5_final (digest,&ctx);
+ md5_init (&ctx); /* outer MD5: hash opad and inner results */
+ md5_update (&ctx,k_opad,MD5BLKLEN);
+ md5_update (&ctx,digest,MD5DIGLEN);
+ md5_final (digest,&ctx);
+ /* convert to printable hex */
+ for (i = 0, s = hshbuf; i < MD5DIGLEN; i++) {
+ *s++ = hex[(j = digest[i]) >> 4];
+ *s++ = hex[j & 0xf];
+ }
+ *s = '\0'; /* tie off hash text */
+ return hshbuf;
+}
+
+/* Everything after this point is derived from the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm
+ */
+
+/* You may wonder why these strange "a &= 0xffffffff;" statements are here.
+ * This is to ensure correct results on machines with a unsigned long size of
+ * larger than 32 bits.
+ */
+
+#define RND1(a,b,c,d,x,s,ac) \
+ a += ((b & c) | (d & ~b)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND2(a,b,c,d,x,s,ac) \
+ a += ((b & d) | (c & ~d)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND3(a,b,c,d,x,s,ac) \
+ a += (b ^ c ^ d) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND4(a,b,c,d,x,s,ac) \
+ a += (c ^ (b | ~d)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+/* Initialize MD5 context
+ * Accepts: context to initialize
+ */
+
+void md5_init (MD5CONTEXT *ctx)
+{
+ ctx->clow = ctx->chigh = 0; /* initialize byte count to zero */
+ /* initialization constants */
+ ctx->state[0] = 0x67452301; ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe; ctx->state[3] = 0x10325476;
+ ctx->ptr = ctx->buf; /* reset buffer pointer */
+}
+
+
+/* MD5 add data to context
+ * Accepts: context
+ * input data
+ * length of data
+ */
+
+void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len)
+{
+ unsigned long i = (ctx->buf + MD5BLKLEN) - ctx->ptr;
+ /* update double precision number of bytes */
+ if ((ctx->clow += len) < len) ctx->chigh++;
+ while (i <= len) { /* copy/transform data, 64 bytes at a time */
+ memcpy (ctx->ptr,data,i); /* fill up 64 byte chunk */
+ md5_transform (ctx->state,ctx->ptr = ctx->buf);
+ data += i,len -= i,i = MD5BLKLEN;
+ }
+ memcpy (ctx->ptr,data,len); /* copy final bit of data in buffer */
+ ctx->ptr += len; /* update buffer pointer */
+}
+
+/* MD5 Finalization
+ * Accepts: destination digest
+ * context
+ */
+
+void md5_final (unsigned char *digest,MD5CONTEXT *ctx)
+{
+ unsigned long i,bits[2];
+ bits[0] = ctx->clow << 3; /* calculate length in bits (before padding) */
+ bits[1] = (ctx->chigh << 3) + (ctx->clow >> 29);
+ *ctx->ptr++ = 0x80; /* padding byte */
+ if ((i = (ctx->buf + MD5BLKLEN) - ctx->ptr) < 8) {
+ memset (ctx->ptr,0,i); /* pad out buffer with zeros */
+ md5_transform (ctx->state,ctx->buf);
+ /* pad out with zeros, leaving 8 bytes */
+ memset (ctx->buf,0,MD5BLKLEN - 8);
+ ctx->ptr = ctx->buf + MD5BLKLEN - 8;
+ }
+ else if (i -= 8) { /* need to pad this buffer? */
+ memset (ctx->ptr,0,i); /* yes, pad out with zeros, leaving 8 bytes */
+ ctx->ptr += i;
+ }
+ md5_encode (ctx->ptr,bits,2); /* make LSB-first length */
+ md5_transform (ctx->state,ctx->buf);
+ /* store state in digest */
+ md5_encode (digest,ctx->state,4);
+ /* erase context */
+ memset (ctx,0,sizeof (MD5CONTEXT));
+}
+
+/* MD5 basic transformation
+ * Accepts: state vector
+ * current 64-byte block
+ */
+
+static void md5_transform (unsigned long *state,unsigned char *block)
+{
+ unsigned long a = state[0],b = state[1],c = state[2],d = state[3],x[16];
+ md5_decode (x,block,16); /* decode into 16 longs */
+ /* round 1 */
+ RND1 (a,b,c,d,x[ 0], 7,0xd76aa478); RND1 (d,a,b,c,x[ 1],12,0xe8c7b756);
+ RND1 (c,d,a,b,x[ 2],17,0x242070db); RND1 (b,c,d,a,x[ 3],22,0xc1bdceee);
+ RND1 (a,b,c,d,x[ 4], 7,0xf57c0faf); RND1 (d,a,b,c,x[ 5],12,0x4787c62a);
+ RND1 (c,d,a,b,x[ 6],17,0xa8304613); RND1 (b,c,d,a,x[ 7],22,0xfd469501);
+ RND1 (a,b,c,d,x[ 8], 7,0x698098d8); RND1 (d,a,b,c,x[ 9],12,0x8b44f7af);
+ RND1 (c,d,a,b,x[10],17,0xffff5bb1); RND1 (b,c,d,a,x[11],22,0x895cd7be);
+ RND1 (a,b,c,d,x[12], 7,0x6b901122); RND1 (d,a,b,c,x[13],12,0xfd987193);
+ RND1 (c,d,a,b,x[14],17,0xa679438e); RND1 (b,c,d,a,x[15],22,0x49b40821);
+ /* round 2 */
+ RND2 (a,b,c,d,x[ 1], 5,0xf61e2562); RND2 (d,a,b,c,x[ 6], 9,0xc040b340);
+ RND2 (c,d,a,b,x[11],14,0x265e5a51); RND2 (b,c,d,a,x[ 0],20,0xe9b6c7aa);
+ RND2 (a,b,c,d,x[ 5], 5,0xd62f105d); RND2 (d,a,b,c,x[10], 9, 0x2441453);
+ RND2 (c,d,a,b,x[15],14,0xd8a1e681); RND2 (b,c,d,a,x[ 4],20,0xe7d3fbc8);
+ RND2 (a,b,c,d,x[ 9], 5,0x21e1cde6); RND2 (d,a,b,c,x[14], 9,0xc33707d6);
+ RND2 (c,d,a,b,x[ 3],14,0xf4d50d87); RND2 (b,c,d,a,x[ 8],20,0x455a14ed);
+ RND2 (a,b,c,d,x[13], 5,0xa9e3e905); RND2 (d,a,b,c,x[ 2], 9,0xfcefa3f8);
+ RND2 (c,d,a,b,x[ 7],14,0x676f02d9); RND2 (b,c,d,a,x[12],20,0x8d2a4c8a);
+ /* round 3 */
+ RND3 (a,b,c,d,x[ 5], 4,0xfffa3942); RND3 (d,a,b,c,x[ 8],11,0x8771f681);
+ RND3 (c,d,a,b,x[11],16,0x6d9d6122); RND3 (b,c,d,a,x[14],23,0xfde5380c);
+ RND3 (a,b,c,d,x[ 1], 4,0xa4beea44); RND3 (d,a,b,c,x[ 4],11,0x4bdecfa9);
+ RND3 (c,d,a,b,x[ 7],16,0xf6bb4b60); RND3 (b,c,d,a,x[10],23,0xbebfbc70);
+ RND3 (a,b,c,d,x[13], 4,0x289b7ec6); RND3 (d,a,b,c,x[ 0],11,0xeaa127fa);
+ RND3 (c,d,a,b,x[ 3],16,0xd4ef3085); RND3 (b,c,d,a,x[ 6],23, 0x4881d05);
+ RND3 (a,b,c,d,x[ 9], 4,0xd9d4d039); RND3 (d,a,b,c,x[12],11,0xe6db99e5);
+ RND3 (c,d,a,b,x[15],16,0x1fa27cf8); RND3 (b,c,d,a,x[ 2],23,0xc4ac5665);
+ /* round 4 */
+ RND4 (a,b,c,d,x[ 0], 6,0xf4292244); RND4 (d,a,b,c,x[ 7],10,0x432aff97);
+ RND4 (c,d,a,b,x[14],15,0xab9423a7); RND4 (b,c,d,a,x[ 5],21,0xfc93a039);
+ RND4 (a,b,c,d,x[12], 6,0x655b59c3); RND4 (d,a,b,c,x[ 3],10,0x8f0ccc92);
+ RND4 (c,d,a,b,x[10],15,0xffeff47d); RND4 (b,c,d,a,x[ 1],21,0x85845dd1);
+ RND4 (a,b,c,d,x[ 8], 6,0x6fa87e4f); RND4 (d,a,b,c,x[15],10,0xfe2ce6e0);
+ RND4 (c,d,a,b,x[ 6],15,0xa3014314); RND4 (b,c,d,a,x[13],21,0x4e0811a1);
+ RND4 (a,b,c,d,x[ 4], 6,0xf7537e82); RND4 (d,a,b,c,x[11],10,0xbd3af235);
+ RND4 (c,d,a,b,x[ 2],15,0x2ad7d2bb); RND4 (b,c,d,a,x[ 9],21,0xeb86d391);
+ /* update state */
+ state[0] += a; state[1] += b; state[2] += c; state[3] += d;
+ memset (x,0,sizeof (x)); /* erase sensitive data */
+}
+
+/* You may wonder why these strange "& 0xff" maskings are here. This is to
+ * ensure correct results on machines with a char size of larger than 8 bits.
+ * For example, the KCC compiler on the PDP-10 uses 9-bit chars.
+ */
+
+/* MD5 encode unsigned long into LSB-first bytes
+ * Accepts: destination pointer
+ * source
+ * length of source
+ */
+
+static void md5_encode (unsigned char *dst,unsigned long *src,int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ *dst++ = (unsigned char) (src[i] & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 8) & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 16) & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 24) & 0xff);
+ }
+}
+
+
+/* MD5 decode LSB-first bytes into unsigned long
+ * Accepts: destination pointer
+ * source
+ * length of destination
+ */
+
+static void md5_decode (unsigned long *dst,unsigned char *src,int len)
+{
+ int i, j;
+ for (i = 0, j = 0; i < len; i++, j += 4)
+ dst[i] = ((unsigned long) (src[j] & 0xff)) |
+ (((unsigned long) (src[j+1] & 0xff)) << 8) |
+ (((unsigned long) (src[j+2] & 0xff)) << 16) |
+ (((unsigned long) (src[j+3] & 0xff)) << 24);
+}
diff --git a/imap/src/c-client/auth_pla.c b/imap/src/c-client/auth_pla.c
new file mode 100644
index 00000000..73ca2259
--- /dev/null
+++ b/imap/src/c-client/auth_pla.c
@@ -0,0 +1,133 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Plain authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 30 August 2006
+ */
+
+long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_plain_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_pla = {
+ AU_AUTHUSER | AU_HIDE, /* allow authuser, hidden */
+ "PLAIN", /* authenticator name */
+ NIL, /* always valid */
+ auth_plain_client, /* client method */
+ auth_plain_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char *u,pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* snarl if not SSL/TLS session */
+ if (!mb->sslflag && !mb->tlsflag)
+ mm_log ("SECURITY PROBLEM: insecure server advertised AUTH=PLAIN",WARN);
+ /* get initial (empty) challenge */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ if (clen) { /* abort if challenge non-empty */
+ mm_log ("Server bug: non-empty initial PLAIN challenge",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ pwd[0] = NIL; /* prompt user if empty challenge */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* empty challenge or user requested abort */
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else {
+ unsigned long rlen =
+ strlen (mb->authuser) + strlen (user) + strlen (pwd) + 2;
+ char *response = (char *) fs_get (rlen);
+ char *t = response; /* copy authorization id */
+ if (mb->authuser[0]) for (u = user; *u; *t++ = *u++);
+ *t++ = '\0'; /* delimiting NUL */
+ /* copy authentication id */
+ for (u = mb->authuser[0] ? mb->authuser : user; *u; *t++ = *u++);
+ *t++ = '\0'; /* delimiting NUL */
+ /* copy password */
+ for (u = pwd; *u; *t++ = *u++);
+ /* send credentials */
+ if ((*responder) (stream,response,rlen)) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ memset (response,0,rlen); /* erase credentials */
+ fs_give ((void **) &response);
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_plain_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *user,*aid,*pass;
+ unsigned long len;
+ /* get user name */
+ if (aid = (*responder) ("",0,&len)) {
+ /* note: responders null-terminate */
+ if ((((unsigned long) ((user = aid + strlen (aid) + 1) - aid)) < len) &&
+ (((unsigned long) ((pass = user + strlen (user) + 1) - aid)) < len) &&
+ (((unsigned long) ((pass + strlen (pass)) - aid)) == len) &&
+ (*aid ? server_login (aid,pass,user,argc,argv) :
+ server_login (user,pass,NIL,argc,argv))) ret = myusername ();
+ fs_give ((void **) &aid);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/c-client.h b/imap/src/c-client/c-client.h
new file mode 100644
index 00000000..7bf3710a
--- /dev/null
+++ b/imap/src/c-client/c-client.h
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: c-client master include for application programs
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 19 May 2000
+ * Last Edited: 6 December 2006
+ */
+
+#ifndef __CCLIENT_H /* nobody should include this twice... */
+#define __CCLIENT_H
+
+#ifdef __cplusplus /* help out people who use C++ compilers */
+extern "C" {
+ /* If you use gcc, you may also have to use -fno-operator-names */
+#define private cclientPrivate /* private to c-client */
+#define and cclientAnd /* C99 doesn't realize that ISO 646 is dead */
+#define or cclientOr
+#define not cclientNot
+#endif
+
+#include "mail.h" /* primary interfaces */
+#include "osdep.h" /* OS-dependent routines */
+#include "rfc822.h" /* RFC822 and MIME routines */
+#include "smtp.h" /* SMTP sending routines */
+#include "nntp.h" /* NNTP sending routines */
+#include "utf8.h" /* Unicode and charset routines */
+#include "utf8aux.h" /* Unicode auxillary routines */
+#include "misc.h" /* miscellaneous utility routines */
+
+#ifdef __cplusplus /* undo the C++ mischief */
+#undef private
+}
+#endif
+
+#endif
diff --git a/imap/src/c-client/env.h b/imap/src/c-client/env.h
new file mode 100644
index 00000000..d03e7e63
--- /dev/null
+++ b/imap/src/c-client/env.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 February 2008
+ */
+
+/* Function prototypes */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim);
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim);
+void *env_parameters (long function,void *value);
+void rfc822_date (char *date);
+void rfc822_timezone (char *s,void *t);
+void internal_date (char *date);
+long server_input_wait (long seconds);
+void server_init (char *server,char *service,char *sasl,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint);
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[]);
+long authserver_login (char *user,char *authuser,int argc,char *argv[]);
+long anonymous_login (int argc,char *argv[]);
+char *mylocalhost (void);
+char *myhomedir (void);
+char *mailboxfile (char *dst,char *name);
+MAILSTREAM *default_proto (long type);
diff --git a/imap/src/c-client/flstring.c b/imap/src/c-client/flstring.c
new file mode 100644
index 00000000..bbdbe3c8
--- /dev/null
+++ b/imap/src/c-client/flstring.c
@@ -0,0 +1,91 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include "mail.h"
+#include "flstring.h"
+
+/* String driver for stdio file strings */
+
+static void file_string_init (STRING *s,void *data,unsigned long size);
+static char file_string_next (STRING *s);
+static void file_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize mail string structure for file
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note file descriptor */
+ /* big enough for one byte */
+ s->chunk = s->curpos = (char *) &s->data1;
+ s->size = size; /* data size */
+ s->cursize = s->chunksize = 1;/* always call stdio */
+ file_string_setpos (s,0); /* initial offset is 0 */
+}
+
+
+/* Get next character from string
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char file_string_next (STRING *s)
+{
+ char ret = *s->curpos;
+ s->offset++; /* advance position */
+ s->cursize = 1; /* reset size */
+ *s->curpos = (char) getc ((FILE *) s->data);
+ return ret;
+}
+
+
+/* Set string pointer position
+ * Accepts: string structure
+ * new position
+ */
+
+static void file_string_setpos (STRING *s,unsigned long i)
+{
+ s->offset = i; /* note new offset */
+ fseek ((FILE *) s->data,i,SEEK_SET);
+ /* in case using returnstringstruct hack */
+ s->chunk = s->curpos = (char *) &s->data1;
+ *s->curpos = (char) getc ((FILE *) s->data);
+}
diff --git a/imap/src/c-client/flstring.h b/imap/src/c-client/flstring.h
new file mode 100644
index 00000000..be6a831c
--- /dev/null
+++ b/imap/src/c-client/flstring.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+
+extern STRINGDRIVER file_string;
diff --git a/imap/src/c-client/fs.h b/imap/src/c-client/fs.h
new file mode 100644
index 00000000..7bf32136
--- /dev/null
+++ b/imap/src/c-client/fs.h
@@ -0,0 +1,34 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+void *fs_get (size_t size);
+void fs_resize (void **block,size_t size);
+void fs_give (void **block);
diff --git a/imap/src/c-client/ftl.h b/imap/src/c-client/ftl.h
new file mode 100644
index 00000000..59348ad5
--- /dev/null
+++ b/imap/src/c-client/ftl.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+void fatal (char *string);
diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c
new file mode 100644
index 00000000..1409b37d
--- /dev/null
+++ b/imap/src/c-client/imap4r1.c
@@ -0,0 +1,5672 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Interactive Message Access Protocol 4rev1 (IMAP4R1) routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 1988
+ * Last Edited: 8 May 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the National Institutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+#include "imap4r1.h"
+
+/* Parameters */
+
+#define IMAPLOOKAHEAD 20 /* envelope lookahead */
+#define IMAPUIDLOOKAHEAD 1000 /* UID lookahead */
+#define IMAPTCPPORT (long) 143 /* assigned TCP contact port */
+#define IMAPSSLPORT (long) 993 /* assigned SSL TCP contact port */
+#define MAXCOMMAND 1000 /* RFC 2683 guideline for cmd line length */
+#define IDLETIMEOUT (long) 30 /* defined in RFC 3501 */
+#define MAXSERVERLIT 0x7ffffffe /* maximum server literal size
+ * must be smaller than 4294967295
+ */
+
+
+/* Parsed reply message from imap_reply */
+
+typedef struct imap_parsed_reply {
+ unsigned char *line; /* original reply string pointer */
+ unsigned char *tag; /* command tag this reply is for */
+ unsigned char *key; /* reply keyword */
+ unsigned char *text; /* subsequent text */
+} IMAPPARSEDREPLY;
+
+
+#define IMAPTMPLEN 16*MAILTMPLEN
+
+
+/* IMAP4 I/O stream local data */
+
+typedef struct imap_local {
+ NETSTREAM *netstream; /* TCP I/O stream */
+ IMAPPARSEDREPLY reply; /* last parsed reply */
+ MAILSTATUS *stat; /* status to fill in */
+ IMAPCAP cap; /* server capabilities */
+ char *appendmailbox; /* mailbox being appended to */
+ unsigned int uidsearch : 1; /* UID searching */
+ unsigned int byeseen : 1; /* saw a BYE response */
+ /* got implicit capabilities */
+ unsigned int gotcapability : 1;
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int tlsflag : 1; /* TLS session */
+ unsigned int tlssslv23 : 1; /* TLS using SSLv23 client method */
+ unsigned int notlsflag : 1; /* TLS not used in session */
+ unsigned int sslflag : 1; /* SSL session */
+ unsigned int novalidate : 1; /* certificate not validated */
+ unsigned int filter : 1; /* filter SEARCH/SORT/THREAD results */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+ long authflags; /* required flags for authenticators */
+ unsigned long sortsize; /* sort return data size */
+ unsigned long *sortdata; /* sort return data */
+ struct {
+ unsigned long uid; /* last UID returned */
+ unsigned long msgno; /* last msgno returned */
+ } lastuid;
+ NAMESPACE **namespace; /* namespace return data */
+ THREADNODE *threaddata; /* thread return data */
+ char *referral; /* last referral */
+ char *prefix; /* find prefix */
+ char *user; /* logged-in user */
+ char *reform; /* reformed sequence */
+ char tmp[IMAPTMPLEN]; /* temporary buffer */
+ SEARCHSET *lookahead; /* fetch lookahead */
+} IMAPLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((IMAPLOCAL *) stream->local)
+
+/* Arguments to imap_send() */
+
+typedef struct imap_argument {
+ int type; /* argument type */
+ void *text; /* argument text */
+} IMAPARG;
+
+
+/* imap_send() argument types */
+
+#define ATOM 0
+#define NUMBER 1
+#define FLAGS 2
+#define ASTRING 3
+#define LITERAL 4
+#define LIST 5
+#define SEARCHPROGRAM 6
+#define SORTPROGRAM 7
+#define BODYTEXT 8
+#define BODYPEEK 9
+#define BODYCLOSE 10
+#define SEQUENCE 11
+#define LISTMAILBOX 12
+#define MULTIAPPEND 13
+#define SNLIST 14
+#define MULTIAPPENDREDO 15
+
+
+/* Append data */
+
+typedef struct append_data {
+ append_t af;
+ void *data;
+ char *flags;
+ char *date;
+ STRING *message;
+} APPENDDATA;
+
+/* Function prototypes */
+
+DRIVER *imap_valid (char *name);
+void *imap_parameters (long function,void *value);
+void imap_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void imap_list (MAILSTREAM *stream,char *ref,char *pat);
+void imap_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
+ char *contents);
+long imap_subscribe (MAILSTREAM *stream,char *mailbox);
+long imap_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long imap_create (MAILSTREAM *stream,char *mailbox);
+long imap_delete (MAILSTREAM *stream,char *mailbox);
+long imap_rename (MAILSTREAM *stream,char *old,char *newname);
+long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2);
+long imap_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *imap_open (MAILSTREAM *stream);
+IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
+ char *usr,char *tmp);
+long imap_anon (MAILSTREAM *stream,char *tmp);
+long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr);
+long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr);
+void *imap_challenge (void *stream,unsigned long *len);
+long imap_response (void *stream,char *s,unsigned long size);
+void imap_close (MAILSTREAM *stream,long options);
+void imap_fast (MAILSTREAM *stream,char *sequence,long flags);
+void imap_flags (MAILSTREAM *stream,char *sequence,long flags);
+long imap_overview (MAILSTREAM *stream,overview_t ofn);
+ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags);
+unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno);
+unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid);
+void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+THREADNODE *imap_thread_work (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long imap_ping (MAILSTREAM *stream);
+void imap_check (MAILSTREAM *stream);
+long imap_expunge (MAILSTREAM *stream,char *sequence,long options);
+long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long imap_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
+ char *flags,char *date,STRING *message,
+ APPENDDATA *map,long options);
+IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
+ char *flags,char *date,STRING *message);
+
+void imap_gc (MAILSTREAM *stream,long gcflags);
+void imap_gc_body (BODY *body);
+void imap_capability (MAILSTREAM *stream);
+long imap_acl_work (MAILSTREAM *stream,char *command,IMAPARG *args[]);
+
+IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[]);
+IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s);
+long imap_soutr (MAILSTREAM *stream,char *string);
+IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
+ SIZEDTEXT *as,long wildok,char *limit);
+IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
+ STRING *st);
+IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHPGM *pgm,char *limit);
+char *imap_send_spgm_trim (char *base,char *s,char *text);
+IMAPPARSEDREPLY *imap_send_sset (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHSET *set,char *prefix,
+ char *limit);
+IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char *base,
+ char **s,char *name,STRINGLIST *list,
+ char *limit);
+void imap_send_sdate (char **s,char *name,unsigned short date);
+IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag);
+IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text);
+IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text);
+long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply);
+void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply);
+void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy);
+NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+THREADNODE *imap_parse_thread (MAILSTREAM *stream,unsigned char **txtptr);
+void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
+ STRINGLIST *stl);
+void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+ADDRESS *imap_parse_address (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned char **txtptr);
+unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag);
+unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,unsigned long *len);
+unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,GETS_DATA *md,
+ unsigned long *len,long flags);
+void imap_parse_body (GETS_DATA *md,char *seg,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,
+ unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_disposition (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+STRINGLIST *imap_parse_language (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_extension (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_capabilities (MAILSTREAM *stream,char *t);
+IMAPPARSEDREPLY *imap_fetch (MAILSTREAM *stream,char *sequence,long flags);
+char *imap_reform_sequence (MAILSTREAM *stream,char *sequence,long flags);
+
+/* Driver dispatch used by MAIL */
+
+DRIVER imapdriver = {
+ "imap", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_NEWS|DR_NAMESPACE|DR_CRLF|DR_RECYCLE|DR_HALFOPEN,
+ (DRIVER *) NIL, /* next driver */
+ imap_valid, /* mailbox is valid for us */
+ imap_parameters, /* manipulate parameters */
+ imap_scan, /* scan mailboxes */
+ imap_list, /* find mailboxes */
+ imap_lsub, /* find subscribed mailboxes */
+ imap_subscribe, /* subscribe to mailbox */
+ imap_unsubscribe, /* unsubscribe from mailbox */
+ imap_create, /* create mailbox */
+ imap_delete, /* delete mailbox */
+ imap_rename, /* rename mailbox */
+ imap_status, /* status of mailbox */
+ imap_open, /* open mailbox */
+ imap_close, /* close mailbox */
+ imap_fast, /* fetch message "fast" attributes */
+ imap_flags, /* fetch message flags */
+ imap_overview, /* fetch overview */
+ imap_structure, /* fetch message envelopes */
+ NIL, /* fetch message header */
+ NIL, /* fetch message body */
+ imap_msgdata, /* fetch partial message */
+ imap_uid, /* unique identifier */
+ imap_msgno, /* message number */
+ imap_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ imap_search, /* search for message based on criteria */
+ imap_sort, /* sort messages */
+ imap_thread, /* thread messages */
+ imap_ping, /* ping mailbox to see if still alive */
+ imap_check, /* check for new messages */
+ imap_expunge, /* expunge deleted messages */
+ imap_copy, /* copy messages to another mailbox */
+ imap_append, /* append string message to mailbox */
+ imap_gc /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM imapproto = {&imapdriver};
+
+ /* driver parameters */
+static unsigned long imap_maxlogintrials = MAXLOGINTRIALS;
+static long imap_lookahead = IMAPLOOKAHEAD;
+static long imap_uidlookahead = IMAPUIDLOOKAHEAD;
+static long imap_fetchlookaheadlimit = IMAPLOOKAHEAD;
+static long imap_defaultport = 0;
+static long imap_sslport = 0;
+static long imap_tryssl = NIL;
+static long imap_prefetch = IMAPLOOKAHEAD;
+static long imap_closeonerror = NIL;
+static imapenvelope_t imap_envelope = NIL;
+static imapreferral_t imap_referral = NIL;
+static char *imap_extrahdrs = NIL;
+
+ /* constants */
+static char *hdrheader[] = {
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-MD5 Content-Disposition Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Disposition Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups"
+};
+static char *hdrtrailer ="Followup-To References)]";
+
+/* IMAP validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *imap_valid (char *name)
+{
+ return mail_valid_net (name,&imapdriver,NIL,NIL);
+}
+
+
+/* IMAP manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *imap_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ if (((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->cap.namespace &&
+ !((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace)
+ imap_send (((MAILSTREAM *) value),"NAMESPACE",NIL);
+ value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace;
+ break;
+ case GET_THREADERS:
+ value = (void *)
+ ((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->cap.threader;
+ break;
+ case SET_FETCHLOOKAHEAD: /* must use pointer from GET_FETCHLOOKAHEAD */
+ fatal ("SET_FETCHLOOKAHEAD not permitted");
+ case GET_FETCHLOOKAHEAD:
+ value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->lookahead;
+ break;
+ case SET_MAXLOGINTRIALS:
+ imap_maxlogintrials = (long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) imap_maxlogintrials;
+ break;
+ case SET_LOOKAHEAD:
+ imap_lookahead = (long) value;
+ break;
+ case GET_LOOKAHEAD:
+ value = (void *) imap_lookahead;
+ break;
+ case SET_UIDLOOKAHEAD:
+ imap_uidlookahead = (long) value;
+ break;
+ case GET_UIDLOOKAHEAD:
+ value = (void *) imap_uidlookahead;
+ break;
+
+ case SET_IMAPPORT:
+ imap_defaultport = (long) value;
+ break;
+ case GET_IMAPPORT:
+ value = (void *) imap_defaultport;
+ break;
+ case SET_SSLIMAPPORT:
+ imap_sslport = (long) value;
+ break;
+ case GET_SSLIMAPPORT:
+ value = (void *) imap_sslport;
+ break;
+ case SET_PREFETCH:
+ imap_prefetch = (long) value;
+ break;
+ case GET_PREFETCH:
+ value = (void *) imap_prefetch;
+ break;
+ case SET_CLOSEONERROR:
+ imap_closeonerror = (long) value;
+ break;
+ case GET_CLOSEONERROR:
+ value = (void *) imap_closeonerror;
+ break;
+ case SET_IMAPENVELOPE:
+ imap_envelope = (imapenvelope_t) value;
+ break;
+ case GET_IMAPENVELOPE:
+ value = (void *) imap_envelope;
+ break;
+ case SET_IMAPREFERRAL:
+ imap_referral = (imapreferral_t) value;
+ break;
+ case GET_IMAPREFERRAL:
+ value = (void *) imap_referral;
+ break;
+ case SET_IMAPEXTRAHEADERS:
+ imap_extrahdrs = (char *) value;
+ break;
+ case GET_IMAPEXTRAHEADERS:
+ value = (void *) imap_extrahdrs;
+ break;
+ case SET_IMAPTRYSSL:
+ imap_tryssl = (long) value;
+ break;
+ case GET_IMAPTRYSSL:
+ value = (void *) imap_tryssl;
+ break;
+ case SET_FETCHLOOKAHEADLIMIT:
+ imap_fetchlookaheadlimit = (long) value;
+ break;
+ case GET_FETCHLOOKAHEADLIMIT:
+ value = (void *) imap_fetchlookaheadlimit;
+ break;
+
+ case SET_IDLETIMEOUT:
+ fatal ("SET_IDLETIMEOUT not permitted");
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* IMAP scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void imap_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ imap_list_work (stream,"SCAN",ref,pat,contents);
+}
+
+
+/* IMAP list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void imap_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ imap_list_work (stream,"LIST",ref,pat,NIL);
+}
+
+
+/* IMAP list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void imap_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ /* do it on the server */
+ imap_list_work (stream,"LSUB",ref,pat,NIL);
+ if (*pat == '{') { /* if remote pattern, must be IMAP */
+ if (!imap_valid (pat)) return;
+ ref = NIL; /* good IMAP pattern, punt reference */
+ }
+ /* if remote reference, must be valid IMAP */
+ if (ref && (*ref == '{') && !imap_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (imap_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+/* IMAP find list of mailboxes
+ * Accepts: mail stream
+ * list command
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
+ char *contents)
+{
+ MAILSTREAM *st = stream;
+ int pl;
+ char *s,prefix[MAILTMPLEN],mbx[MAILTMPLEN];
+ IMAPARG *args[4],aref,apat,acont;
+ if (ref && *ref) { /* have a reference? */
+ if (!(imap_valid (ref) && /* make sure valid IMAP name and open stream */
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,ref,OP_HALFOPEN|OP_SILENT))))) return;
+ /* calculate prefix length */
+ pl = strchr (ref,'}') + 1 - ref;
+ strncpy (prefix,ref,pl); /* build prefix */
+ prefix[pl] = '\0'; /* tie off prefix */
+ ref += pl; /* update reference */
+ }
+ else {
+ if (!(imap_valid (pat) && /* make sure valid IMAP name and open stream */
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,pat,OP_HALFOPEN|OP_SILENT))))) return;
+ /* calculate prefix length */
+ pl = strchr (pat,'}') + 1 - pat;
+ strncpy (prefix,pat,pl); /* build prefix */
+ prefix[pl] = '\0'; /* tie off prefix */
+ pat += pl; /* update reference */
+ }
+ LOCAL->prefix = prefix; /* note prefix */
+ if (contents) { /* want to do a scan? */
+ if (LEVELSCAN (stream)) { /* make sure permitted */
+ args[0] = &aref; args[1] = &apat; args[2] = &acont; args[3] = NIL;
+ aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
+ apat.type = LISTMAILBOX; apat.text = (void *) pat;
+ acont.type = ASTRING; acont.text = (void *) contents;
+ imap_send (stream,cmd,args);
+ }
+ else mm_log ("Scan not valid on this IMAP server",ERROR);
+ }
+
+ else if (LEVELIMAP4 (stream)){/* easy if IMAP4 */
+ args[0] = &aref; args[1] = &apat; args[2] = NIL;
+ aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
+ apat.type = LISTMAILBOX; apat.text = (void *) pat;
+ /* referrals armed? */
+ if (LOCAL->cap.mbx_ref && mail_parameters (stream,GET_IMAPREFERRAL,NIL)) {
+ /* yes, convert LIST -> RLIST */
+ if (!compare_cstring (cmd,"LIST")) cmd = "RLIST";
+ /* and convert LSUB -> RLSUB */
+ else if (!compare_cstring (cmd,"LSUB")) cmd = "RLSUB";
+ }
+ imap_send (stream,cmd,args);
+ }
+ else if (LEVEL1176 (stream)) {/* convert to IMAP2 format wildcard */
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+ for (s = mbx; *s; s++) if (*s == '%') *s = '*';
+ args[0] = &apat; args[1] = NIL;
+ apat.type = LISTMAILBOX; apat.text = (void *) mbx;
+ if (!(strstr (cmd,"LIST") &&/* if list, try IMAP2bis, then RFC-1176 */
+ strcmp (imap_send (stream,"FIND ALL.MAILBOXES",args)->key,"BAD")) &&
+ !strcmp (imap_send (stream,"FIND MAILBOXES",args)->key,"BAD"))
+ LOCAL->cap.rfc1176 = NIL; /* must be RFC-1064 */
+ }
+ LOCAL->prefix = NIL; /* no more prefix */
+ /* close temporary stream if we made one */
+ if (stream != st) mail_close (stream);
+}
+
+/* IMAP subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *st = stream;
+ long ret = ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
+ imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
+ "Subscribe" : "Subscribe Mailbox",NIL) : NIL;
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ return ret;
+}
+
+
+/* IMAP unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from manage list
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *st = stream;
+ long ret = ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
+ imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
+ "Unsubscribe" : "Unsubscribe Mailbox",NIL) : NIL;
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ return ret;
+}
+
+/* IMAP create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_create (MAILSTREAM *stream,char *mailbox)
+{
+ return imap_manage (stream,mailbox,"Create",NIL);
+}
+
+
+/* IMAP delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return imap_manage (stream,mailbox,"Delete",NIL);
+}
+
+
+/* IMAP rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return imap_manage (stream,old,"Rename",newname);
+}
+
+/* IMAP manage a mailbox
+ * Accepts: mail stream
+ * mailbox to manipulate
+ * command to execute
+ * optional second argument
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2)
+{
+ MAILSTREAM *st = stream;
+ IMAPPARSEDREPLY *reply;
+ long ret = NIL;
+ char mbx[MAILTMPLEN],mbx2[MAILTMPLEN];
+ IMAPARG *args[3],ambx,amb2;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ ambx.type = amb2.type = ASTRING; ambx.text = (void *) mbx;
+ amb2.text = (void *) mbx2;
+ args[0] = &ambx; args[1] = args[2] = NIL;
+ /* require valid names and open stream */
+ if (mail_valid_net (mailbox,&imapdriver,NIL,mbx) &&
+ (arg2 ? mail_valid_net (arg2,&imapdriver,NIL,mbx2) : &imapdriver) &&
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT)))) {
+ if (arg2) args[1] = &amb2; /* second arg present? */
+ if (!(ret = (imap_OK (stream,reply = imap_send (stream,command,args)))) &&
+ ir && LOCAL->referral) {
+ long code = -1;
+ switch (*command) { /* which command was it? */
+ case 'S': code = REFSUBSCRIBE; break;
+ case 'U': code = REFUNSUBSCRIBE; break;
+ case 'C': code = REFCREATE; break;
+ case 'D': code = REFDELETE; break;
+ case 'R': code = REFRENAME; break;
+ default:
+ fatal ("impossible referral command");
+ }
+ if ((code >= 0) && (mailbox = (*ir) (stream,LOCAL->referral,code)))
+ ret = imap_manage (NIL,mailbox,command,(*command == 'R') ?
+ (mailbox + strlen (mailbox) + 1) : NIL);
+ }
+ mm_log (reply->text,ret ? NIL : ERROR);
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ }
+ return ret;
+}
+
+/* IMAP status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ IMAPARG *args[3],ambx,aflg;
+ char tmp[MAILTMPLEN];
+ NETMBX mb;
+ unsigned long i;
+ long ret = NIL;
+ MAILSTREAM *tstream = NIL;
+ /* use given stream if (rev1 or halfopen) and
+ right host */
+ if (!((stream && (LEVELIMAP4rev1 (stream) || stream->halfopen) &&
+ mail_usable_network_stream (stream,mbx)) ||
+ (stream = tstream = mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT))))
+ return NIL;
+ /* parse mailbox name */
+ mail_valid_net_parse (mbx,&mb);
+ args[0] = &ambx;args[1] = NIL;/* set up first argument as mailbox */
+ ambx.type = ASTRING; ambx.text = (void *) mb.mailbox;
+ if (LEVELIMAP4rev1 (stream)) {/* have STATUS command? */
+ imapreferral_t ir;
+ aflg.type = FLAGS; aflg.text = (void *) tmp;
+ args[1] = &aflg; args[2] = NIL;
+ tmp[0] = tmp[1] = '\0'; /* build flag list */
+ if (flags & SA_MESSAGES) strcat (tmp," MESSAGES");
+ if (flags & SA_RECENT) strcat (tmp," RECENT");
+ if (flags & SA_UNSEEN) strcat (tmp," UNSEEN");
+ if (flags & SA_UIDNEXT) strcat (tmp," UIDNEXT");
+ if (flags & SA_UIDVALIDITY) strcat (tmp," UIDVALIDITY");
+ tmp[0] = '(';
+ strcat (tmp,")");
+ /* send "STATUS mailbox flag" */
+ if (imap_OK (stream,imap_send (stream,"STATUS",args))) ret = T;
+ else if ((ir = (imapreferral_t)
+ mail_parameters (stream,GET_IMAPREFERRAL,NIL)) &&
+ LOCAL->referral &&
+ (mbx = (*ir) (stream,LOCAL->referral,REFSTATUS)))
+ ret = imap_status (NIL,mbx,flags | (stream->debug ? SA_DEBUG : NIL));
+ }
+
+ /* IMAP2 way */
+ else if (imap_OK (stream,imap_send (stream,"EXAMINE",args))) {
+ MAILSTATUS status;
+ status.flags = flags & ~ (SA_UIDNEXT | SA_UIDVALIDITY);
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ status.unseen = 0;
+ if (flags & SA_UNSEEN) { /* must search to get unseen messages */
+ /* clear search vector */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
+ if (imap_OK (stream,imap_send (stream,"SEARCH UNSEEN",NIL)))
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->searched) status.unseen++;
+ }
+ strcpy (strchr (strcpy (tmp,stream->mailbox),'}') + 1,mb.mailbox);
+ /* pass status to main program */
+ mm_status (stream,tmp,&status);
+ ret = T; /* note success */
+ }
+ if (tstream) mail_close (tstream);
+ return ret; /* success */
+}
+
+/* IMAP open
+ * Accepts: stream to open
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *imap_open (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ char *s,tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ NETMBX mb;
+ IMAPPARSEDREPLY *reply = NIL;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &imapproto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ usr[0] = '\0'; /* initially no user name */
+ if (LOCAL) { /* if stream opened earlier by us */
+ /* recycle if still alive */
+ if (LOCAL->netstream && (!stream->halfopen || LOCAL->cap.unselect)) {
+ i = stream->silent; /* temporarily mark silent */
+ stream->silent = T; /* don't give mm_exists() events */
+ j = imap_ping (stream); /* learn if stream still alive */
+ stream->silent = i; /* restore prior state */
+ if (j) { /* was stream still alive? */
+ sprintf (tmp,"Reusing connection to %s",net_host (LOCAL->netstream));
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",
+ LOCAL->user);
+ if (!stream->silent) mm_log (tmp,(long) NIL);
+ /* unselect if now want halfopen */
+ if (stream->halfopen) imap_send (stream,"UNSELECT",NIL);
+ }
+ else imap_close (stream,NIL);
+ }
+ else imap_close (stream,NIL);
+ }
+ /* copy flags from name */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.readonlyflag) stream->rdonly = T;
+ if (mb.anoflag) stream->anonymous = T;
+ if (mb.secflag) stream->secure = T;
+ if (mb.trysslflag || imap_tryssl) stream->tryssl = T;
+
+ if (!LOCAL) { /* open new connection if no recycle */
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ unsigned long defprt = imap_defaultport ? imap_defaultport : IMAPTCPPORT;
+ unsigned long sslport = imap_sslport ? imap_sslport : IMAPSSLPORT;
+ stream->local = /* instantiate localdata */
+ (void *) memset (fs_get (sizeof (IMAPLOCAL)),0,sizeof (IMAPLOCAL));
+ /* assume IMAP2bis server */
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ /* in case server is a loser */
+ if (mb.loser) LOCAL->loser = T;
+ /* desirable authenticators */
+ LOCAL->authflags = (stream->secure ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL);
+ /* IMAP connection open logic is more complex than net_open() normally
+ * deals with, because of the simap and rimap hacks.
+ * If the session is anonymous, a specific port is given, or if /ssl or
+ * /tls is set, do net_open() since those conditions override everything
+ * else.
+ */
+ if (stream->anonymous || mb.port || mb.sslflag || mb.tlsflag)
+ reply = (LOCAL->netstream = net_open (&mb,NIL,defprt,ssld,"*imaps",
+ sslport)) ?
+ imap_reply (stream,NIL) : NIL;
+ /*
+ * No overriding conditions, so get the best connection that we can. In
+ * order, attempt to open via simap, tryssl, rimap, and finally TCP.
+ */
+ /* try simap */
+ else if (reply = imap_rimap (stream,"*imap",&mb,usr,tmp));
+ else if (ssld && /* try tryssl if enabled */
+ (stream->tryssl || mail_parameters (NIL,GET_TRYSSLFIRST,NIL)) &&
+ (LOCAL->netstream =
+ net_open_work (ssld,mb.host,"*imaps",sslport,mb.port,
+ (mb.novalidate ? NET_NOVALIDATECERT : 0) |
+ NET_SILENT | NET_TRYSSL))) {
+ if (net_sout (LOCAL->netstream,"",0)) {
+ mb.sslflag = T;
+ reply = imap_reply (stream,NIL);
+ }
+ else { /* flush fake SSL stream */
+ net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ }
+ }
+ /* try rimap first, then TCP */
+ else if (!(reply = imap_rimap (stream,"imap",&mb,usr,tmp)) &&
+ (LOCAL->netstream = net_open (&mb,NIL,defprt,NIL,NIL,NIL)))
+ reply = imap_reply (stream,NIL);
+ /* make sure greeting is good */
+ if (!reply || strcmp (reply->tag,"*") ||
+ (strcmp (reply->key,"OK") && strcmp (reply->key,"PREAUTH"))) {
+ if (reply) mm_log (reply->text,ERROR);
+ return NIL; /* lost during greeting */
+ }
+
+ /* if connected and not preauthenticated */
+ if (LOCAL->netstream && strcmp (reply->key,"PREAUTH")) {
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ /* get server capabilities */
+ if (!LOCAL->gotcapability) imap_capability (stream);
+ if (LOCAL->netstream && /* does server support STARTTLS? */
+ stls && LOCAL->cap.starttls && !mb.sslflag && !mb.notlsflag &&
+ imap_OK (stream,imap_send (stream,"STARTTLS",NIL))) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ LOCAL->netstream->dtb = ssld;
+ if (!(LOCAL->netstream->stream =
+ (*stls) (LOCAL->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT : NIL)))) {
+ /* drat, drop this connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ }
+ /* get capabilities now that TLS in effect */
+ if (LOCAL->netstream) imap_capability (stream);
+ }
+ else if (mb.tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ if (LOCAL->netstream) { /* still in the land of the living? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,(long) mail_parameters(NIL,GET_SASLUSESPTRNAME,NIL)?
+ net_remotehost (LOCAL->netstream) :
+ net_host (LOCAL->netstream),NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ /* need new capabilities after login */
+ LOCAL->gotcapability = NIL;
+ if (!(stream->anonymous ? imap_anon (stream,tmp) :
+ (LOCAL->cap.auth ? imap_auth (stream,&mb,tmp,usr) :
+ imap_login (stream,&mb,tmp,usr)))) {
+ /* failed, is there a referral? */
+ if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFAUTHFAILED))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ /* set as new mailbox name to open */
+ stream->mailbox = s;
+ return imap_open (stream);
+ }
+ return NIL; /* authentication failed */
+ }
+ else if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFAUTH))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = s; /* set as new mailbox name to open */
+ /* recurse to log in on real site */
+ return imap_open (stream);
+ }
+ }
+ }
+ /* get server capabilities again */
+ if (LOCAL->netstream && !LOCAL->gotcapability) imap_capability (stream);
+ /* save state for future recycling */
+ if (mb.tlsflag) LOCAL->tlsflag = T;
+ if (mb.tlssslv23) LOCAL->tlssslv23 = T;
+ if (mb.notlsflag) LOCAL->notlsflag = T;
+ if (mb.sslflag) LOCAL->sslflag = T;
+ if (mb.novalidate) LOCAL->novalidate = T;
+ if (mb.loser) LOCAL->loser = T;
+ }
+
+ if (LOCAL->netstream) { /* still have a connection? */
+ stream->perm_seen = stream->perm_deleted = stream->perm_answered =
+ stream->perm_draft = LEVELIMAP4 (stream) ? NIL : T;
+ stream->perm_user_flags = LEVELIMAP4 (stream) ? NIL : 0xffffffff;
+ stream->sequence++; /* bump sequence number */
+ sprintf (tmp,"{%s",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (LOCAL->netstream) : mb.host);
+ if (!((i = net_port (LOCAL->netstream)) & 0xffff0000))
+ sprintf (tmp + strlen (tmp),":%lu",i);
+ strcat (tmp,"/imap");
+ if (LOCAL->tlsflag) strcat (tmp,"/tls");
+ if (LOCAL->tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (LOCAL->notlsflag) strcat (tmp,"/notls");
+ if (LOCAL->sslflag) strcat (tmp,"/ssl");
+ if (LOCAL->novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ if (stream->rdonly) strcat (tmp,"/readonly");
+ if (stream->anonymous) strcat (tmp,"/anonymous");
+ else { /* record user name */
+ if (!LOCAL->user && usr[0]) LOCAL->user = cpystr (usr);
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",
+ LOCAL->user);
+ }
+ strcat (tmp,"}");
+
+ if (!stream->halfopen) { /* wants to open a mailbox? */
+ IMAPARG *args[2];
+ IMAPARG ambx;
+ ambx.type = ASTRING;
+ ambx.text = (void *) mb.mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ stream->nmsgs = 0;
+ if (imap_OK (stream,reply = imap_send (stream,stream->rdonly ?
+ "EXAMINE": "SELECT",args))) {
+ strcat (tmp,mb.mailbox);/* mailbox name */
+ if (!stream->nmsgs && !stream->silent)
+ mm_log ("Mailbox is empty",(long) NIL);
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (mb.mailbox,"INBOX");
+ }
+ else if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFSELECT))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = s; /* set as new mailbox name to open */
+ return imap_open (stream);
+ }
+ else {
+ mm_log (reply->text,ERROR);
+ if (imap_closeonerror) return NIL;
+ stream->halfopen = T; /* let him keep it half-open */
+ }
+ }
+ if (stream->halfopen) { /* half-open connection? */
+ strcat (tmp,"<no_mailbox>");
+ /* make sure dummy message counts */
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ }
+ /* success if stream open */
+ return LOCAL->netstream ? stream : NIL;
+}
+
+/* IMAP rimap connect
+ * Accepts: MAIL stream
+ * NETMBX specification
+ * service to use
+ * user name
+ * scratch buffer
+ * Returns: parsed reply if success, else NIL
+ */
+
+IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
+ char *usr,char *tmp)
+{
+ unsigned long i;
+ char c[2];
+ NETSTREAM *tstream;
+ IMAPPARSEDREPLY *reply = NIL;
+ /* try rimap open */
+ if (!mb->norsh && (tstream = net_aopen (NIL,mb,service,usr))) {
+ /* if success, see if reasonable banner */
+ if (net_getbuffer (tstream,(long) 1,c) && (*c == '*')) {
+ i = 0; /* copy to buffer */
+ do tmp[i++] = *c;
+ while (net_getbuffer (tstream,(long) 1,c) && (*c != '\015') &&
+ (*c != '\012') && (i < (MAILTMPLEN-1)));
+ tmp[i] = '\0'; /* tie off */
+ /* snarfed a valid greeting? */
+ if ((*c == '\015') && net_getbuffer (tstream,(long) 1,c) &&
+ (*c == '\012') &&
+ !strcmp ((reply = imap_parse_reply (stream,cpystr (tmp)))->tag,"*")){
+ /* parse line as IMAP */
+ imap_parse_unsolicited (stream,reply);
+ /* make sure greeting is good */
+ if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH")) {
+ LOCAL->netstream = tstream;
+ return reply; /* return success */
+ }
+ }
+ }
+ net_close (tstream); /* failed, punt the temporary netstream */
+ }
+ return NIL;
+}
+
+/* IMAP log in as anonymous
+ * Accepts: stream to authenticate
+ * scratch buffer
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_anon (MAILSTREAM *stream,char *tmp)
+{
+ IMAPPARSEDREPLY *reply;
+ char *s = net_localhost (LOCAL->netstream);
+ if (LOCAL->cap.authanon) {
+ char tag[16];
+ unsigned long i;
+ char *broken = "[CLOSED] IMAP connection broken (anonymous auth)";
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ /* build command */
+ sprintf (tmp,"%s AUTHENTICATE ANONYMOUS",tag);
+ if (!imap_soutr (stream,tmp)) {
+ mm_log (broken,ERROR);
+ return NIL;
+ }
+ if (imap_challenge (stream,&i)) imap_response (stream,s,strlen (s));
+ /* get response */
+ if (!(reply = &LOCAL->reply)->tag) reply = imap_fake (stream,tag,broken);
+ /* what we wanted? */
+ if (compare_cstring (reply->tag,tag)) {
+ /* abort if don't have tagged response */
+ while (compare_cstring ((reply = imap_reply (stream,tag))->tag,tag))
+ imap_soutr (stream,"*");
+ }
+ }
+ else {
+ IMAPARG *args[2];
+ IMAPARG ausr;
+ ausr.type = ASTRING;
+ ausr.text = (void *) s;
+ args[0] = &ausr; args[1] = NIL;
+ /* send "LOGIN anonymous <host>" */
+ reply = imap_send (stream,"LOGIN ANONYMOUS",args);
+ }
+ /* success if reply OK */
+ if (imap_OK (stream,reply)) return T;
+ mm_log (reply->text,ERROR);
+ return NIL;
+}
+
+/* IMAP authenticate
+ * Accepts: stream to authenticate
+ * parsed network mailbox structure
+ * scratch buffer
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
+{
+ unsigned long trial,ua;
+ int ok;
+ char tag[16];
+ char *lsterr = NIL;
+ AUTHENTICATOR *at;
+ IMAPPARSEDREPLY *reply;
+ for (ua = LOCAL->cap.auth, LOCAL->saslcancel = NIL; LOCAL->netstream && ua &&
+ (at = mail_lookup_auth (find_rightmost_bit (&ua) + 1));) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* no error */
+ do { /* gensym a new tag */
+ if (lsterr) { /* previous attempt with this one failed? */
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ LOCAL->saslcancel = NIL;
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ /* build command */
+ sprintf (tmp,"%s AUTHENTICATE %s",tag,at->name);
+ if (imap_soutr (stream,tmp)) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
+ ok = (*at->client) (imap_challenge,imap_response,"imap",mb,stream,
+ &trial,usr);
+ LOCAL->sensitive = NIL; /* unhide */
+ /* make sure have a response */
+ if (!(reply = &LOCAL->reply)->tag)
+ reply = imap_fake (stream,tag,
+ "[CLOSED] IMAP connection broken (authenticate)");
+ else if (compare_cstring (reply->tag,tag))
+ while (compare_cstring ((reply = imap_reply (stream,tag))->tag,tag))
+ imap_soutr (stream,"*");
+ /* good if SASL ok and success response */
+ if (ok && imap_OK (stream,reply)) return T;
+ if (!trial) { /* if main program requested cancellation */
+ mm_log ("IMAP Authentication cancelled",ERROR);
+ return NIL;
+ }
+ /* no error if protocol-initiated cancel */
+ lsterr = cpystr (reply->text);
+ }
+ }
+ while (LOCAL->netstream && !LOCAL->byeseen && trial &&
+ (trial < imap_maxlogintrials));
+ }
+ if (lsterr) { /* previous authenticator failed? */
+ if (!LOCAL->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to IMAP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ return NIL; /* ran out of authenticators */
+}
+
+/* IMAP login
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer of length MAILTMPLEN
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
+{
+ unsigned long trial = 0;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3];
+ IMAPARG ausr,apwd;
+ long ret = NIL;
+ if (stream->secure) /* never do LOGIN if want security */
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ /* never do LOGIN if server disabled it */
+ else if (LOCAL->cap.logindisabled)
+ mm_log ("Server disables LOGIN, no recognized SASL authenticator",ERROR);
+ else if (mb->authuser[0]) /* never do LOGIN with /authuser */
+ mm_log ("Can't do /authuser with this server",ERROR);
+ else { /* OK to try login */
+ ausr.type = apwd.type = ASTRING;
+ ausr.text = (void *) usr;
+ apwd.text = (void *) pwd;
+ args[0] = &ausr; args[1] = &apwd; args[2] = NIL;
+ do {
+ pwd[0] = 0; /* prompt user for password */
+ mm_login (mb,usr,pwd,trial++);
+ if (pwd[0]) { /* send login command if have password */
+ LOCAL->sensitive = T; /* hide this command */
+ /* send "LOGIN usr pwd" */
+ if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args)))
+ ret = LONGT; /* success */
+ else {
+ mm_log (reply->text,WARN);
+ if (!LOCAL->referral && (trial == imap_maxlogintrials))
+ mm_log ("Too many login failures",ERROR);
+ }
+ LOCAL->sensitive = NIL; /* unhide */
+ }
+ /* user refused to give password */
+ else mm_log ("Login aborted",ERROR);
+ } while (!ret && pwd[0] && (trial < imap_maxlogintrials) &&
+ LOCAL->netstream && !LOCAL->byeseen && !LOCAL->referral);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *imap_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ IMAPPARSEDREPLY *reply = NIL;
+ /* get tagged response or challenge */
+ while (stream && LOCAL->netstream &&
+ (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) &&
+ !strcmp (reply->tag,"*")) imap_parse_unsolicited (stream,reply);
+ /* parse challenge if have one */
+ if (stream && LOCAL->netstream && reply && reply->tag &&
+ (*reply->tag == '+') && !reply->tag[1] && reply->text &&
+ !(ret = rfc822_base64 ((unsigned char *) reply->text,
+ strlen (reply->text),len))) {
+ sprintf (tmp,"IMAP SERVER BUG (invalid challenge): %.80s",
+ (char *) reply->text);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T if successful, else NIL
+ */
+
+long imap_response (void *s,char *response,unsigned long size)
+{
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ unsigned long i,j,ret;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string for mm_dlog() */
+ if (stream->debug) mail_dlog (t,LOCAL->sensitive);
+ /* append CRLF */
+ *u++ = '\015'; *u++ = '\012';
+ ret = net_sout (LOCAL->netstream,t,u - t);
+ fs_give ((void **) &t);
+ }
+ else ret = imap_soutr (stream,"");
+ }
+ else { /* abort requested */
+ ret = imap_soutr (stream,"*");
+ LOCAL->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return ret;
+}
+
+/* IMAP close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void imap_close (MAILSTREAM *stream,long options)
+{
+ THREADER *thr,*t;
+ IMAPPARSEDREPLY *reply;
+ if (stream && LOCAL) { /* send "LOGOUT" */
+ if (!LOCAL->byeseen) { /* don't even think of doing it if saw a BYE */
+ /* expunge silently if requested */
+ if (options & CL_EXPUNGE)
+ imap_send (stream,LEVELIMAP4 (stream) ? "CLOSE" : "EXPUNGE",NIL);
+ if (LOCAL->netstream &&
+ !imap_OK (stream,reply = imap_send (stream,"LOGOUT",NIL)))
+ mm_log (reply->text,WARN);
+ }
+ /* close NET connection if still open */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ /* free up memory */
+ if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
+ if (LOCAL->namespace) {
+ mail_free_namespace (&LOCAL->namespace[0]);
+ mail_free_namespace (&LOCAL->namespace[1]);
+ mail_free_namespace (&LOCAL->namespace[2]);
+ fs_give ((void **) &LOCAL->namespace);
+ }
+ if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
+ /* flush threaders */
+ if (thr = LOCAL->cap.threader) while (t = thr) {
+ fs_give ((void **) &t->name);
+ thr = t->next;
+ fs_give ((void **) &t);
+ }
+ if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
+ if (LOCAL->user) fs_give ((void **) &LOCAL->user);
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ if (LOCAL->reform) fs_give ((void **) &LOCAL->reform);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ }
+}
+
+/* IMAP fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ *
+ * Generally, imap_structure is preferred
+ */
+
+void imap_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ IMAPPARSEDREPLY *reply = imap_fetch (stream,sequence,flags & FT_UID);
+ if (!imap_OK (stream,reply)) mm_log (reply->text,ERROR);
+}
+
+
+/* IMAP fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void imap_flags (MAILSTREAM *stream,char *sequence,long flags)
+{ /* send "FETCH sequence FLAGS" */
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & FT_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ aatt.type = ATOM; aatt.text = (void *) "FLAGS";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
+ mm_log (reply->text,ERROR);
+}
+
+/* IMAP fetch overview
+ * Accepts: MAIL stream, sequence bits set
+ * pointer to overview return function
+ * Returns: T if successful, NIL otherwise
+ */
+
+long imap_overview (MAILSTREAM *stream,overview_t ofn)
+{
+ MESSAGECACHE *elt;
+ ENVELOPE *env;
+ OVERVIEW ov;
+ char *s,*t;
+ unsigned long i,start,last,len,slen;
+ if (!LOCAL->netstream) return NIL;
+ /* build overview sequence */
+ for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->private.msg.env) {
+ if (s) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) sprintf (t,":%lu,%lu",last,i);
+ else sprintf (t,",%lu",i);
+ if ((len - (slen = (t += strlen (t)) - s)) < 20) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + slen; /* relocate current pointer */
+ }
+ start = last = i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = i);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ }
+ /* last sequence */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* prefetch as needed */
+ imap_fetch (stream,s,FT_NEEDENV);
+ fs_give ((void **) &s);
+ }
+ ov.optional.lines = 0; /* now overview each message */
+ ov.optional.xref = NIL;
+ if (ofn) for (i = 1; i <= stream->nmsgs; i++)
+ if (((elt = mail_elt (stream,i))->sequence) &&
+ (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
+ ov.subject = env->subject;
+ ov.from = env->from;
+ ov.date = env->date;
+ ov.message_id = env->message_id;
+ ov.references = env->references;
+ ov.optional.octets = elt->rfc822_size;
+ (*ofn) (stream,mail_uid (stream,i),&ov,i);
+ }
+ return LONGT;
+}
+
+/* IMAP fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ unsigned long i,j,k,x;
+ char *s,seq[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ ENVELOPE **env;
+ BODY **b;
+ IMAPPARSEDREPLY *reply = NIL;
+ IMAPARG *args[3],aseq,aatt;
+ SEARCHSET *set = LOCAL->lookahead;
+ LOCAL->lookahead = NIL;
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = NIL;
+ if (flags & FT_UID) /* see if can find msgno from UID */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.uid == msgno) {
+ msgno = i; /* found msgno, use it from now on */
+ flags &= ~FT_UID; /* no longer a UID fetch */
+ }
+ sprintf (s = seq,"%lu",msgno);/* initial sequence */
+ if (LEVELIMAP4 (stream) && (flags & FT_UID)) {
+ /* UID fetching is requested and we can't map the UID to a message sequence
+ * number. Assume that the message isn't cached at all.
+ */
+ if (!imap_OK (stream,reply = imap_fetch (stream,seq,FT_NEEDENV +
+ (body ? FT_NEEDBODY : NIL) +
+ (flags & (FT_UID + FT_NOHDRS)))))
+ mm_log (reply->text,ERROR);
+ /* now hunt for this UID */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.uid == msgno) {
+ if (body) *body = elt->private.msg.body;
+ return elt->private.msg.env;
+ }
+ if (body) *body = NIL; /* can't find the UID */
+ return NIL;
+ }
+ elt = mail_elt (stream,msgno);/* get cache pointer */
+ if (stream->scache) { /* short caching? */
+ env = &stream->env; /* use temporaries on the stream */
+ b = &stream->body;
+ if (msgno != stream->msgno){/* flush old poop if a different message */
+ mail_free_envelope (env);
+ mail_free_body (b);
+ stream->msgno = msgno; /* this is now the current short cache msg */
+ }
+ }
+
+ else { /* normal cache */
+ env = &elt->private.msg.env;/* get envelope and body pointers */
+ b = &elt->private.msg.body;
+ /* prefetch if don't have envelope */
+ if (!(flags & FT_NOLOOKAHEAD) &&
+ ((!*env || (*env)->incomplete) ||
+ (body && !*b && LEVELIMAP2bis (stream)))) {
+ if (set) { /* have a lookahead list? */
+ MESSAGE *msg;
+ for (k = imap_fetchlookaheadlimit;
+ k && set && (((s += strlen (s)) - seq) < (MAXCOMMAND - 30));
+ set = set->next) {
+ i = (set->first == 0xffffffff) ? stream->nmsgs :
+ min (set->first,stream->nmsgs);
+ if (j = (set->last == 0xffffffff) ? stream->nmsgs :
+ min (set->last,stream->nmsgs)) {
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ /* find first message not msgno or in cache */
+ while (((i == msgno) ||
+ ((msg = &(mail_elt (stream,i)->private.msg))->env &&
+ (!body || msg->body))) && (i++ < j));
+ /* until range or lookahead finished */
+ while (k && (i <= j)) {
+ /* find first cached message in range */
+ for (x = i + 1; (x <= j) &&
+ !((msg = &(mail_elt (stream,x)->private.msg))->env &&
+ (!body || msg->body)); x++);
+ if (i == --x) { /* only one message? */
+ sprintf (s += strlen (s),",%lu",i++);
+ k--; /* prefetching one message */
+ }
+ else { /* a range to prefetch */
+ sprintf (s += strlen (s),",%lu:%lu",i,x);
+ i = 1 + x - i; /* number of messages in this range */
+ /* still can look ahead some more? */
+ if (k = (k > i) ? k - i : 0)
+ /* yes, scan further in this range */
+ for (i = x + 2; (i <= j) &&
+ ((i == msgno) ||
+ ((msg = &(mail_elt (stream,i)->private.msg))->env &&
+ (!body || msg->body)));
+ i++);
+ }
+ }
+ }
+ else if ((i != msgno) && !mail_elt (stream,i)->private.msg.env) {
+ sprintf (s += strlen (s),",%lu",i);
+ k--; /* prefetching one message */
+ }
+ }
+ }
+ /* build message number list */
+ else for (i = msgno+1,k = imap_lookahead; k && (i <= stream->nmsgs); i++)
+ if (!mail_elt (stream,i)->private.msg.env) {
+ s += strlen (s); /* find string end, see if nearing end */
+ if ((s - seq) > (MAILTMPLEN - 20)) break;
+ sprintf (s,",%lu",i); /* append message */
+ for (j = i + 1, k--; /* hunt for last message without an envelope */
+ k && (j <= stream->nmsgs) &&
+ !mail_elt (stream,j)->private.msg.env; j++, k--);
+ /* if different, make a range */
+ if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
+ }
+ }
+ }
+
+ if (!stream->lock) { /* no-op if stream locked */
+ /* Build the fetch attributes. Unlike imap_fetch(), this tries not to
+ * fetch data that is already cached. However, since it is based on the
+ * message requested and not on any of the prefetched messages, it can
+ * goof, either by fetching data already cached or not prefetching data
+ * that isn't cached (but was cached in the message requested).
+ * Fortunately, no great harm is done. If it doesn't prefetch the data,
+ * it will get it when the affected message(s) are requested.
+ */
+ if (!elt->private.uid && LEVELIMAP4 (stream)) strcpy (tmp," UID");
+ else tmp[0] = '\0'; /* initialize command */
+ /* need envelope? */
+ if (!*env || (*env)->incomplete) {
+ strcat (tmp," ENVELOPE"); /* yes, get it and possible extra poop */
+ if (!(flags & FT_NOHDRS) && LEVELIMAP4rev1 (stream)) {
+ if (imap_extrahdrs) sprintf (tmp + strlen (tmp)," %s %s %s",
+ hdrheader[LOCAL->cap.extlevel],
+ imap_extrahdrs,hdrtrailer);
+ else sprintf (tmp + strlen (tmp)," %s %s",
+ hdrheader[LOCAL->cap.extlevel],hdrtrailer);
+ }
+ }
+ /* need body? */
+ if (body && !*b && LEVELIMAP2bis (stream))
+ strcat (tmp,LEVELIMAP4 (stream) ? " BODYSTRUCTURE" : " BODY");
+ if (!elt->day) strcat (tmp," INTERNALDATE");
+ if (!elt->rfc822_size) strcat (tmp," RFC822.SIZE");
+ if (tmp[0]) { /* anything to do? */
+ tmp[0] = '('; /* make into a list */
+ strcat (tmp," FLAGS)"); /* always get current flags */
+ aatt.text = (void *) tmp; /* do the built command */
+ if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args))) {
+ /* failed, probably RFC-1176 server */
+ if (!LEVELIMAP4 (stream) && LEVELIMAP2bis (stream) && body && !*b){
+ aatt.text = (void *) "ALL";
+ if (imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
+ /* doesn't have body capabilities */
+ LOCAL->cap.imap2bis = NIL;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log (reply->text,ERROR);
+ }
+ }
+ }
+ if (body) { /* wants to return body */
+ if (!*b && !LEVELIMAP2bis (stream)) {
+ /* simulate body structure fetch for IMAP2 */
+ *b = mail_initbody (mail_newbody ());
+ (*b)->subtype = cpystr (rfc822_default_subtype ((*b)->type));
+ ((*b)->parameter = mail_newbody_parameter ())->attribute =
+ cpystr ("CHARSET");
+ (*b)->parameter->value = cpystr ("US-ASCII");
+ s = mail_fetch_text (stream,msgno,NIL,&i,flags);
+ (*b)->size.bytes = i;
+ while (i--) if (*s++ == '\n') (*b)->size.lines++;
+ }
+ *body = *b; /* return the body */
+ }
+ return *env; /* return the envelope */
+}
+
+/* IMAP fetch message data
+ * Accepts: MAIL stream
+ * message number
+ * section specifier
+ * offset of first designated byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * lines to fetch if header
+ * flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags)
+{
+ int i;
+ char *t,tmp[MAILTMPLEN],partial[40],seq[40];
+ char *noextend,*nopartial,*nolines,*nopeek,*nononpeek;
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[5],*auxargs[3],aseq,aatt,alns,acls,aflg;
+ noextend = nopartial = nolines = nopeek = nononpeek = NIL;
+ /* does searching desire a lookahead? */
+ if ((flags & FT_SEARCHLOOKAHEAD) && (msgno < stream->nmsgs) &&
+ !stream->scache) {
+ sprintf (seq,"%lu:%lu",msgno,
+ (unsigned long) min (msgno + IMAPLOOKAHEAD,stream->nmsgs));
+ aseq.type = SEQUENCE;
+ aseq.text = (void *) seq;
+ }
+ else { /* no, do it the easy way */
+ aseq.type = NUMBER;
+ aseq.text = (void *) msgno;
+ }
+ aatt.type = ATOM; /* assume atomic attribute */
+ alns.type = LIST; alns.text = (void *) lines;
+ acls.type = BODYCLOSE; acls.text = (void *) partial;
+ aflg.type = ATOM; aflg.text = (void *) "FLAGS";
+ args[0] = &aseq; args[1] = &aatt; args[2] = args[3] = args[4] = NIL;
+ auxargs[0] = &aseq; auxargs[1] = &aflg; auxargs[2] = NIL;
+ partial[0] = '\0'; /* initially no partial specifier */
+ if (LEVELIMAP4rev1 (stream)) {/* easy if IMAP4rev1 server */
+ /* HEADER fetching with special handling? */
+ if (!strcmp (section,"HEADER") && (lines || (flags & FT_PREFETCHTEXT))) {
+ if (lines) { /* want specific header lines? */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ aatt.text = (void *) ((flags & FT_NOT) ?
+ "HEADER.FIELDS.NOT" : "HEADER.FIELDS");
+ args[2] = &alns; args[3] = &acls;
+ }
+ /* must be prefetching */
+ else aatt.text = (void *) ((flags & FT_PEEK) ?
+ "(BODY.PEEK[HEADER] BODY.PEEK[TEXT])" :
+ "(BODY[HEADER] BODY[TEXT])");
+ }
+ else { /* simple case */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ aatt.text = (void *) section;
+ args[2] = &acls;
+ }
+ if (first || last) sprintf (partial,"<%lu.%lu>",first,last ? last:-1);
+ }
+
+ /* IMAP4 did not have:
+ * . HEADER body part (can simulate with BODY[0] or BODY.PEEK[0])
+ * . TEXT body part (can simulate top-level with RFC822.TEXT or
+ * RFC822.TEXT.PEEK)
+ * . MIME body part
+ * . (usable) partial fetching
+ * . (usable) selective header line fetching
+ */
+ else if (LEVEL1730 (stream)) {/* IMAP4 (RFC 1730) compatibility */
+ /* BODY[HEADER] becomes BODY.PEEK[0] */
+ if (!strcmp (section,"HEADER"))
+ aatt.text = (void *)
+ ((flags & FT_PREFETCHTEXT) ?
+ ((flags & FT_PEEK) ? "(BODY.PEEK[0] RFC822.TEXT.PEEK)" :
+ "(BODY[0] RFC822.TEXT)") :
+ ((flags & FT_PEEK) ? "BODY.PEEK[0]" : "BODY[0]"));
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT"))
+ aatt.text = (void *) ((flags & FT_PEEK) ? "RFC822.TEXT.PEEK" :
+ "RFC822.TEXT");
+ else if (!section[0]) /* BODY[] becomes RFC822 */
+ aatt.text = (void *) ((flags & FT_PEEK) ? "RFC822.PEEK" : "RFC822");
+ /* nested header */
+ else if (t = strstr (section,".HEADER")) {
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) tmp; /* convert .HEADER to .0 */
+ strncpy (tmp,section,t-section);
+ strcpy (tmp+(t-section),".0");
+ }
+ else { /* IMAP4 body part */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) section;
+ }
+ if (strstr (section,".MIME") || strstr (section,".TEXT")) noextend = "4";
+ if (first || last) nopartial = "4";
+ if (lines) nolines = "4";
+ }
+
+ /* IMAP2bis did not have:
+ * . HEADER body part (can simulate peeking top-level with RFC822.HEADER)
+ * . TEXT body part (can simulate non-peeking top-level with RFC822.TEXT)
+ * . MIME body part
+ * . partial fetching
+ * . selective header line fetching
+ * . non-peeking header fetching
+ * . peeking body fetching
+ */
+ /* IMAP2bis compatibility */
+ else if (LEVELIMAP2bis (stream)) {
+ /* BODY[HEADER] becomes RFC822.HEADER */
+ if (!strcmp (section,"HEADER")) {
+ aatt.text = (void *)
+ ((flags & FT_PREFETCHTEXT) ?
+ "(RFC822.HEADER RFC822.TEXT)" : "RFC822.HEADER");
+ if (flags & FT_PEEK) flags &= ~FT_PEEK;
+ else nononpeek = "2bis";
+ }
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT")) aatt.text = (void *) "RFC822.TEXT";
+ /* BODY[] becomes RFC822 */
+ else if (!section[0]) aatt.text = (void *) "RFC822";
+ else { /* IMAP2bis body part */
+ aatt.type = BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) section;
+ }
+ if (strstr (section,".HEADER") || strstr (section,".MIME") ||
+ strstr (section,".TEXT")) noextend = "2bis";
+ if (first || last) nopartial = "2bis";
+ if (lines) nolines = "2bis";
+ if (flags & FT_PEEK) nopeek = "2bis";
+ }
+
+ /* IMAP2 did not have:
+ * . HEADER body part (can simulate peeking top-level with RFC822.HEADER)
+ * . TEXT body part (can simulate non-peeking top-level with RFC822.TEXT)
+ * . MIME body part
+ * . multiple body parts (can simulate BODY[1] with RFC822.TEXT)
+ * . partial fetching
+ * . selective header line fetching
+ * . non-peeking header fetching
+ * . peeking body fetching
+ */
+ else { /* IMAP2 (RFC 1176/1064) compatibility */
+ /* BODY[HEADER] */
+ if (!strcmp (section,"HEADER")) {
+ aatt.text = (void *) ((flags & FT_PREFETCHTEXT) ?
+ "(RFC822.HEADER RFC822.TEXT)" : "RFC822.HEADER");
+ if (flags & FT_PEEK) flags &= ~FT_PEEK;
+ nononpeek = "2";
+ }
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT")) aatt.text = (void *) "RFC822.TEXT";
+ /* BODY[1] treated like RFC822.TEXT */
+ else if (!strcmp (section,"1")) {
+ SIZEDTEXT text;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* have a cached RFC822.TEXT? */
+ if (elt->private.msg.text.text.data) {
+ text.size = elt->private.msg.text.text.size;
+ /* should move instead of copy */
+ text.data = memcpy (fs_get (text.size+1),
+ elt->private.msg.text.text.data,text.size);
+ (t = (char *) text.data)[text.size] = '\0';
+ imap_cache (stream,msgno,"1",NIL,&text);
+ return LONGT; /* don't have to do any fetches */
+ }
+ /* otherwise do RFC822.TEXT */
+ aatt.text = (void *) "RFC822.TEXT";
+ }
+ /* BODY[] becomes RFC822 */
+ else if (!section[0]) aatt.text = (void *) "RFC822";
+ else noextend = "2"; /* how did we get here? */
+ if (flags & FT_PEEK) nopeek = "2";
+ if (first || last) nopartial = "2";
+ if (lines) nolines = "2";
+ }
+
+ /* Report unavailable functionalities. The application can use the helpful
+ * LEVELIMAPREV1, LEVELIMAP4, and LEVELIMAP2bis operations provided in
+ * imap4r1.h to avoid triggering these errors. There aren't any workarounds
+ * for these restrictions.
+ */
+ if (noextend) {
+ sprintf (tmp,"[NOTIMAP4REV1] IMAP%s server can't do extended body fetch",
+ noextend);
+ mm_log (tmp,ERROR);
+ return NIL; /* can't do anything close either */
+ }
+ if (nopartial) {
+ sprintf (tmp,"[NOTIMAP4REV1] IMAP%s server can't do partial fetch",
+ nopartial);
+ mm_notify (stream,tmp,WARN);
+ }
+ if (nolines) {
+ sprintf(tmp,"[NOTIMAP4REV1] IMAP%s server can't do selective header fetch",
+ nolines);
+ mm_notify (stream,tmp,WARN);
+ }
+
+ /* trying to do unsupported peek behavior? */
+ if ((t = nopeek) || (t = nononpeek)) {
+ /* get most recent \Seen setting */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,auxargs)))
+ mm_log (reply->text,WARN);
+ /* note current setting of \Seen flag */
+ if (!(i = mail_elt (stream,msgno)->seen)) {
+ sprintf (tmp,nopeek ? /* only babble if \Seen not set */
+ "[NOTIMAP4] Simulating peeking fetch in IMAP%s" :
+ "[NOTIMAP4] Simulating non-peeking header fetch in IMAP%s",t);
+ mm_notify (stream,tmp,NIL);
+ }
+ /* send the fetch command */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
+ mm_log (reply->text,ERROR);
+ return NIL; /* failure */
+ }
+ /* send command if need to reset \Seen */
+ if (((nopeek && !i && mail_elt (stream,msgno)->seen &&
+ (aflg.text = "-FLAGS \\Seen")) ||
+ ((nononpeek && !mail_elt (stream,msgno)->seen) &&
+ (aflg.text = "+FLAGS \\Seen"))) &&
+ !imap_OK (stream,reply = imap_send (stream,"STORE",auxargs)))
+ mm_log (reply->text,WARN);
+ }
+ /* simple case if traditional behavior */
+ else if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
+ mm_log (reply->text,ERROR);
+ return NIL; /* failure */
+ }
+ /* simulate BODY[1] return for RFC 1064/1176 */
+ if (!LEVELIMAP2bis (stream) && !strcmp (section,"1")) {
+ SIZEDTEXT text;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ text.size = elt->private.msg.text.text.size;
+ /* should move instead of copy */
+ text.data = memcpy (fs_get (text.size+1),elt->private.msg.text.text.data,
+ text.size);
+ (t = (char *) text.data)[text.size] = '\0';
+ imap_cache (stream,msgno,"1",NIL,&text);
+ }
+ return LONGT;
+}
+
+/* IMAP fetch UID
+ * Accepts: MAIL stream
+ * message number
+ * Returns: UID
+ */
+
+unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ char *s,seq[MAILTMPLEN];
+ unsigned long i,j,k;
+ /* IMAP2 didn't have UIDs */
+ if (!LEVELIMAP4 (stream)) return msgno;
+ /* do we know its UID yet? */
+ if (!(elt = mail_elt (stream,msgno))->private.uid) {
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = (void *) "UID";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ sprintf (seq,"%lu",msgno);
+ if (k = imap_uidlookahead) {/* build UID list */
+ for (i = msgno + 1, s = seq; k && (i <= stream->nmsgs); i++)
+ if (!mail_elt (stream,i)->private.uid) {
+ s += strlen (s); /* find string end, see if nearing end */
+ if ((s - seq) > (MAILTMPLEN - 20)) break;
+ sprintf (s,",%lu",i); /* append message */
+ for (j = i + 1, k--; /* hunt for last message without a UID */
+ k && (j <= stream->nmsgs) && !mail_elt (stream,j)->private.uid;
+ j++, k--);
+ /* if different, make a range */
+ if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
+ }
+ }
+ /* send "FETCH msgno UID" */
+ if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
+ mm_log (reply->text,ERROR);
+ }
+ return elt->private.uid; /* return our UID now */
+}
+
+/* IMAP fetch message number from UID
+ * Accepts: MAIL stream
+ * UID
+ * Returns: message number
+ */
+
+unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid)
+{
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ char seq[MAILTMPLEN];
+ int holes = 0;
+ unsigned long i,msgno;
+ /* IMAP2 didn't have UIDs */
+ if (!LEVELIMAP4 (stream)) return uid;
+ /* This really should be a binary search, but since there are likely to be
+ * holes in the msgno->UID map it's hard to do.
+ */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ if (!(i = mail_elt (stream,msgno)->private.uid)) holes = T;
+ else if (i == uid) return msgno;
+ }
+ if (holes) { /* have holes in cache? */
+ /* yes, have server hunt for UID */
+ LOCAL->lastuid.uid = LOCAL->lastuid.msgno = 0;
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = (void *) "UID";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ sprintf (seq,"%lu",uid);
+ /* send "UID FETCH uid UID" */
+ if (!imap_OK (stream,reply = imap_send (stream,"UID FETCH",args)))
+ mm_log (reply->text,ERROR);
+ if (LOCAL->lastuid.uid) { /* got any results from FETCH? */
+ if ((LOCAL->lastuid.uid == uid) &&
+ /* what, me paranoid? */
+ (LOCAL->lastuid.msgno <= stream->nmsgs) &&
+ (mail_elt (stream,LOCAL->lastuid.msgno)->private.uid == uid))
+ /* got it the easy way */
+ return LOCAL->lastuid.msgno;
+ /* sigh, do another linear search... */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
+ }
+ }
+ return 0; /* didn't find the UID anywhere */
+}
+
+/* IMAP modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ char *cmd = (LEVELIMAP4 (stream) && (flags & ST_UID)) ? "UID STORE":"STORE";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[4],aseq,ascm,aflg;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & ST_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ ascm.type = ATOM; ascm.text = (void *)
+ ((flags & ST_SET) ?
+ ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
+ "+Flags.silent" : "+Flags") :
+ ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
+ "-Flags.silent" : "-Flags"));
+ aflg.type = FLAGS; aflg.text = (void *) flag;
+ args[0] = &aseq; args[1] = &ascm; args[2] = &aflg; args[3] = NIL;
+ /* send "STORE sequence +Flags flag" */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
+ mm_log (reply->text,ERROR);
+}
+
+/* IMAP search for messages
+ * Accepts: MAIL stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
+{
+ unsigned long i,j,k;
+ char *s;
+ IMAPPARSEDREPLY *reply;
+ MESSAGECACHE *elt;
+ if ((flags & SE_NOSERVER) || /* if want to do local search */
+ LOCAL->loser || /* or loser */
+ (!LEVELIMAP4 (stream) && /* or old server but new functions... */
+ (charset || (flags & SE_UID) || pgm->msgno || pgm->uid || pgm->or ||
+ pgm->not || pgm->header || pgm->larger || pgm->smaller ||
+ pgm->sentbefore || pgm->senton || pgm->sentsince || pgm->draft ||
+ pgm->undraft || pgm->return_path || pgm->sender || pgm->reply_to ||
+ pgm->message_id || pgm->in_reply_to || pgm->newsgroups ||
+ pgm->followup_to || pgm->references)) ||
+ (!LEVELWITHIN (stream) && (pgm->older || pgm->younger))) {
+ if ((flags & SE_NOLOCAL) ||
+ !mail_search_default (stream,charset,pgm,flags | SE_NOSERVER))
+ return NIL;
+ }
+ /* do silly ALL or seq-only search locally */
+ else if (!(flags & (SE_NOLOCAL|SE_SILLYOK)) &&
+ !(pgm->uid || pgm->or || pgm->not ||
+ pgm->header || pgm->from || pgm->to || pgm->cc || pgm->bcc ||
+ pgm->subject || pgm->body || pgm->text ||
+ pgm->larger || pgm->smaller ||
+ pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->before || pgm->on || pgm->since ||
+ pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted || pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged || pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword ||
+ pgm->return_path || pgm->sender ||
+ pgm->reply_to || pgm->in_reply_to || pgm->message_id ||
+ pgm->newsgroups || pgm->followup_to || pgm->references)) {
+ if (!mail_search_default (stream,NIL,pgm,flags | SE_NOSERVER))
+ fatal ("impossible mail_search_default() failure");
+ }
+
+ else { /* do server-based SEARCH */
+ char *cmd = (flags & SE_UID) ? "UID SEARCH" : "SEARCH";
+ IMAPARG *args[4],apgm,aatt,achs;
+ SEARCHSET *ss,*set;
+ args[1] = args[2] = args[3] = NIL;
+ apgm.type = SEARCHPROGRAM; apgm.text = (void *) pgm;
+ if (charset) { /* optional charset argument requested */
+ args[0] = &aatt; args[1] = &achs; args[2] = &apgm;
+ aatt.type = ATOM; aatt.text = (void *) "CHARSET";
+ achs.type = ASTRING; achs.text = (void *) charset;
+ }
+ else args[0] = &apgm; /* no charset argument */
+ /* tell receiver that these will be UIDs */
+ LOCAL->uidsearch = (flags & SE_UID) ? T : NIL;
+ reply = imap_send (stream,cmd,args);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && pgm && (ss = pgm->msgno) &&
+ !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SEARCH results */
+ for (i = 1; i <= stream->nmsgs; i++)
+ mail_elt (stream,i)->private.filter = NIL;
+ for (set = ss; set; set = set->next) if (i = set->first) {
+ /* single message becomes one-message range */
+ if (!(j = set->last)) j = i;
+ else if (j < i) { /* swap reversed range */
+ i = set->last; j = set->first;
+ }
+ while (i <= j) mail_elt (stream,i++)->private.filter = T;
+ }
+ pgm->msgno = NIL; /* and without the searchset */
+ reply = imap_send (stream,cmd,args);
+ pgm->msgno = ss; /* restore searchset */
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ LOCAL->uidsearch = NIL;
+ /* do locally if server won't grok */
+ if (!strcmp (reply->key,"BAD")) {
+ if ((flags & SE_NOLOCAL) ||
+ !mail_search_default (stream,charset,pgm,flags | SE_NOSERVER))
+ return NIL;
+ }
+ else if (!imap_OK (stream,reply)) {
+ mm_log (reply->text,ERROR);
+ return NIL;
+ }
+ }
+
+ /* can never pre-fetch with a short cache */
+ if ((k = imap_prefetch) && !(flags & (SE_NOPREFETCH | SE_UID)) &&
+ !stream->scache) { /* only if prefetching permitted */
+ s = LOCAL->tmp; /* build sequence in temporary buffer */
+ *s = '\0'; /* initially nothing */
+ /* search through mailbox */
+ for (i = 1; k && (i <= stream->nmsgs); ++i)
+ /* for searched messages with no envelope */
+ if ((elt = mail_elt (stream,i)) && elt->searched &&
+ !mail_elt (stream,i)->private.msg.env) {
+ /* prepend with comma if not first time */
+ if (LOCAL->tmp[0]) *s++ = ',';
+ sprintf (s,"%lu",j = i);/* output message number */
+ s += strlen (s); /* point at end of string */
+ k--; /* count one up */
+ /* search for possible end of range */
+ while (k && (i < stream->nmsgs) &&
+ (elt = mail_elt (stream,i+1))->searched &&
+ !elt->private.msg.env) i++,k--;
+ if (i != j) { /* if a range */
+ sprintf (s,":%lu",i); /* output delimiter and end of range */
+ s += strlen (s); /* point at end of string */
+ }
+ if ((s - LOCAL->tmp) > (IMAPTMPLEN - 50)) break;
+ }
+ if (LOCAL->tmp[0]) { /* anything to pre-fetch? */
+ /* pre-fetch envelopes for the first imap_prefetch number of messages */
+ if (!imap_OK (stream,reply =
+ imap_fetch (stream,s = cpystr (LOCAL->tmp),FT_NEEDENV +
+ ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL) +
+ ((flags & SE_NEEDBODY) ? FT_NEEDBODY : NIL))))
+ mm_log (reply->text,ERROR);
+ fs_give ((void **) &s); /* flush copy of sequence */
+ }
+ }
+ return LONGT;
+}
+
+/* IMAP sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i,j,start,last;
+ unsigned long *ret = NIL;
+ pgm->nmsgs = 0; /* start off with no messages */
+ /* can use server-based sort? */
+ if (LEVELSORT (stream) && !(flags & SE_NOSERVER) &&
+ (!spg || (LEVELWITHIN (stream) || !(spg->older || spg->younger)))) {
+ char *cmd = (flags & SE_UID) ? "UID SORT" : "SORT";
+ IMAPARG *args[4],apgm,achs,aspg;
+ IMAPPARSEDREPLY *reply;
+ SEARCHSET *ss = NIL;
+ SEARCHPGM *tsp = NIL;
+ apgm.type = SORTPROGRAM; apgm.text = (void *) pgm;
+ achs.type = ASTRING; achs.text = (void *) (charset ? charset : "US-ASCII");
+ aspg.type = SEARCHPROGRAM;
+ /* did he provide a searchpgm? */
+ if (!(aspg.text = (void *) spg)) {
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ if (ss) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) ss->last = last;
+ (ss = ss->next = mail_newsearchset ())->first = i;
+ start = last = i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new searchpgm */
+ (tsp = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
+ ss->first = start = last = i;
+ }
+ }
+ /* nothing to sort if no messages */
+ if (!(aspg.text = (void *) tsp)) return NIL;
+ /* else install last sequence */
+ if (last != start) ss->last = last;
+ }
+
+ args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
+ /* ask server to do it */
+ reply = imap_send (stream,cmd,args);
+ if (tsp) { /* was there a temporary searchpgm? */
+ aspg.text = NIL; /* yes, flush it */
+ mail_free_searchpgm (&tsp);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SORT/THREAD results */
+ reply = imap_send (stream,cmd,args);
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ }
+ /* do locally if server barfs */
+ if (!strcmp (reply->key,"BAD"))
+ return (flags & SE_NOLOCAL) ? NIL :
+ imap_sort (stream,charset,spg,pgm,flags | SE_NOSERVER);
+ /* server sorted OK? */
+ else if (imap_OK (stream,reply)) {
+ pgm->nmsgs = LOCAL->sortsize;
+ ret = LOCAL->sortdata;
+ LOCAL->sortdata = NIL; /* mail program is responsible for flushing */
+ }
+ else mm_log (reply->text,ERROR);
+ }
+
+ /* not much can do if short caching */
+ else if (stream->scache) ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ else { /* try to be a bit more clever */
+ char *s,*t;
+ unsigned long len;
+ MESSAGECACHE *elt;
+ SORTCACHE **sc;
+ SORTPGM *sp;
+ long ftflags = 0;
+ /* see if need envelopes */
+ for (sp = pgm; sp && !ftflags; sp = sp->next) switch (sp->function) {
+ case SORTDATE: case SORTFROM: case SORTSUBJECT: case SORTTO: case SORTCC:
+ ftflags = FT_NEEDENV + ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL);
+ }
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,flags & SE_NOSERVER);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->searched) {
+ pgm->nmsgs++;
+ if (ftflags ? !elt->private.msg.env : !elt->day) {
+ if (s) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) sprintf (t,":%lu,%lu",last,i);
+ else sprintf (t,",%lu",i);
+ start = last = i; /* begin a new range */
+ if ((len - (j = ((t += strlen (t)) - s)) < 20)) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + j; /* relocate current pointer */
+ }
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = i);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ }
+ /* last sequence */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* load cache for all messages being sorted */
+ imap_fetch (stream,s,ftflags);
+ fs_give ((void **) &s);
+ }
+ if (pgm->nmsgs) { /* pass 2: sort cache */
+ sortresults_t sr = (sortresults_t)
+ mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ sc = mail_sort_loadcache (stream,pgm);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,ret,pgm->nmsgs);
+ }
+ }
+ return ret;
+}
+
+/* IMAP thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADER *thr;
+ if (!(flags & SE_NOSERVER) &&
+ (!spg || (LEVELWITHIN (stream) || !(spg->older || spg->younger))))
+ /* does server have this threader type? */
+ for (thr = LOCAL->cap.threader; thr; thr = thr->next)
+ if (!compare_cstring (thr->name,type))
+ return imap_thread_work (stream,type,charset,spg,flags);
+ /* server doesn't support it, do locally */
+ return (flags & SE_NOLOCAL) ? NIL:
+ mail_thread_msgs (stream,type,charset,spg,flags | SE_NOSERVER,imap_sort);
+}
+
+/* IMAP thread messages worker routine
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree
+ */
+
+THREADNODE *imap_thread_work (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ unsigned long i,start,last;
+ char *cmd = (flags & SE_UID) ? "UID THREAD" : "THREAD";
+ IMAPARG *args[4],apgm,achs,aspg;
+ IMAPPARSEDREPLY *reply;
+ THREADNODE *ret = NIL;
+ SEARCHSET *ss = NIL;
+ SEARCHPGM *tsp = NIL;
+ apgm.type = ATOM; apgm.text = (void *) type;
+ achs.type = ASTRING;
+ achs.text = (void *) (charset ? charset : "US-ASCII");
+ aspg.type = SEARCHPROGRAM;
+ /* did he provide a searchpgm? */
+ if (!(aspg.text = (void *) spg)) {
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ if (ss) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) ss->last = last;
+ (ss = ss->next = mail_newsearchset ())->first = i;
+ start = last =i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new searchpgm */
+ (tsp = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
+ ss->first = start = last = i;
+ }
+ }
+ /* nothing to sort if no messages */
+ if (!(aspg.text = (void *) tsp)) return NIL;
+ /* else install last sequence */
+ if (last != start) ss->last = last;
+ }
+
+ args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
+ /* ask server to do it */
+ reply = imap_send (stream,cmd,args);
+ if (tsp) { /* was there a temporary searchpgm? */
+ aspg.text = NIL; /* yes, flush it */
+ mail_free_searchpgm (&tsp);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SORT/THREAD results */
+ reply = imap_send (stream,cmd,args);
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ }
+ /* do locally if server barfs */
+ if (!strcmp (reply->key,"BAD"))
+ ret = (flags & SE_NOLOCAL) ? NIL:
+ mail_thread_msgs (stream,type,charset,spg,flags | SE_NOSERVER,imap_sort);
+ /* server threaded OK? */
+ else if (imap_OK (stream,reply)) {
+ ret = LOCAL->threaddata;
+ LOCAL->threaddata = NIL; /* mail program is responsible for flushing */
+ }
+ else mm_log (reply->text,ERROR);
+ return ret;
+}
+
+/* IMAP ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, else NIL
+ */
+
+long imap_ping (MAILSTREAM *stream)
+{
+ return (LOCAL->netstream && /* send "NOOP" */
+ imap_OK (stream,imap_send (stream,"NOOP",NIL))) ? T : NIL;
+}
+
+
+/* IMAP check mailbox
+ * Accepts: MAIL stream
+ */
+
+void imap_check (MAILSTREAM *stream)
+{
+ /* send "CHECK" */
+ IMAPPARSEDREPLY *reply = imap_send (stream,"CHECK",NIL);
+ mm_log (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
+}
+
+/* IMAP expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long imap_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = NIL;
+ IMAPPARSEDREPLY *reply = NIL;
+ if (sequence) { /* wants selective expunging? */
+ if (options & EX_UID) { /* UID EXPUNGE form? */
+ if (LEVELUIDPLUS (stream)) {/* server support UIDPLUS? */
+ IMAPARG *args[2],aseq;
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ args[0] = &aseq; args[1] = NIL;
+ ret = imap_OK (stream,reply = imap_send (stream,"UID EXPUNGE",args));
+ }
+ else mm_log ("[NOTUIDPLUS] Can't do UID EXPUNGE with this server",ERROR);
+ }
+ /* otherwise try to make into UID EXPUNGE */
+ else if (mail_sequence (stream,sequence)) {
+ unsigned long i,j;
+ char *t = (char *) fs_get (IMAPTMPLEN);
+ char *s = t;
+ /* search through mailbox */
+ for (*s = '\0', i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->sequence) {
+ if (t[0]) *s++ = ','; /* prepend with comma if not first time */
+ sprintf (s,"%lu",mail_uid (stream,j = i));
+ s += strlen (s); /* point at end of string */
+ /* search for possible end of range */
+ while ((i < stream->nmsgs) && mail_elt (stream,i+1)->sequence) i++;
+ if (i != j) { /* output end of range */
+ sprintf (s,":%lu",mail_uid (stream,i));
+ s += strlen (s); /* point at end of string */
+ }
+ if ((s - t) > (IMAPTMPLEN - 50)) {
+ mm_log ("Excessively complex sequence",ERROR);
+ return NIL;
+ }
+ }
+ /* now do as UID EXPUNGE */
+ ret = imap_expunge (stream,t,EX_UID);
+ fs_give ((void **) &t);
+ }
+ }
+ /* ordinary EXPUNGE */
+ else ret = imap_OK (stream,reply = imap_send (stream,"EXPUNGE",NIL));
+ if (reply) mm_log (reply->text,ret ? (long) NIL : ERROR);
+ return ret;
+}
+
+/* IMAP copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if successful else NIL
+ */
+
+long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long flags)
+{
+ char *cmd = (LEVELIMAP4 (stream) && (flags & CP_UID)) ? "UID COPY" : "COPY";
+ char *s;
+ long ret = NIL;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,ambx;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & CP_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &aseq; args[1] = &ambx; args[2] = NIL;
+ /* note mailbox in case APPENDUID */
+ LOCAL->appendmailbox = mailbox;
+ /* send "COPY sequence mailbox" */
+ ret = imap_OK (stream,reply = imap_send (stream,cmd,args));
+ LOCAL->appendmailbox = NIL; /* no longer appending */
+ if (ret) { /* success, delete messages if move */
+ if (flags & CP_MOVE) imap_flag (stream,sequence,"\\Deleted",
+ ST_SET + ((flags&CP_UID) ? ST_UID : NIL));
+ }
+ /* failed, do referral action if any */
+ else if (ir && pc && LOCAL->referral && mail_sequence (stream,sequence) &&
+ (s = (*ir) (stream,LOCAL->referral,REFCOPY)))
+ ret = (*pc) (stream,sequence,s,flags | (stream->debug ? CP_DEBUG : NIL));
+ /* otherwise issue error message */
+ else mm_log (reply->text,ERROR);
+ return ret;
+}
+
+/* IMAP mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long imap_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MAILSTREAM *st = stream;
+ IMAPARG *args[3],ambx,amap;
+ IMAPPARSEDREPLY *reply = NIL;
+ APPENDDATA map;
+ char tmp[MAILTMPLEN];
+ long debug = stream ? stream->debug : NIL;
+ long ret = NIL;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ /* mailbox must be good */
+ if (mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
+ /* create a stream if given one no good */
+ if ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
+ (debug ? OP_DEBUG : NIL)))) {
+ /* note mailbox in case APPENDUID */
+ LOCAL->appendmailbox = mailbox;
+ /* use multi-append? */
+ if (LEVELMULTIAPPEND (stream)) {
+ ambx.type = ASTRING; ambx.text = (void *) tmp;
+ amap.type = MULTIAPPEND; amap.text = (void *) &map;
+ map.af = af; map.data = data;
+ args[0] = &ambx; args[1] = &amap; args[2] = NIL;
+ /* success if OK */
+ ret = imap_OK (stream,reply = imap_send (stream,"APPEND",args));
+ LOCAL->appendmailbox = NIL;
+ }
+ /* do succession of single appends */
+ else while ((*af) (stream,data,&map.flags,&map.date,&map.message) &&
+ map.message &&
+ (ret = imap_OK (stream,reply =
+ imap_append_single (stream,tmp,map.flags,
+ map.date,map.message))));
+ LOCAL->appendmailbox = NIL;
+ /* don't do referrals if success or no reply */
+ if (ret || !reply) mailbox = NIL;
+ /* otherwise generate referral */
+ else if (!(mailbox = (ir && LOCAL->referral) ?
+ (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
+ mm_log (reply->text,ERROR);
+ /* close temporary stream */
+ if (st != stream) stream = mail_close (stream);
+ if (mailbox) /* chase referral if any */
+ ret = imap_append_referral (mailbox,tmp,af,data,map.flags,map.date,
+ map.message,&map,debug);
+ }
+ else mm_log ("Can't access server for append",ERROR);
+ }
+ return ret; /* return */
+}
+
+/* IMAP mail append message referral retry
+ * Accepts: destination mailbox
+ * temporary buffer
+ * append callback
+ * data for callback
+ * flags from previous attempt
+ * date from previous attempt
+ * message stringstruct from previous attempt
+ * options (currently non-zero to set OP_DEBUG)
+ * Returns: T if append successful, else NIL
+ */
+
+long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
+ char *flags,char *date,STRING *message,
+ APPENDDATA *map,long options)
+{
+ MAILSTREAM *stream;
+ IMAPARG *args[3],ambx,amap;
+ IMAPPARSEDREPLY *reply;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (NIL,GET_IMAPREFERRAL,NIL);
+ /* barf if bad mailbox */
+ while (mailbox && mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
+ /* create a stream if given one no good */
+ if (!(stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
+ (options ? OP_DEBUG : NIL)))) {
+ sprintf (tmp,"Can't access referral server: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* got referral server, use multi-append? */
+ if (LEVELMULTIAPPEND (stream)) {
+ ambx.type = ASTRING; ambx.text = (void *) tmp;
+ amap.type = MULTIAPPENDREDO; amap.text = (void *) map;
+ args[0] = &ambx; args[1] = &amap; args[2] = NIL;
+ /* do multiappend on referral site */
+ if (imap_OK (stream,reply = imap_send (stream,"APPEND",args))) {
+ mail_close (stream); /* multiappend OK, close stream */
+ return LONGT; /* all done */
+ }
+ }
+ /* do multiple single appends */
+ else while (imap_OK (stream,reply =
+ imap_append_single (stream,tmp,flags,date,message)))
+ if (!((*af) (stream,data,&flags,&date,&message) && message)) {
+ mail_close (stream); /* last message, close stream */
+ return LONGT; /* all done */
+ }
+ /* generate error if no nested referral */
+ if (!(mailbox = (ir && LOCAL->referral) ?
+ (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
+ mm_log (reply->text,ERROR);
+ mail_close (stream); /* close previous referral stream */
+ }
+ return NIL; /* bogus mailbox */
+}
+
+/* IMAP append single message
+ * Accepts: mail stream
+ * destination mailbox
+ * initial flags
+ * internal date
+ * stringstruct of message to append
+ * Returns: reply from append
+ */
+
+IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
+ char *flags,char *date,STRING *message)
+{
+ MESSAGECACHE elt;
+ IMAPARG *args[5],ambx,aflg,adat,amsg;
+ IMAPPARSEDREPLY *reply;
+ char tmp[MAILTMPLEN];
+ int i;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[i = 0] = &ambx;
+ if (flags) {
+ aflg.type = FLAGS; aflg.text = (void *) flags;
+ args[++i] = &aflg;
+ }
+ if (date) { /* ensure date in INTERNALDATE format */
+ if (!mail_parse_date (&elt,date)) {
+ /* flush previous reply */
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* build new fake reply */
+ LOCAL->reply.tag = LOCAL->reply.line = cpystr ("*");
+ LOCAL->reply.key = "BAD";
+ LOCAL->reply.text = "Bad date in append";
+ return &LOCAL->reply;
+ }
+ adat.type = ASTRING;
+ adat.text = (void *) (date = mail_date (tmp,&elt));
+ args[++i] = &adat;
+ }
+ amsg.type = LITERAL; amsg.text = (void *) message;
+ args[++i] = &amsg;
+ args[++i] = NIL;
+ /* easy if IMAP4[rev1] */
+ if (LEVELIMAP4 (stream)) reply = imap_send (stream,"APPEND",args);
+ else { /* try the IMAP2bis way */
+ args[1] = &amsg; args[2] = NIL;
+ reply = imap_send (stream,"APPEND",args);
+ }
+ return reply;
+}
+
+/* IMAP garbage collect stream
+ * Accepts: Mail stream
+ * garbage collection flags
+ */
+
+void imap_gc (MAILSTREAM *stream,long gcflags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ /* make sure the cache is large enough */
+ (*mc) (stream,stream->nmsgs,CH_SIZE);
+ if (gcflags & GC_TEXTS) { /* garbage collect texts? */
+ if (!stream->scache) for (i = 1; i <= stream->nmsgs; ++i)
+ if (elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))
+ imap_gc_body (elt->private.msg.body);
+ imap_gc_body (stream->body);
+ }
+ /* gc cache if requested and unlocked */
+ if (gcflags & GC_ELT) for (i = 1; i <= stream->nmsgs; ++i)
+ if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) &&
+ (elt->lockcount == 1)) (*mc) (stream,i,CH_FREE);
+}
+
+/* IMAP garbage collect body texts
+ * Accepts: body to GC
+ */
+
+void imap_gc_body (BODY *body)
+{
+ PART *part;
+ if (body) { /* have a body? */
+ if (body->mime.text.data) /* flush MIME data */
+ fs_give ((void **) &body->mime.text.data);
+ /* flush text contents */
+ if (body->contents.text.data)
+ fs_give ((void **) &body->contents.text.data);
+ body->mime.text.size = body->contents.text.size = 0;
+ /* multipart? */
+ if (body->type == TYPEMULTIPART)
+ for (part = body->nested.part; part; part = part->next)
+ imap_gc_body (&part->body);
+ /* MESSAGE/RFC822? */
+ else if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822")) {
+ imap_gc_body (body->nested.msg->body);
+ if (body->nested.msg->full.text.data)
+ fs_give ((void **) &body->nested.msg->full.text.data);
+ if (body->nested.msg->header.text.data)
+ fs_give ((void **) &body->nested.msg->header.text.data);
+ if (body->nested.msg->text.text.data)
+ fs_give ((void **) &body->nested.msg->text.text.data);
+ body->nested.msg->full.text.size = body->nested.msg->header.text.size =
+ body->nested.msg->text.text.size = 0;
+ }
+ }
+}
+
+/* IMAP get capabilities
+ * Accepts: mail stream
+ */
+
+void imap_capability (MAILSTREAM *stream)
+{
+ THREADER *thr,*t;
+ LOCAL->gotcapability = NIL; /* flush any previous capabilities */
+ /* request new capabilities */
+ imap_send (stream,"CAPABILITY",NIL);
+ if (!LOCAL->gotcapability) { /* did server get any? */
+ /* no, flush threaders just in case */
+ if (thr = LOCAL->cap.threader) while (t = thr) {
+ fs_give ((void **) &t->name);
+ thr = t->next;
+ fs_give ((void **) &t);
+ }
+ /* zap most capabilities */
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ /* assume IMAP2bis server if failure */
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ }
+}
+
+/* IMAP set ACL
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * new access rights
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_setacl (MAILSTREAM *stream,char *mailbox,char *id,char *rights)
+{
+ IMAPARG *args[4],ambx,aid,art;
+ ambx.type = aid.type = art.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ art.text = (void *) rights;
+ args[0] = &ambx; args[1] = &aid; args[2] = &art; args[3] = NIL;
+ return imap_acl_work (stream,"SETACL",args);
+}
+
+
+/* IMAP delete ACL
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_deleteacl (MAILSTREAM *stream,char *mailbox,char *id)
+{
+ IMAPARG *args[3],ambx,aid;
+ ambx.type = aid.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ args[0] = &ambx; args[1] = &aid; args[2] = NIL;
+ return imap_acl_work (stream,"DELETEACL",args);
+}
+
+
+/* IMAP get ACL
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getacl (MAILSTREAM *stream,char *mailbox)
+{
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ return imap_acl_work (stream,"GETACL",args);
+}
+
+/* IMAP list rights
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_listrights (MAILSTREAM *stream,char *mailbox,char *id)
+{
+ IMAPARG *args[3],ambx,aid;
+ ambx.type = aid.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ args[0] = &ambx; args[1] = &aid; args[2] = NIL;
+ return imap_acl_work (stream,"LISTRIGHTS",args);
+}
+
+
+/* IMAP my rights
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_myrights (MAILSTREAM *stream,char *mailbox)
+{
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ return imap_acl_work (stream,"MYRIGHTS",args);
+}
+
+
+/* IMAP ACL worker routine
+ * Accepts: mail stream
+ * command
+ * command arguments
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_acl_work (MAILSTREAM *stream,char *command,IMAPARG *args[])
+{
+ long ret = NIL;
+ if (LEVELACL (stream)) { /* send command */
+ IMAPPARSEDREPLY *reply;
+ if (imap_OK (stream,reply = imap_send (stream,command,args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("ACL not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* IMAP set quota
+ * Accepts: mail stream
+ * quota root name
+ * resource limit list as a stringlist
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "SETQUOTA" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aqrt,alim;
+ aqrt.type = ASTRING; aqrt.text = (void *) qroot;
+ alim.type = SNLIST; alim.text = (void *) limits;
+ args[0] = &aqrt; args[1] = &alim; args[2] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"SETQUOTA",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* IMAP get quota
+ * Accepts: mail stream
+ * quota root name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getquota (MAILSTREAM *stream,char *qroot)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "GETQUOTA" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[2],aqrt;
+ aqrt.type = ASTRING; aqrt.text = (void *) qroot;
+ args[0] = &aqrt; args[1] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"GETQUOTA",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+
+/* IMAP get quota root
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getquotaroot (MAILSTREAM *stream,char *mailbox)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "GETQUOTAROOT" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"GETQUOTAROOT",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* IMAP send command
+ * Accepts: MAIL stream
+ * command
+ * argument list
+ * Returns: parsed reply
+ */
+
+#define CMDBASE LOCAL->tmp /* command base */
+
+IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[])
+{
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *arg,**arglst;
+ SORTPGM *spg;
+ STRINGLIST *list;
+ SIZEDTEXT st;
+ APPENDDATA *map;
+ sendcommand_t sc = (sendcommand_t) mail_parameters (NIL,GET_SENDCOMMAND,NIL);
+ size_t i;
+ void *a;
+ char c,*s,*t,tag[10];
+ stream->unhealthy = NIL; /* make stream healthy again */
+ /* gensym a new tag */
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ if (!LOCAL->netstream) /* make sure have a session */
+ return imap_fake (stream,tag,"[CLOSED] IMAP connection lost");
+ mail_lock (stream); /* lock up the stream */
+ if (sc) /* tell client sending a command */
+ (*sc) (stream,cmd,((compare_cstring (cmd,"FETCH") &&
+ compare_cstring (cmd,"STORE") &&
+ compare_cstring (cmd,"SEARCH")) ?
+ NIL : SC_EXPUNGEDEFERRED));
+ /* ignore referral from previous command */
+ if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
+ sprintf (CMDBASE,"%s %s",tag,cmd);
+ s = CMDBASE + strlen (CMDBASE);
+ if (arglst = args) while (arg = *arglst++) {
+ *s++ = ' '; /* delimit argument with space */
+ switch (arg->type) {
+ case ATOM: /* atom */
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case NUMBER: /* number */
+ sprintf (s,"%lu",(unsigned long) arg->text);
+ s += strlen (s);
+ break;
+ case FLAGS: /* flag list as a single string */
+ if (*(t = (char *) arg->text) != '(') {
+ *s++ = '('; /* wrap parens around string */
+ while (*t) *s++ = *t++;
+ *s++ = ')'; /* wrap parens around string */
+ }
+ else while (*t) *s++ = *t++;
+ break;
+ case ASTRING: /* atom or string, must be literal? */
+ st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+ if (reply = imap_send_astring (stream,tag,&s,&st,NIL,CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+ case LITERAL: /* literal, as a stringstruct */
+ if (reply = imap_send_literal (stream,tag,&s,arg->text)) return reply;
+ break;
+
+ case LIST: /* list of strings */
+ list = (STRINGLIST *) arg->text;
+ c = '('; /* open paren */
+ do { /* for each list item */
+ *s++ = c; /* write prefix character */
+ if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL,
+ CMDBASE+MAXCOMMAND)) return reply;
+ c = ' '; /* prefix character for subsequent strings */
+ }
+ while (list = list->next);
+ *s++ = ')'; /* close list */
+ break;
+ case SEARCHPROGRAM: /* search program */
+ if (reply = imap_send_spgm (stream,tag,CMDBASE,&s,arg->text,
+ CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+ case SORTPROGRAM: /* search program */
+ c = '('; /* open paren */
+ for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
+ *s++ = c; /* write prefix */
+ if (spg->reverse) for (t = "REVERSE "; *t; *s++ = *t++);
+ switch (spg->function) {
+ case SORTDATE:
+ for (t = "DATE"; *t; *s++ = *t++);
+ break;
+ case SORTARRIVAL:
+ for (t = "ARRIVAL"; *t; *s++ = *t++);
+ break;
+ case SORTFROM:
+ for (t = "FROM"; *t; *s++ = *t++);
+ break;
+ case SORTSUBJECT:
+ for (t = "SUBJECT"; *t; *s++ = *t++);
+ break;
+ case SORTTO:
+ for (t = "TO"; *t; *s++ = *t++);
+ break;
+ case SORTCC:
+ for (t = "CC"; *t; *s++ = *t++);
+ break;
+ case SORTSIZE:
+ for (t = "SIZE"; *t; *s++ = *t++);
+ break;
+ default:
+ fatal ("Unknown sort program function in imap_send()!");
+ }
+ c = ' '; /* prefix character for subsequent items */
+ }
+ *s++ = ')'; /* close list */
+ break;
+
+ case BODYTEXT: /* body section */
+ for (t = "BODY["; *t; *s++ = *t++);
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case BODYPEEK: /* body section */
+ for (t = "BODY.PEEK["; *t; *s++ = *t++);
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case BODYCLOSE: /* close bracket and possible length */
+ s[-1] = ']'; /* no leading space */
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case SEQUENCE: /* sequence */
+ if ((i = strlen (t = (char *) arg->text)) <= (size_t) MAXCOMMAND)
+ while (*t) *s++ = *t++; /* easy case */
+ else {
+ mail_unlock (stream); /* unlock stream */
+ a = arg->text; /* save original sequence pointer */
+ arg->type = ATOM; /* make recursive call be faster */
+ do { /* break up into multiple commands */
+ if (i <= MAXCOMMAND) {/* final part? */
+ reply = imap_send (stream,cmd,args);
+ i = 0; /* and mark as done */
+ }
+ else { /* still needs to be split further */
+ if (!(t = strchr (t + MAXCOMMAND - 30,',')) ||
+ ((t - (char *) arg->text) > MAXCOMMAND))
+ fatal ("impossible over-long sequence");
+ *t = '\0'; /* tie off sequence at point of split*/
+ /* recurse to do this part */
+ reply = imap_send (stream,cmd,args);
+ *t++ = ','; /* restore the comma in case something cares */
+ /* punt if error */
+ if (!imap_OK (stream,reply)) break;
+ /* calculate size of remaining sequence */
+ i -= (t - (char *) arg->text);
+ /* point to new remaining sequence */
+ arg->text = (void *) t;
+ }
+ } while (i);
+ arg->type = SEQUENCE; /* restore in case something cares */
+ arg->text = a;
+ return reply; /* return result */
+ }
+ break;
+ case LISTMAILBOX: /* astring with wildcards */
+ st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+ if (reply = imap_send_astring (stream,tag,&s,&st,T,CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+
+ case MULTIAPPEND: /* append multiple messages */
+ /* get package pointer */
+ map = (APPENDDATA *) arg->text;
+ if (!(*map->af) (stream,map->data,&map->flags,&map->date,&map->message)||
+ !map->message) {
+ STRING es;
+ INIT (&es,mail_string,"",0);
+ return (reply = imap_send_literal (stream,tag,&s,&es)) ?
+ reply : imap_fake (stream,tag,"Server zero-length literal error");
+ }
+ case MULTIAPPENDREDO: /* redo multiappend */
+ /* get package pointer */
+ map = (APPENDDATA *) arg->text;
+ do { /* make sure date valid if given */
+ char datetmp[MAILTMPLEN];
+ MESSAGECACHE elt;
+ STRING es;
+ if (!map->date || mail_parse_date (&elt,map->date)) {
+ if (t = map->flags) { /* flags given? */
+ if (*t != '(') {
+ *s++ = '('; /* wrap parens around string */
+ while (*t) *s++ = *t++;
+ *s++ = ')'; /* wrap parens around string */
+ }
+ else while (*t) *s++ = *t++;
+ *s++ = ' '; /* delimit with space */
+ }
+ if (map->date) { /* date given? */
+ st.size = strlen ((char *) (st.data = (unsigned char *)
+ mail_date (datetmp,&elt)));
+ if (reply = imap_send_astring (stream,tag,&s,&st,NIL,
+ CMDBASE+MAXCOMMAND)) return reply;
+ *s++ = ' '; /* delimit with space */
+ }
+ if (reply = imap_send_literal (stream,tag,&s,map->message))
+ return reply;
+ /* get next message */
+ if ((*map->af) (stream,map->data,&map->flags,&map->date,
+ &map->message)) {
+ /* have a message, delete next in command */
+ if (map->message) *s++ = ' ';
+ continue; /* loop back for next message */
+ }
+ }
+ /* bad date or need to abort */
+ INIT (&es,mail_string,"",0);
+ return (reply = imap_send_literal (stream,tag,&s,&es)) ?
+ reply : imap_fake (stream,tag,"Server zero-length literal error");
+ break; /* exit the loop */
+ } while (map->message);
+ break;
+
+ case SNLIST: /* list of string/number pairs */
+ list = (STRINGLIST *) arg->text;
+ c = '('; /* open paren */
+ do { /* for each list item */
+ *s++ = c; /* write prefix character */
+ if (list) { /* sigh, QUOTA has bizarre syntax! */
+ for (t = (char *) list->text.data; *t; *s++ = *t++);
+ sprintf (s," %lu",list->text.size);
+ s += strlen (s);
+ c = ' '; /* prefix character for subsequent strings */
+ }
+ }
+ while (list = list->next);
+ *s++ = ')'; /* close list */
+ break;
+ default:
+ fatal ("Unknown argument type in imap_send()!");
+ }
+ }
+ /* send the command */
+ reply = imap_sout (stream,tag,CMDBASE,&s);
+ mail_unlock (stream); /* unlock stream */
+ return reply;
+}
+
+/* IMAP send atom-string
+ * Accepts: MAIL stream
+ * reply tag
+ * pointer to current position pointer of output bigbuf
+ * atom-string to output
+ * flag if list_wildcards allowed
+ * maximum to write as atom or qstring
+ * Returns: error reply or NIL if success
+ */
+
+IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
+ SIZEDTEXT *as,long wildok,char *limit)
+{
+ unsigned long j;
+ char c;
+ STRING st;
+ /* default to atom unless empty or loser */
+ int qflag = (as->size && !LOCAL->loser) ? NIL : T;
+ /* in case needed */
+ INIT (&st,mail_string,(void *) as->data,as->size);
+ /* always write literal if no space */
+ if ((*s + as->size) > limit) return imap_send_literal (stream,tag,s,&st);
+ for (j = 0; j < as->size; j++) switch (c = as->data[j]) {
+ default: /* all other characters */
+ if (!(c & 0x80)) { /* must not be 8bit */
+ if (c <= ' ') qflag = T; /* must quote if a CTL */
+ break;
+ }
+ case '\0': /* not a CHAR */
+ case '\012': case '\015': /* not a TEXT-CHAR */
+ case '"': case '\\': /* quoted-specials (IMAP2 required this) */
+ return imap_send_literal (stream,tag,s,&st);
+ case '*': case '%': /* list_wildcards */
+ if (wildok) break; /* allowed if doing the wild thing */
+ /* atom_specials */
+ case '(': case ')': case '{': case ' ': case 0x7f:
+#if 0
+ case '"': case '\\': /* quoted-specials (could work in IMAP4) */
+#endif
+ qflag = T; /* must use quoted string format */
+ break;
+ }
+ if (qflag) *(*s)++ = '"'; /* write open quote */
+ for (j = 0; j < as->size; j++) *(*s)++ = as->data[j];
+ if (qflag) *(*s)++ = '"'; /* write close quote */
+ return NIL;
+}
+
+/* IMAP send literal
+ * Accepts: MAIL stream
+ * reply tag
+ * pointer to current position pointer of output bigbuf
+ * literal to output as stringstruct
+ * Returns: error reply or NIL if success
+ */
+
+IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
+ STRING *st)
+{
+ IMAPPARSEDREPLY *reply;
+ unsigned long i = SIZE (st);
+ unsigned long j;
+ sprintf (*s,"{%lu}",i); /* write literal count */
+ *s += strlen (*s); /* size of literal count */
+ /* send the command */
+ reply = imap_sout (stream,tag,CMDBASE,s);
+ if (strcmp (reply->tag,"+")) {/* prompt for more data? */
+ mail_unlock (stream); /* no, give up */
+ return reply;
+ }
+ while (i) { /* dump the text */
+ if (st->cursize) { /* if text to do in this chunk */
+ /* RFC 3501 technically forbids NULs in literals. Normally, the
+ * delivering MTA would take care of MIME converting the message text
+ * so that it is NUL-free. If it doesn't, then we have the choice of
+ * either violating IMAP by sending NULs, corrupting the data, or going
+ * to lots of work to do MIME conversion in the IMAP server.
+ *
+ * No current stringstruct driver objects to having its buffer patched.
+ * If this ever changes, it will be necessary to change this kludge.
+ */
+ /* patch NULs to C1 control */
+ for (j = 0; j < st->cursize; ++j)
+ if (!st->curpos[j]) st->curpos[j] = 0x80;
+ if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
+ mail_unlock (stream);
+ return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
+ }
+ i -= st->cursize; /* note that we wrote out this much */
+ st->curpos += (st->cursize - 1);
+ st->cursize = 0;
+ }
+ (*st->dtb->next) (st); /* advance to next buffer's worth */
+ }
+ return NIL; /* success */
+}
+
+/* IMAP send search program
+ * Accepts: MAIL stream
+ * reply tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * search program to output
+ * pointer to limit guideline
+ * Returns: error reply or NIL if success
+ */
+
+
+IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHPGM *pgm,char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ SEARCHHEADER *hdr;
+ SEARCHOR *pgo;
+ SEARCHPGMLIST *pgl;
+ char *t;
+ /* trim if called recursively */
+ if (base) *s = imap_send_spgm_trim (base,*s,NIL);
+ base = *s; /* this is the new base */
+ /* default searchpgm */
+ for (t = "ALL"; *t; *(*s)++ = *t++);
+ if (!pgm) return NIL; /* done if NIL searchpgm */
+ if ((pgm->msgno && /* message sequences */
+ (pgm->msgno->next || /* trim away first:last */
+ (pgm->msgno->first != 1) || (pgm->msgno->last != stream->nmsgs)) &&
+ (reply = imap_send_sset (stream,tag,base,s,pgm->msgno," ",limit))) ||
+ (pgm->uid &&
+ (reply = imap_send_sset (stream,tag,base,s,pgm->uid," UID ",limit))))
+ return reply;
+ /* message sizes */
+ if (pgm->larger) {
+ sprintf (*s," LARGER %lu",pgm->larger);
+ *s += strlen (*s);
+ }
+ if (pgm->smaller) {
+ sprintf (*s," SMALLER %lu",pgm->smaller);
+ *s += strlen (*s);
+ }
+
+ /* message flags */
+ if (pgm->answered) for (t = " ANSWERED"; *t; *(*s)++ = *t++);
+ if (pgm->unanswered) for (t =" UNANSWERED"; *t; *(*s)++ = *t++);
+ if (pgm->deleted) for (t =" DELETED"; *t; *(*s)++ = *t++);
+ if (pgm->undeleted) for (t =" UNDELETED"; *t; *(*s)++ = *t++);
+ if (pgm->draft) for (t =" DRAFT"; *t; *(*s)++ = *t++);
+ if (pgm->undraft) for (t =" UNDRAFT"; *t; *(*s)++ = *t++);
+ if (pgm->flagged) for (t =" FLAGGED"; *t; *(*s)++ = *t++);
+ if (pgm->unflagged) for (t =" UNFLAGGED"; *t; *(*s)++ = *t++);
+ if (pgm->recent) for (t =" RECENT"; *t; *(*s)++ = *t++);
+ if (pgm->old) for (t =" OLD"; *t; *(*s)++ = *t++);
+ if (pgm->seen) for (t =" SEEN"; *t; *(*s)++ = *t++);
+ if (pgm->unseen) for (t =" UNSEEN"; *t; *(*s)++ = *t++);
+ if ((pgm->keyword && /* keywords */
+ (reply = imap_send_slist (stream,tag,base,s," KEYWORD ",pgm->keyword,
+ limit))) ||
+ (pgm->unkeyword &&
+ (reply = imap_send_slist (stream,tag,base,s," UNKEYWORD ",
+ pgm->unkeyword,limit))))
+ return reply;
+ /* sent date ranges */
+ if (pgm->sentbefore) imap_send_sdate (s,"SENTBEFORE",pgm->sentbefore);
+ if (pgm->senton) imap_send_sdate (s,"SENTON",pgm->senton);
+ if (pgm->sentsince) imap_send_sdate (s,"SENTSINCE",pgm->sentsince);
+ /* internal date ranges */
+ if (pgm->before) imap_send_sdate (s,"BEFORE",pgm->before);
+ if (pgm->on) imap_send_sdate (s,"ON",pgm->on);
+ if (pgm->since) imap_send_sdate (s,"SINCE",pgm->since);
+ if (pgm->older) {
+ sprintf (*s," OLDER %lu",pgm->older);
+ *s += strlen (*s);
+ }
+ if (pgm->younger) {
+ sprintf (*s," YOUNGER %lu",pgm->younger);
+ *s += strlen (*s);
+ }
+ /* search texts */
+ if ((pgm->bcc && (reply = imap_send_slist (stream,tag,base,s," BCC ",
+ pgm->bcc,limit))) ||
+ (pgm->cc && (reply = imap_send_slist (stream,tag,base,s," CC ",pgm->cc,
+ limit))) ||
+ (pgm->from && (reply = imap_send_slist (stream,tag,base,s," FROM ",
+ pgm->from,limit))) ||
+ (pgm->to && (reply = imap_send_slist (stream,tag,base,s," TO ",pgm->to,
+ limit))))
+ return reply;
+ if ((pgm->subject && (reply = imap_send_slist (stream,tag,base,s," SUBJECT ",
+ pgm->subject,limit))) ||
+ (pgm->body && (reply = imap_send_slist (stream,tag,base,s," BODY ",
+ pgm->body,limit))) ||
+ (pgm->text && (reply = imap_send_slist (stream,tag,base,s," TEXT ",
+ pgm->text,limit))))
+ return reply;
+
+ /* Note that these criteria are not supported by IMAP and have to be
+ emulated */
+ if ((pgm->return_path &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Return-Path ",
+ pgm->return_path,limit))) ||
+ (pgm->sender &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Sender ",
+ pgm->sender,limit))) ||
+ (pgm->reply_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Reply-To ",
+ pgm->reply_to,limit))) ||
+ (pgm->in_reply_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER In-Reply-To ",
+ pgm->in_reply_to,limit))) ||
+ (pgm->message_id &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Message-ID ",
+ pgm->message_id,limit))) ||
+ (pgm->newsgroups &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Newsgroups ",
+ pgm->newsgroups,limit))) ||
+ (pgm->followup_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Followup-To ",
+ pgm->followup_to,limit))) ||
+ (pgm->references &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER References ",
+ pgm->references,limit)))) return reply;
+
+ /* all other headers */
+ if (hdr = pgm->header) do {
+ *s = imap_send_spgm_trim (base,*s," HEADER ");
+ if (reply = imap_send_astring (stream,tag,s,&hdr->line,NIL,limit))
+ return reply;
+ *(*s)++ = ' ';
+ if (reply = imap_send_astring (stream,tag,s,&hdr->text,NIL,limit))
+ return reply;
+ } while (hdr = hdr->next);
+ for (pgo = pgm->or; pgo; pgo = pgo->next) {
+ *s = imap_send_spgm_trim (base,*s," OR (");
+ if (reply = imap_send_spgm (stream,tag,base,s,pgo->first,limit))
+ return reply;
+ for (t = ") ("; *t; *(*s)++ = *t++);
+ if (reply = imap_send_spgm (stream,tag,base,s,pgo->second,limit))
+ return reply;
+ *(*s)++ = ')';
+ }
+ for (pgl = pgm->not; pgl; pgl = pgl->next) {
+ *s = imap_send_spgm_trim (base,*s," NOT (");
+ if (reply = imap_send_spgm (stream,tag,base,s,pgl->pgm,limit))
+ return reply;
+ *(*s)++ = ')';
+ }
+ /* trim if needed */
+ *s = imap_send_spgm_trim (base,*s,NIL);
+ return NIL; /* search program written OK */
+}
+
+
+/* Write new text and trim extraneous "ALL" from searchpgm
+ * Accepts: pointer to start of searchpgm or NIL
+ * current end pointer
+ * new text to write or NIL
+ * Returns: new end pointer, trimmed if needed
+ */
+
+char *imap_send_spgm_trim (char *base,char *s,char *text)
+{
+ char *t;
+ /* write new text */
+ if (text) for (t = text; *t; *s++ = *t++);
+ /* need to trim? */
+ if (base && (s > (t = (base + 4))) && (*base == 'A') && (base[1] == 'L') &&
+ (base[2] == 'L') && (base[3] == ' ')) {
+ memmove (base,t,s - t); /* yes, blat down remaining text */
+ s -= 4; /* and reduce current pointer */
+ }
+ return s; /* return new end pointer */
+}
+
+/* IMAP send search set
+ * Accepts: MAIL stream
+ * current command tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * search set to output
+ * message prefix
+ * maximum output pointer
+ * Returns: NIL if success, error reply if error
+ */
+
+IMAPPARSEDREPLY *imap_send_sset (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHSET *set,char *prefix,
+ char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ STRING st;
+ char c,*t;
+ char *start = *s;
+ /* trim and write prefix */
+ *s = imap_send_spgm_trim (base,*s,prefix);
+ /* run down search list */
+ for (c = NIL; set && (*s < limit); set = set->next, c = ',') {
+ if (c) *(*s)++ = c; /* write delimiter and first value */
+ if (set->first == 0xffffffff) *(*s)++ = '*';
+ else {
+ sprintf (*s,"%lu",set->first);
+ *s += strlen (*s);
+ }
+ /* have a second value? */
+ if (set->last && (set->first != set->last)) {
+ *(*s)++ = ':'; /* write delimiter and second value */
+ if (set->last == 0xffffffff) *(*s)++ = '*';
+ else {
+ sprintf (*s,"%lu",set->last);
+ *s += strlen (*s);
+ }
+ }
+ }
+ if (set) { /* insert "OR" in front of incomplete set */
+ memmove (start + 3,start,*s - start);
+ memcpy (start," OR",3);
+ *s += 3; /* point to end of buffer */
+ /* write glue that is equivalent to ALL */
+ for (t =" ((OR BCC FOO NOT BCC "; *t; *(*s)++ = *t++);
+ /* but broken by a literal */
+ INIT (&st,mail_string,(void *) "FOO",3);
+ if (reply = imap_send_literal (stream,tag,s,&st)) return reply;
+ *(*s)++ = ')'; /* close glue */
+ if (reply = imap_send_sset (stream,tag,NIL,s,set,prefix,limit))
+ return reply;
+ *(*s)++ = ')'; /* close second OR argument */
+ }
+ return NIL;
+}
+
+/* IMAP send search list
+ * Accepts: MAIL stream
+ * reply tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * name of search list
+ * search list to output
+ * maximum output pointer
+ * Returns: NIL if success, error reply if error
+ */
+
+IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char *base,
+ char **s,char *name,STRINGLIST *list,
+ char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ do {
+ *s = imap_send_spgm_trim (base,*s,name);
+ base = NIL; /* no longer need trimming */
+ reply = imap_send_astring (stream,tag,s,&list->text,NIL,limit);
+ }
+ while (!reply && (list = list->next));
+ return reply;
+}
+
+
+/* IMAP send search date
+ * Accepts: pointer to current position pointer of output bigbuf
+ * field name
+ * search date to output
+ */
+
+void imap_send_sdate (char **s,char *name,unsigned short date)
+{
+ sprintf (*s," %s %d-%s-%d",name,date & 0x1f,
+ months[((date >> 5) & 0xf) - 1],BASEYEAR + (date >> 9));
+ *s += strlen (*s);
+}
+
+/* IMAP send buffered command to sender
+ * Accepts: MAIL stream
+ * reply tag
+ * string
+ * pointer to string tail pointer
+ * Returns: reply
+ */
+
+IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s)
+{
+ IMAPPARSEDREPLY *reply;
+ if (stream->debug) { /* output debugging telemetry */
+ **s = '\0';
+ mail_dlog (base,LOCAL->sensitive);
+ }
+ *(*s)++ = '\015'; /* append CRLF */
+ *(*s)++ = '\012';
+ **s = '\0';
+ reply = net_sout (LOCAL->netstream,base,*s - base) ?
+ imap_reply (stream,tag) :
+ imap_fake (stream,tag,"[CLOSED] IMAP connection broken (command)");
+ *s = base; /* restart buffer */
+ return reply;
+}
+
+
+/* IMAP send null-terminated string to sender
+ * Accepts: MAIL stream
+ * string
+ * Returns: T if success, else NIL
+ */
+
+long imap_soutr (MAILSTREAM *stream,char *string)
+{
+ long ret;
+ unsigned long i;
+ char *s;
+ if (stream->debug) mm_dlog (string);
+ sprintf (s = (char *) fs_get ((i = strlen (string) + 2) + 1),
+ "%s\015\012",string);
+ ret = net_sout (LOCAL->netstream,s,i);
+ fs_give ((void **) &s);
+ return ret;
+}
+
+/* IMAP get reply
+ * Accepts: MAIL stream
+ * tag to search or NIL if want a greeting
+ * Returns: parsed reply, never NIL
+ */
+
+IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag)
+{
+ IMAPPARSEDREPLY *reply;
+ while (LOCAL->netstream) { /* parse reply from server */
+ if (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) {
+ /* continuation ready? */
+ if (!strcmp (reply->tag,"+")) return reply;
+ /* untagged data? */
+ else if (!strcmp (reply->tag,"*")) {
+ imap_parse_unsolicited (stream,reply);
+ if (!tag) return reply; /* return if just wanted greeting */
+ }
+ else { /* tagged data */
+ if (tag && !compare_cstring (tag,reply->tag)) return reply;
+ /* report bogon */
+ sprintf (LOCAL->tmp,"Unexpected tagged response: %.80s %.80s %.80s",
+ (char *) reply->tag,(char *) reply->key,(char *) reply->text);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ }
+ return imap_fake (stream,tag,
+ "[CLOSED] IMAP connection broken (server response)");
+}
+
+/* IMAP parse reply
+ * Accepts: MAIL stream
+ * text of reply
+ * Returns: parsed reply, or NIL if can't parse at least a tag and key
+ */
+
+
+IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text)
+{
+ char *r;
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* init fields in case error */
+ LOCAL->reply.key = LOCAL->reply.text = LOCAL->reply.tag = NIL;
+ if (!(LOCAL->reply.line = text)) {
+ /* NIL text means the stream died */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ return NIL;
+ }
+ if (stream->debug) mm_dlog (LOCAL->reply.line);
+ if (!(LOCAL->reply.tag = strtok_r (LOCAL->reply.line," ",&r))) {
+ mm_notify (stream,"IMAP server sent a blank line",WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ /* non-continuation replies */
+ if (strcmp (LOCAL->reply.tag,"+")) {
+ /* parse key */
+ if (!(LOCAL->reply.key = strtok_r (NIL," ",&r))) {
+ /* determine what is missing */
+ sprintf (LOCAL->tmp,"Missing IMAP reply key: %.80s",
+ (char *) LOCAL->reply.tag);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return NIL; /* can't parse this text */
+ }
+ ucase (LOCAL->reply.key); /* canonicalize key to upper */
+ /* get text as well, allow empty text */
+ if (!(LOCAL->reply.text = strtok_r (NIL,"\n",&r)))
+ LOCAL->reply.text = LOCAL->reply.key + strlen (LOCAL->reply.key);
+ }
+ else { /* special handling of continuation */
+ LOCAL->reply.key = "BAD"; /* so it barfs if not expecting continuation */
+ if (!(LOCAL->reply.text = strtok_r (NIL,"\n",&r)))
+ LOCAL->reply.text = "";
+ }
+ return &LOCAL->reply; /* return parsed reply */
+}
+
+/* IMAP fake reply when stream determined to be dead
+ * Accepts: MAIL stream
+ * tag
+ * text of fake reply (must start with "[CLOSED]")
+ * Returns: parsed reply
+ */
+
+IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text)
+{
+ mm_notify (stream,text,BYE); /* send bye alert */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL; /* farewell, dear NET stream... */
+ /* flush previous reply */
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* build new fake reply */
+ LOCAL->reply.tag = LOCAL->reply.line = cpystr (tag ? tag : "*");
+ LOCAL->reply.key = "NO";
+ LOCAL->reply.text = text;
+ return &LOCAL->reply; /* return parsed reply */
+}
+
+
+/* IMAP check for OK response in tagged reply
+ * Accepts: MAIL stream
+ * parsed reply
+ * Returns: T if OK else NIL
+ */
+
+long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
+{
+ long ret = NIL;
+ /* OK - operation succeeded */
+ if (!strcmp (reply->key,"OK")) {
+ imap_parse_response (stream,reply->text,NIL,NIL);
+ ret = T;
+ }
+ /* NO - operation failed */
+ else if (!strcmp (reply->key,"NO"))
+ imap_parse_response (stream,reply->text,WARN,NIL);
+ else { /* BAD - operation rejected */
+ if (!strcmp (reply->key,"BAD")) {
+ imap_parse_response (stream,reply->text,ERROR,NIL);
+ sprintf (LOCAL->tmp,"IMAP protocol error: %.80s",(char *) reply->text);
+ }
+ /* bad protocol received */
+ else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
+ (char *) reply->key,(char *) reply->text);
+ mm_log (LOCAL->tmp,ERROR); /* either way, this is not good */
+ }
+ return ret;
+}
+
+/* IMAP parse and act upon unsolicited reply
+ * Accepts: MAIL stream
+ * parsed reply
+ */
+
+void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
+{
+ unsigned long i = 0;
+ unsigned long j,msgno;
+ unsigned char *s,*t;
+ char *r;
+ /* see if key is a number */
+ if (isdigit (*reply->key)) {
+ msgno = strtoul (reply->key,(char **) &s,10);
+ if (*s) { /* better be nothing after number */
+ sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",
+ (char *) reply->key);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return;
+ }
+ if (!reply->text) { /* better be some data */
+ mm_notify (stream,"Missing message data",WARN);
+ stream->unhealthy = T;
+ return;
+ }
+ /* get message data type, canonicalize upper */
+ s = ucase (strtok_r (reply->text," ",&r));
+ /* and locate the text after it */
+ t = strtok_r (NIL,"\n",&r);
+ /* now take the action */
+ /* change in size of mailbox */
+ if (!strcmp (s,"EXISTS") && (msgno >= stream->nmsgs))
+ mail_exists (stream,msgno);
+ else if (!strcmp (s,"RECENT") && (msgno <= stream->nmsgs))
+ mail_recent (stream,msgno);
+ else if (!strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ MESSAGECACHE *elt = (MESSAGECACHE *) (*mc) (stream,msgno,CH_ELT);
+ if (elt) imap_gc_body (elt->private.msg.body);
+ /* notify upper level */
+ mail_expunged (stream,msgno);
+ }
+
+ else if ((!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
+ msgno && (msgno <= stream->nmsgs)) {
+ char *prop;
+ GETS_DATA md;
+ ENVELOPE **e;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ ENVELOPE *env = NIL;
+ imapenvelope_t ie =
+ (imapenvelope_t) mail_parameters (stream,GET_IMAPENVELOPE,NIL);
+ ++t; /* skip past open parenthesis */
+ /* parse Lisp-form property list */
+ while (prop = (strtok_r (t," )",&r))) {
+ t = strtok_r (NIL,"\n",&r);
+ INIT_GETS (md,stream,elt->msgno,NIL,0,0);
+ e = NIL; /* not pointing at any envelope yet */
+ /* canonicalize property, parse it */
+ if (!strcmp (ucase (prop),"FLAGS")) imap_parse_flags (stream,elt,&t);
+ else if (!strcmp (prop,"INTERNALDATE") &&
+ (s = imap_parse_string (stream,&t,reply,NIL,NIL,LONGT))) {
+ if (!mail_parse_date (elt,s)) {
+ sprintf (LOCAL->tmp,"Bogus date: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* slam in default so we don't try again */
+ mail_parse_date (elt,"01-Jan-1970 00:00:00 +0000");
+ }
+ fs_give ((void **) &s);
+ }
+ /* unique identifier */
+ else if (!strcmp (prop,"UID")) {
+ LOCAL->lastuid.uid = elt->private.uid = strtoul (t,(char **) &t,10);
+ LOCAL->lastuid.msgno = elt->msgno;
+ }
+ else if (!strcmp (prop,"ENVELOPE")) {
+ if (stream->scache) { /* short cache, flush old stuff */
+ mail_free_body (&stream->body);
+ stream->msgno = elt->msgno;
+ e = &stream->env; /* get pointer to envelope */
+ }
+ else e = &elt->private.msg.env;
+ imap_parse_envelope (stream,e,&t,reply);
+ }
+ else if (!strncmp (prop,"BODY",4)) {
+ if (!prop[4] || !strcmp (prop+4,"STRUCTURE")) {
+ BODY **body;
+ if (stream->scache){/* short cache, flush old stuff */
+ if (stream->msgno != msgno) {
+ mail_free_envelope (&stream->env);
+ sprintf (LOCAL->tmp,"Body received for %lu but current is %lu",
+ msgno,stream->msgno);
+ stream->msgno = msgno;
+ }
+ /* get pointer to body */
+ body = &stream->body;
+ }
+ else body = &elt->private.msg.body;
+ /* flush any prior body */
+ mail_free_body (body);
+ /* instantiate and parse a new body */
+ imap_parse_body_structure (stream,*body = mail_newbody(),&t,reply);
+ }
+
+ else if (prop[4] == '[') {
+ STRINGLIST *stl = NIL;
+ SIZEDTEXT text;
+ /* will want to return envelope data */
+ if (!strcmp (md.what = cpystr (prop + 5),"HEADER]") ||
+ !strcmp (md.what,"0]"))
+ e = stream->scache ? &stream->env : &elt->private.msg.env;
+ LOCAL->tmp[0] ='\0';/* no errors yet */
+ /* found end of section? */
+ if (!(s = strchr (md.what,']'))) {
+ /* skip leading nesting */
+ for (s = md.what; *s && (isdigit (*s) || (*s == '.')); s++);
+ /* better be one of these */
+ if (strncmp (s,"HEADER.FIELDS",13) &&
+ (!s[13] || strcmp (s+13,".NOT")))
+ sprintf (LOCAL->tmp,"Unterminated section: %.80s",md.what);
+ /* get list of headers */
+ else if (!(stl = imap_parse_stringlist (stream,&t,reply)))
+ sprintf (LOCAL->tmp,"Bogus header field list: %.80s",
+ (char *) t);
+ else if (*t != ']')
+ sprintf (LOCAL->tmp,"Unterminated header section: %.80s",
+ (char *) t);
+ /* point after the text */
+ else if (t = strchr (s = t,' ')) *t++ = '\0';
+ }
+ if (s && !LOCAL->tmp[0]) {
+ *s++ = '\0'; /* tie off section specifier */
+ if (*s == '<') { /* partial specifier? */
+ md.first = strtoul (s+1,(char **) &s,10) + 1;
+ if (*s++ != '>') /* make sure properly terminated */
+ sprintf (LOCAL->tmp,"Unterminated partial data: %.80s",
+ (char *) s-1);
+ }
+ if (!LOCAL->tmp[0] && *s)
+ sprintf (LOCAL->tmp,"Junk after section: %.80s",(char *) s);
+ }
+ if (LOCAL->tmp[0]) { /* got any errors? */
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_stringlist (&stl);
+ }
+ else { /* parse text from server */
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,
+ ((md.what[0] && (md.what[0] != 'H')) ||
+ md.first || md.last) ? &md : NIL,
+ &text.size,NIL);
+ /* all done if partial */
+ if (md.first || md.last) mail_free_stringlist (&stl);
+ /* otherwise register it in the cache */
+ else imap_cache (stream,msgno,md.what,stl,&text);
+ }
+ fs_give ((void **) &md.what);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown body message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ /* one of the RFC822 props? */
+ else if (!strncmp (prop,"RFC822",6) && (!prop[6] || (prop[6] == '.'))){
+ SIZEDTEXT text;
+ if (!prop[6]) { /* cache full message */
+ md.what = "";
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+ imap_cache (stream,msgno,md.what,NIL,&text);
+ }
+ else if (!strcmp (prop+7,"SIZE"))
+ elt->rfc822_size = strtoul (t,(char **) &t,10);
+ /* legacy properties */
+ else if (!strcmp (prop+7,"HEADER")) {
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,NIL,&text.size,NIL);
+ imap_cache (stream,msgno,"HEADER",NIL,&text);
+ e = stream->scache ? &stream->env : &elt->private.msg.env;
+ }
+ else if (!strcmp (prop+7,"TEXT")) {
+ md.what = "TEXT";
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+ imap_cache (stream,msgno,md.what,NIL,&text);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown RFC822 message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ if (e && *e) env = *e; /* note envelope if we got one */
+ }
+ /* do callback if requested */
+ if (ie && env) (*ie) (stream,msgno,env);
+ }
+ /* obsolete response to COPY */
+ else if (strcmp (s,"COPY")) {
+ sprintf (LOCAL->tmp,"Unknown message data: %lu %.80s",msgno,(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ else if (!strcmp (reply->key,"FLAGS") && reply->text &&
+ (*reply->text == '(') &&
+ (s = strtok_r (reply->text+1," )",&r)))
+ do if (*s != '\\') {
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i] &&
+ compare_cstring (s,stream->user_flags[i]); i++);
+ if (i > NUSERFLAGS) {
+ sprintf (LOCAL->tmp,"Too many server flags, discarding: %.80s",
+ (char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ }
+ else if (!stream->user_flags[i]) stream->user_flags[i++] = cpystr (s);
+ }
+ while (s = strtok_r (NIL," )",&r));
+ else if (!strcmp (reply->key,"SEARCH")) {
+ /* only do something if have text */
+ if (reply->text && (t = strtok_r (reply->text," ",&r))) do
+ if (i = strtoul (t,NIL,10)) {
+ /* UIDs always passed to main program */
+ if (LOCAL->uidsearch) mm_searched (stream,i);
+ /* should be a msgno then */
+ else if ((i <= stream->nmsgs) &&
+ (!LOCAL->filter || mail_elt (stream,i)->private.filter)) {
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ } while (t = strtok_r (NIL," ",&r));
+ }
+ else if (!strcmp (reply->key,"SORT")) {
+ sortresults_t sr = (sortresults_t)
+ mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ LOCAL->sortsize = 0; /* initialize sort data */
+ if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
+ LOCAL->sortdata = (unsigned long *)
+ fs_get ((stream->nmsgs + 1) * sizeof (unsigned long));
+ /* only do something if have text */
+ if (reply->text && (t = strtok_r (reply->text," ",&r))) {
+ do if ((i = atol (t)) && (LOCAL->filter ?
+ mail_elt (stream,i)->searched : T))
+ LOCAL->sortdata[LOCAL->sortsize++] = i;
+ while ((t = strtok_r (NIL," ",&r)) && (LOCAL->sortsize < stream->nmsgs));
+ }
+ LOCAL->sortdata[LOCAL->sortsize] = 0;
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,LOCAL->sortdata,LOCAL->sortsize);
+ }
+ else if (!strcmp (reply->key,"THREAD")) {
+ threadresults_t tr = (threadresults_t)
+ mail_parameters (NIL,GET_THREADRESULTS,NIL);
+ if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
+ if (s = reply->text) {
+ LOCAL->threaddata = imap_parse_thread (stream,&s);
+ if (tr) (*tr) (stream,LOCAL->threaddata);
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk at end of thread: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ }
+
+ else if (!strcmp (reply->key,"STATUS") && reply->text) {
+ MAILSTATUS status;
+ unsigned char *txt = reply->text;
+ if ((t = imap_parse_astring (stream,&txt,reply,&j)) && txt &&
+ (*txt++ == ' ') && (*txt++ == '(') && (s = strchr (txt,')')) &&
+ (s - txt) && !s[1]) {
+ *s = '\0'; /* tie off status data */
+ /* initialize data block */
+ status.flags = status.messages = status.recent = status.unseen =
+ status.uidnext = status.uidvalidity = 0;
+ while (*txt && (s = strchr (txt,' '))) {
+ *s++ = '\0'; /* tie off status attribute name */
+ /* get attribute value */
+ i = strtoul (s,(char **) &s,10);
+ if (!compare_cstring (txt,"MESSAGES")) {
+ status.flags |= SA_MESSAGES;
+ status.messages = i;
+ }
+ else if (!compare_cstring (txt,"RECENT")) {
+ status.flags |= SA_RECENT;
+ status.recent = i;
+ }
+ else if (!compare_cstring (txt,"UNSEEN")) {
+ status.flags |= SA_UNSEEN;
+ status.unseen = i;
+ }
+ else if (!compare_cstring (txt,"UIDNEXT")) {
+ status.flags |= SA_UIDNEXT;
+ status.uidnext = i;
+ }
+ else if (!compare_cstring (txt,"UIDVALIDITY")) {
+ status.flags |= SA_UIDVALIDITY;
+ status.uidvalidity = i;
+ }
+ /* next attribute */
+ txt = (*s == ' ') ? s + 1 : s;
+ }
+ if (((i = 1 + strchr (stream->mailbox,'}') - stream->mailbox) + j) <
+ IMAPTMPLEN) {
+ strcpy (strncpy (LOCAL->tmp,stream->mailbox,i) + i,t);
+ /* pass status to main program */
+ mm_status (stream,LOCAL->tmp,&status);
+ }
+ }
+ if (t) fs_give ((void **) &t);
+ }
+
+ else if ((!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
+ reply->text && (*reply->text == '(') &&
+ (s = strchr (reply->text,')')) && (s[1] == ' ')) {
+ char delimiter = '\0';
+ *s++ = '\0'; /* tie off attribute list */
+ /* parse attribute list */
+ if (t = strtok_r (reply->text+1," ",&r)) do {
+ if (!compare_cstring (t,"\\NoInferiors")) i |= LATT_NOINFERIORS;
+ else if (!compare_cstring (t,"\\NoSelect")) i |= LATT_NOSELECT;
+ else if (!compare_cstring (t,"\\Marked")) i |= LATT_MARKED;
+ else if (!compare_cstring (t,"\\Unmarked")) i |= LATT_UNMARKED;
+ else if (!compare_cstring (t,"\\HasChildren")) i |= LATT_HASCHILDREN;
+ else if (!compare_cstring (t,"\\HasNoChildren")) i |= LATT_HASNOCHILDREN;
+ /* ignore extension flags */
+ }
+ while (t = strtok_r (NIL," ",&r));
+ switch (*++s) { /* process delimiter */
+ case 'N': /* NIL */
+ case 'n':
+ s += 4; /* skip over NIL<space> */
+ break;
+ case '"': /* have a delimiter */
+ delimiter = (*++s == '\\') ? *++s : *s;
+ s += 3; /* skip over <delimiter><quote><space> */
+ }
+ /* parse the mailbox name */
+ if (t = imap_parse_astring (stream,&s,reply,&j)) {
+ /* prepend prefix if requested */
+ if (LOCAL->prefix && ((strlen (LOCAL->prefix) + j) < IMAPTMPLEN))
+ sprintf (s = LOCAL->tmp,"%s%s",LOCAL->prefix,(char *) t);
+ else s = t; /* otherwise just mailbox name */
+ /* pass data to main program */
+ if (reply->key[1] == 'S') mm_lsub (stream,delimiter,s,i);
+ else mm_list (stream,delimiter,s,i);
+ fs_give ((void **) &t); /* flush mailbox name */
+ }
+ }
+ else if (!strcmp (reply->key,"NAMESPACE")) {
+ if (LOCAL->namespace) {
+ mail_free_namespace (&LOCAL->namespace[0]);
+ mail_free_namespace (&LOCAL->namespace[1]);
+ mail_free_namespace (&LOCAL->namespace[2]);
+ }
+ else LOCAL->namespace = (NAMESPACE **) fs_get (3 * sizeof (NAMESPACE *));
+ if (s = reply->text) { /* parse namespace results */
+ LOCAL->namespace[0] = imap_parse_namespace (stream,&s,reply);
+ LOCAL->namespace[1] = imap_parse_namespace (stream,&s,reply);
+ LOCAL->namespace[2] = imap_parse_namespace (stream,&s,reply);
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk after namespace list: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ else {
+ mm_notify (stream,"Missing namespace list",WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ else if (!strcmp (reply->key,"ACL") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ getacl_t ar = (getacl_t) mail_parameters (NIL,GET_ACL,NIL);
+ if (s && (*s++ == ' ')) {
+ ACLLIST *al = mail_newacllist ();
+ ACLLIST *ac = al;
+ do if ((ac->identifier = imap_parse_astring (stream,&s,reply,NIL)) &&
+ s && (*s++ == ' '))
+ ac->rights = imap_parse_astring (stream,&s,reply,NIL);
+ while (ac->rights && s && (*s == ' ') && s++ &&
+ (ac = ac->next = mail_newacllist ()));
+ if (!ac->rights || (s && *s)) {
+ sprintf (LOCAL->tmp,"Invalid ACL identifer/rights for %.80s",
+ (char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (ar) (*ar) (stream,t,al);
+ mail_free_acllist (&al); /* clean up */
+ }
+ /* no optional rights */
+ else if (ar) (*ar) (stream,t,NIL);
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ else if (!strcmp (reply->key,"LISTRIGHTS") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ listrights_t lr = (listrights_t) mail_parameters (NIL,GET_LISTRIGHTS,NIL);
+ char *id,*r;
+ if (s && (*s++ == ' ') && (id = imap_parse_astring (stream,&s,reply,NIL))){
+ if (s && (*s++ == ' ') &&
+ (r = imap_parse_astring (stream,&s,reply,NIL))) {
+ if (s && (*s++ == ' ')) {
+ STRINGLIST *rl = mail_newstringlist ();
+ STRINGLIST *rc = rl;
+ do rc->text.data = (unsigned char *)
+ imap_parse_astring (stream,&s,reply,&rc->text.size);
+ while (rc->text.data && s && (*s == ' ') && s++ &&
+ (rc = rc->next = mail_newstringlist ()));
+ if (!rc->text.data || (s && *s)) {
+ sprintf (LOCAL->tmp,"Invalid optional LISTRIGHTS for %.80s",
+ (char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (lr) (*lr) (stream,t,id,r,rl);
+ /* clean up */
+ mail_free_stringlist (&rl);
+ }
+ /* no optional rights */
+ else if (lr) (*lr) (stream,t,id,r,NIL);
+ fs_give ((void **) &r); /* free rights */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing LISTRIGHTS rights for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &id); /* free identifier */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing LISTRIGHTS identifer for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ else if (!strcmp (reply->key,"MYRIGHTS") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ myrights_t mr = (myrights_t) mail_parameters (NIL,GET_MYRIGHTS,NIL);
+ char *r;
+ if (s && (*s++ == ' ') && (r = imap_parse_astring (stream,&s,reply,NIL))) {
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk after MYRIGHTS for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (mr) (*mr) (stream,t,r);
+ fs_give ((void **) &r); /* free rights */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing MYRIGHTS for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ /* this response has a bizarre syntax! */
+ else if (!strcmp (reply->key,"QUOTA") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ /* in case error */
+ sprintf (LOCAL->tmp,"Bad quota resource list for %.80s",(char *) t);
+ if (s && (*s++ == ' ') && (*s++ == '(') && *s && ((*s != ')') || !s[1])) {
+ quota_t qt = (quota_t) mail_parameters (NIL,GET_QUOTA,NIL);
+ QUOTALIST *ql = NIL;
+ QUOTALIST *qc;
+ /* parse non-empty quota resource list */
+ if (*s != ')') for (ql = qc = mail_newquotalist (); T;
+ qc = qc->next = mail_newquotalist ()) {
+ if ((qc->name = imap_parse_astring (stream,&s,reply,NIL)) && s &&
+ (*s++ == ' ') && (isdigit (*s) || (LOCAL->loser && (*s == '-')))) {
+ if (isdigit (*s)) qc->usage = strtoul (s,(char **) &s,10);
+ else if (t = strchr (s,' ')) t = s;
+ if ((*s++ == ' ') && (isdigit (*s) || (LOCAL->loser &&(*s == '-')))){
+ if (isdigit (*s)) qc->limit = strtoul (s,(char **) &s,10);
+ else if (t = strpbrk (s," )")) t = s;
+ /* another resource follows? */
+ if (*s == ' ') continue;
+ /* end of resource list? */
+ if ((*s == ')') && !s[1]) {
+ if (qt) (*qt) (stream,t,ql);
+ break; /* all done */
+ }
+ }
+ }
+ /* something bad happened */
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break; /* parse failed */
+ }
+ /* all done with quota resource list now */
+ if (ql) mail_free_quotalist (&ql);
+ }
+ else {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free root name */
+ }
+ else if (!strcmp (reply->key,"QUOTAROOT") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ sprintf (LOCAL->tmp,"Bad quota root list for %.80s",(char *) t);
+ if (s && (*s++ == ' ')) {
+ quotaroot_t qr = (quotaroot_t) mail_parameters (NIL,GET_QUOTAROOT,NIL);
+ STRINGLIST *rl = mail_newstringlist ();
+ STRINGLIST *rc = rl;
+ do rc->text.data = (unsigned char *)
+ imap_parse_astring (stream,&s,reply,&rc->text.size);
+ while (rc->text.data && *s && (*s++ == ' ') &&
+ (rc = rc->next = mail_newstringlist ()));
+ if (!rc->text.data || (s && *s)) {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (qr) (*qr) (stream,t,rl);
+ /* clean up */
+ mail_free_stringlist (&rl);
+ }
+ else {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t);
+ }
+
+ else if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH"))
+ imap_parse_response (stream,reply->text,NIL,T);
+ else if (!strcmp (reply->key,"NO"))
+ imap_parse_response (stream,reply->text,WARN,T);
+ else if (!strcmp (reply->key,"BAD"))
+ imap_parse_response (stream,reply->text,ERROR,T);
+ else if (!strcmp (reply->key,"BYE")) {
+ LOCAL->byeseen = T; /* note that a BYE seen */
+ imap_parse_response (stream,reply->text,BYE,T);
+ }
+ else if (!strcmp (reply->key,"CAPABILITY") && reply->text)
+ imap_parse_capabilities (stream,reply->text);
+ else if (!strcmp (reply->key,"MAILBOX") && reply->text) {
+ if (LOCAL->prefix &&
+ ((strlen (LOCAL->prefix) + strlen (reply->text)) < IMAPTMPLEN))
+ sprintf (t = LOCAL->tmp,"%s%s",LOCAL->prefix,(char *) reply->text);
+ else t = reply->text;
+ mm_list (stream,NIL,t,NIL);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",
+ (char *) reply->key);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+}
+
+/* Parse human-readable response text
+ * Accepts: mail stream
+ * text
+ * error level for mm_notify()
+ * non-NIL if want mm_notify() event even if no response code
+ */
+
+void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy)
+{
+ char *s,*t,*r;
+ size_t i;
+ unsigned long j;
+ MESSAGECACHE *elt;
+ copyuid_t cu;
+ appenduid_t au;
+ SEARCHSET *source = NIL;
+ SEARCHSET *dest = NIL;
+ if (text && (*text == '[') && (t = strchr (s = text + 1,']')) &&
+ ((i = t - s) < IMAPTMPLEN)) {
+ LOCAL->tmp[i] = '\0'; /* make mungable copy of text code */
+ if (s = strchr (strncpy (t = LOCAL->tmp,s,i),' ')) *s++ = '\0';
+ if (s) { /* have argument? */
+ ntfy = NIL; /* suppress mm_notify if normal SELECT data */
+ if (!compare_cstring (t,"UIDVALIDITY") &&
+ ((j = strtoul (s,NIL,10)) != stream->uid_validity)) {
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ stream->uid_validity = j;
+ /* purge any UIDs in cache */
+ for (j = 1; j <= stream->nmsgs; j++)
+ if (elt = (MESSAGECACHE *) (*mc) (stream,j,CH_ELT))
+ elt->private.uid = 0;
+ }
+ else if (!compare_cstring (t,"UIDNEXT"))
+ stream->uid_last = strtoul (s,NIL,10) - 1;
+ else if (!compare_cstring (t,"PERMANENTFLAGS") && (*s == '(') &&
+ (t[i-1] == ')')) {
+ t[i-1] = '\0'; /* tie off flags */
+ stream->perm_seen = stream->perm_deleted = stream->perm_answered =
+ stream->perm_draft = stream->kwd_create = NIL;
+ stream->perm_user_flags = NIL;
+ if (s = strtok_r (s+1," ",&r)) do {
+ if (*s == '\\') { /* system flags */
+ if (!compare_cstring (s,"\\Seen")) stream->perm_seen = T;
+ else if (!compare_cstring (s,"\\Deleted"))
+ stream->perm_deleted = T;
+ else if (!compare_cstring (s,"\\Flagged"))
+ stream->perm_flagged = T;
+ else if (!compare_cstring (s,"\\Answered"))
+ stream->perm_answered = T;
+ else if (!compare_cstring (s,"\\Draft")) stream->perm_draft = T;
+ else if (!strcmp (s,"\\*")) stream->kwd_create = T;
+ }
+ else stream->perm_user_flags |= imap_parse_user_flag (stream,s);
+ }
+ while (s = strtok_r (NIL," ",&r));
+ }
+
+ else if (!compare_cstring (t,"CAPABILITY"))
+ imap_parse_capabilities (stream,s);
+ else if ((j = LEVELUIDPLUS (stream) && LOCAL->appendmailbox) &&
+ !compare_cstring (t,"COPYUID") &&
+ (cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL)) &&
+ isdigit (*s) && (j = strtoul (s,&s,10)) && (*s++ == ' ') &&
+ (source = mail_parse_set (s,&s)) && (*s++ == ' ') &&
+ (dest = mail_parse_set (s,&s)) && !*s)
+ (*cu) (stream,LOCAL->appendmailbox,j,source,dest);
+ else if (j && !compare_cstring (t,"APPENDUID") &&
+ (au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL)) &&
+ isdigit (*s) && (j = strtoul (s,&s,10)) && (*s++ == ' ') &&
+ (dest = mail_parse_set (s,&s)) && !*s)
+ (*au) (LOCAL->appendmailbox,j,dest);
+ else { /* all other response code events */
+ ntfy = T; /* must mm_notify() */
+ if (!compare_cstring (t,"REFERRAL"))
+ LOCAL->referral = cpystr (t + 9);
+ }
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* no arguments */
+ if (!compare_cstring (t,"UIDNOTSTICKY")) {
+ ntfy = NIL;
+ stream->uid_nosticky = T;
+ }
+ else if (!compare_cstring (t,"READ-ONLY")) stream->rdonly = T;
+ else if (!compare_cstring (t,"READ-WRITE"))
+ stream->rdonly = NIL;
+ else if (!compare_cstring (t,"PARSE") && !errflg)
+ errflg = PARSE;
+ }
+ }
+ /* give event to main program */
+ if (ntfy && !stream->silent) mm_notify (stream,text ? text : "",errflg);
+}
+
+/* Parse a namespace
+ * Accepts: mail stream
+ * current text pointer
+ * parsed reply
+ * Returns: namespace list, text pointer updated
+ */
+
+NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ NAMESPACE *ret = NIL;
+ NAMESPACE *nam = NIL;
+ NAMESPACE *prev = NIL;
+ PARAMETER *par = NIL;
+ if (*txtptr) { /* only if argument given */
+ /* ignore leading space */
+ while (**txtptr == ' ') ++*txtptr;
+ switch (**txtptr) {
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ case '(':
+ ++*txtptr; /* skip past open paren */
+ while (**txtptr == '(') {
+ ++*txtptr; /* skip past open paren */
+ prev = nam; /* note previous if any */
+ nam = (NAMESPACE *) memset (fs_get (sizeof (NAMESPACE)),0,
+ sizeof (NAMESPACE));
+ if (!ret) ret = nam; /* if first time note first namespace */
+ /* if previous link new block to it */
+ if (prev) prev->next = nam;
+ nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL,NIL);
+ /* ignore whitespace */
+ while (**txtptr == ' ') ++*txtptr;
+ switch (**txtptr) { /* parse delimiter */
+ case 'N':
+ case 'n':
+ *txtptr += 3; /* bump past "NIL" */
+ break;
+ case '"':
+ if (*++*txtptr == '\\') nam->delimiter = *++*txtptr;
+ else nam->delimiter = **txtptr;
+ *txtptr += 2; /* bump past character and closing quote */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Missing delimiter in namespace: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ *txtptr = NIL; /* stop parse */
+ return ret;
+ }
+
+ while (**txtptr == ' '){/* append new parameter to tail */
+ if (nam->param) par = par->next = mail_newbody_parameter ();
+ else nam->param = par = mail_newbody_parameter ();
+ if (!(par->attribute = imap_parse_string (stream,txtptr,reply,NIL,
+ NIL,NIL))) {
+ mm_notify (stream,"Missing namespace extension attribute",WARN);
+ stream->unhealthy = T;
+ par->attribute = cpystr ("UNKNOWN");
+ }
+ /* skip space */
+ while (**txtptr == ' ') ++*txtptr;
+ if (**txtptr == '(') {/* have value list? */
+ char *att = par->attribute;
+ ++*txtptr; /* yes */
+ do { /* parse each value */
+ if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,
+ NIL,LONGT))) {
+ sprintf (LOCAL->tmp,
+ "Missing value for namespace attribute %.80s",att);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ /* is there another value? */
+ if (**txtptr == ' ') par = par->next = mail_newbody_parameter ();
+ } while (!par->value);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing values for namespace attribute %.80s",
+ par->attribute);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ }
+ if (**txtptr == ')') ++*txtptr;
+ else { /* missing trailing paren */
+ sprintf (LOCAL->tmp,"Junk at end of namespace: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return ret;
+ }
+ }
+ if (**txtptr == ')') { /* expected trailing paren? */
+ ++*txtptr; /* got it! */
+ break;
+ }
+ default:
+ sprintf (LOCAL->tmp,"Not a namespace: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ *txtptr = NIL; /* stop parse now */
+ break;
+ }
+ }
+ return ret;
+}
+
+/* Parse a thread node list
+ * Accepts: mail stream
+ * current text pointer
+ * Returns: thread node list, text pointer updated
+ */
+
+THREADNODE *imap_parse_thread (MAILSTREAM *stream,unsigned char **txtptr)
+{
+ char *s;
+ THREADNODE *ret = NIL; /* returned tree */
+ THREADNODE *last = NIL; /* last branch in this tree */
+ THREADNODE *parent = NIL; /* parent of current node */
+ THREADNODE *cur; /* current node */
+ while (**txtptr == '(') { /* see a thread? */
+ ++*txtptr; /* skip past open paren */
+ while (**txtptr != ')') { /* parse thread */
+ if (**txtptr == '(') { /* thread branch */
+ cur = imap_parse_thread (stream,txtptr);
+ /* add to parent */
+ if (parent) parent = parent->next = cur;
+ else { /* no parent, create dummy */
+ if (last) last = last->branch = mail_newthreadnode (NIL);
+ /* new tree */
+ else ret = last = mail_newthreadnode (NIL);
+ /* add to dummy parent */
+ last->next = parent = cur;
+ }
+ }
+ /* threaded message number */
+ else if (isdigit (*(s = *txtptr)) &&
+ ((cur = mail_newthreadnode (NIL))->num =
+ strtoul (*txtptr,(char **) txtptr,10))) {
+ if (LOCAL->filter && !mail_elt (stream,cur->num)->searched)
+ cur->num = NIL; /* make dummy if filtering and not searched */
+ /* add to parent */
+ if (parent) parent = parent->next = cur;
+ /* no parent, start new thread */
+ else if (last) last = last->branch = parent = cur;
+ /* create new tree */
+ else ret = last = parent = cur;
+ }
+ else { /* anything else is a bogon */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Bogus thread member: %.80s",s);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return ret;
+ }
+ /* skip past any space */
+ if (**txtptr == ' ') ++*txtptr;
+ }
+ ++*txtptr; /* skip pase end of thread */
+ parent = NIL; /* close this thread */
+ }
+ return ret; /* return parsed thread */
+}
+
+/* Parse RFC822 message header
+ * Accepts: MAIL stream
+ * envelope to parse into
+ * header as sized text
+ * stringlist if partial header
+ */
+
+void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
+ STRINGLIST *stl)
+{
+ ENVELOPE *nenv;
+ /* parse what we can from this header */
+ rfc822_parse_msg (&nenv,NIL,(char *) hdr->data,hdr->size,NIL,
+ net_host (LOCAL->netstream),stream->dtb->flags);
+ if (*env) { /* need to merge this header into envelope? */
+ if (!(*env)->newsgroups) { /* need Newsgroups? */
+ (*env)->newsgroups = nenv->newsgroups;
+ (*env)->ngpathexists = nenv->ngpathexists;
+ nenv->newsgroups = NIL;
+ }
+ if (!(*env)->followup_to) { /* need Followup-To? */
+ (*env)->followup_to = nenv->followup_to;
+ nenv->followup_to = NIL;
+ }
+ if (!(*env)->references) { /* need References? */
+ (*env)->references = nenv->references;
+ nenv->references = NIL;
+ }
+ if (!(*env)->sparep) { /* need spare pointer? */
+ (*env)->sparep = nenv->sparep;
+ nenv->sparep = NIL;
+ }
+ mail_free_envelope (&nenv);
+ (*env)->imapenvonly = NIL; /* have complete envelope now */
+ }
+ /* otherwise set it to this envelope */
+ else (*env = nenv)->incomplete = stl ? T : NIL;
+}
+
+/* IMAP parse envelope
+ * Accepts: MAIL stream
+ * pointer to envelope pointer
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ ENVELOPE *oenv = *env;
+ char c = *((*txtptr)++); /* grab first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *((*txtptr)++);
+ switch (c) { /* dispatch on first character */
+ case '(': /* if envelope S-expression */
+ *env = mail_newenvelope (); /* parse the new envelope */
+ (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->to = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (oenv) { /* need to merge old envelope? */
+ (*env)->newsgroups = oenv->newsgroups;
+ oenv->newsgroups = NIL;
+ (*env)->ngpathexists = oenv->ngpathexists;
+ (*env)->followup_to = oenv->followup_to;
+ oenv->followup_to = NIL;
+ (*env)->references = oenv->references;
+ oenv->references = NIL;
+ mail_free_envelope(&oenv);/* free old envelope */
+ }
+ /* have IMAP envelope components only */
+ else (*env)->imapenvonly = T;
+ if (**txtptr != ')') {
+ sprintf (LOCAL->tmp,"Junk at end of envelope: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an envelope: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+}
+
+/* IMAP parse address list
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: address list, NIL on failure
+ *
+ * Updates text pointer
+ */
+
+ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ ADDRESS *adr = NIL;
+ char c = **txtptr; /* sniff at first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *++*txtptr;
+ ++*txtptr; /* skip past open paren */
+ switch (c) {
+ case '(': /* if envelope S-expression */
+ adr = imap_parse_address (stream,txtptr,reply);
+ if (**txtptr != ')') {
+ sprintf (LOCAL->tmp,"Junk at end of address list: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ return adr;
+}
+
+/* IMAP parse address
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: address, NIL on failure
+ *
+ * Updates text pointer
+ */
+
+ADDRESS *imap_parse_address (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ long ingroup = 0;
+ ADDRESS *adr = NIL;
+ ADDRESS *ret = NIL;
+ ADDRESS *prev = NIL;
+ char c = **txtptr; /* sniff at first address character */
+ switch (c) {
+ case '(': /* if envelope S-expression */
+ while (c == '(') { /* recursion dies on small stack machines */
+ ++*txtptr; /* skip past open paren */
+ if (adr) prev = adr; /* note previous if any */
+ adr = mail_newaddr (); /* instantiate address and parse its fields */
+ adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (**txtptr != ')') { /* handle trailing paren */
+ sprintf (LOCAL->tmp,"Junk at end of address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past close paren */
+ c = **txtptr; /* set up for while test */
+ /* ignore leading spaces in front of next */
+ while (c == ' ') c = *++*txtptr;
+
+ if (!adr->mailbox) { /* end of group? */
+ /* decrement group if all looks well */
+ if (ingroup && !(adr->personal || adr->adl || adr->host)) --ingroup;
+ else {
+ if (ingroup) { /* in a group? */
+ sprintf (LOCAL->tmp,/* yes, must be bad syntax */
+ "Junk in end of group: pn=%.80s al=%.80s dn=%.80s",
+ adr->personal ? adr->personal : "",
+ adr->adl ? adr->adl : "",
+ adr->host ? adr->host : "");
+ mm_notify (stream,LOCAL->tmp,WARN);
+ }
+ else mm_notify (stream,"End of group encountered when not in group",
+ WARN);
+ stream->unhealthy = T;
+ mail_free_address (&adr);
+ adr = prev;
+ prev = NIL;
+ }
+ }
+ else if (!adr->host) { /* start of group? */
+ if (adr->personal || adr->adl) {
+ sprintf (LOCAL->tmp,"Junk in start of group: pn=%.80s al=%.80s",
+ adr->personal ? adr->personal : "",
+ adr->adl ? adr->adl : "");
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_address (&adr);
+ adr = prev;
+ prev = NIL;
+ }
+ else ++ingroup; /* in a group now */
+ }
+ if (adr) { /* good address */
+ if (!ret) ret = adr; /* if first time note first adr */
+ /* if previous link new block to it */
+ if (prev) prev->next = adr;
+ /* flush bogus personal name */
+ if (LOCAL->loser && adr->personal && strchr (adr->personal,'@'))
+ fs_give ((void **) &adr->personal);
+ }
+ }
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ *txtptr += 3; /* bump past NIL */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ return ret;
+}
+
+/* IMAP parse flags
+ * Accepts: current message cache
+ * current text pointer
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned char **txtptr)
+{
+ char *flag;
+ char c = '\0';
+ struct { /* old flags */
+ unsigned int valid : 1;
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.valid = elt->valid; old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ elt->valid = T; /* mark have valid flags now */
+ elt->user_flags = NIL; /* zap old flag values */
+ elt->seen = elt->deleted = elt->flagged = elt->answered = elt->draft =
+ elt->recent = NIL;
+ while (c != ')') { /* parse list of flags */
+ /* point at a flag */
+ while (*(flag = ++*txtptr) == ' ');
+ /* scan for end of flag */
+ while (**txtptr != ' ' && **txtptr != ')') ++*txtptr;
+ c = **txtptr; /* save delimiter */
+ **txtptr = '\0'; /* tie off flag */
+ if (!*flag) break; /* null flag */
+ /* if starts with \ must be sys flag */
+ else if (*flag == '\\') {
+ if (!compare_cstring (flag,"\\Seen")) elt->seen = T;
+ else if (!compare_cstring (flag,"\\Deleted")) elt->deleted = T;
+ else if (!compare_cstring (flag,"\\Flagged")) elt->flagged = T;
+ else if (!compare_cstring (flag,"\\Answered")) elt->answered = T;
+ else if (!compare_cstring (flag,"\\Recent")) elt->recent = T;
+ else if (!compare_cstring (flag,"\\Draft")) elt->draft = T;
+ }
+ /* otherwise user flag */
+ else elt->user_flags |= imap_parse_user_flag (stream,flag);
+ }
+ ++*txtptr; /* bump past delimiter */
+ if (!old.valid || (old.seen != elt->seen) ||
+ (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) mm_flags (stream,elt->msgno);
+}
+
+
+/* IMAP parse user flag
+ * Accepts: MAIL stream
+ * flag name
+ * Returns: flag bit position
+ */
+
+unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag)
+{
+ long i;
+ /* sniff through all user flags */
+ for (i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i])
+ if (!compare_cstring (flag,stream->user_flags[i])) return (1 << i);
+ return (unsigned long) 0; /* not found */
+}
+
+/* IMAP parse atom-string
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * returned string length
+ * Returns: string
+ *
+ * Updates text pointer
+ */
+
+unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,unsigned long *len)
+{
+ unsigned long i;
+ unsigned char c,*s,*ret;
+ /* ignore leading spaces */
+ for (c = **txtptr; c == ' '; c = *++*txtptr);
+ switch (c) {
+ case '"': /* quoted string? */
+ case '{': /* literal? */
+ ret = imap_parse_string (stream,txtptr,reply,NIL,len,NIL);
+ break;
+ default: /* must be atom */
+ for (c = *(s = *txtptr); /* find end of atom */
+ c && (c > ' ') && (c != '(') && (c != ')') && (c != '{') &&
+ (c != '%') && (c != '*') && (c != '"') && (c != '\\') && (c < 0x80);
+ c = *++*txtptr);
+ if (i = *txtptr - s) { /* atom ends at atom_special */
+ if (len) *len = i; /* return length of atom */
+ ret = strncpy ((char *) fs_get (i + 1),s,i);
+ ret[i] = '\0'; /* tie off string */
+ }
+ else { /* no atom found */
+ sprintf (LOCAL->tmp,"Not an atom: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0;
+ ret = NIL;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* IMAP parse string
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * mailgets data
+ * returned string length
+ * filter newline flag
+ * Returns: string
+ *
+ * Updates text pointer
+ */
+
+unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,GETS_DATA *md,
+ unsigned long *len,long flags)
+{
+ char *st;
+ char *string = NIL;
+ unsigned long i,j,k;
+ int bogon = NIL;
+ unsigned char c = **txtptr; /* sniff at first character */
+ mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
+ readprogress_t rp =
+ (readprogress_t) mail_parameters (NIL,GET_READPROGRESS,NIL);
+ /* ignore leading spaces */
+ while (c == ' ') c = *++*txtptr;
+ st = ++*txtptr; /* remember start of string */
+ switch (c) {
+ case '"': /* if quoted string */
+ i = 0; /* initial byte count */
+ /* search for end of string */
+ for (c = **txtptr; c != '"'; ++i,c = *++*txtptr) {
+ /* backslash quotes next character */
+ if (c == '\\') c = *++*txtptr;
+ /* CHAR8 not permitted in quoted string */
+ if (!bogon && (bogon = (c & 0x80))) {
+ sprintf (LOCAL->tmp,"Invalid CHAR in quoted string: %x",
+ (unsigned int) c);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (!c) { /* NUL not permitted either */
+ mm_notify (stream,"Unterminated quoted string",WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0; /* punt, since may be at end of string */
+ return NIL;
+ }
+ }
+ ++*txtptr; /* bump past delimiter */
+ string = (char *) fs_get ((size_t) i + 1);
+ for (j = 0; j < i; j++) { /* copy the string */
+ if (*st == '\\') ++st; /* quoted character */
+ string[j] = *st++;
+ }
+ string[j] = '\0'; /* tie off string */
+ if (len) *len = i; /* set return value too */
+ if (md && mg) { /* have special routine to slurp string? */
+ STRING bs;
+ if (md->first) { /* partial fetch? */
+ md->first--; /* restore origin octet */
+ md->last = i; /* number of octets that we got */
+ }
+ INIT (&bs,mail_string,string,i);
+ (*mg) (mail_read,&bs,i,md);
+ }
+ break;
+
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ if (len) *len = 0;
+ break;
+ case '{': /* if literal string */
+ /* get size of string */
+ if ((i = strtoul (*txtptr,(char **) txtptr,10)) > MAXSERVERLIT) {
+ sprintf (LOCAL->tmp,"Absurd server literal length %lu",i);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T; /* read and discard */
+ do net_getbuffer (LOCAL->netstream,j = min (i,(long) IMAPTMPLEN - 1),
+ LOCAL->tmp);
+ while (i -= j);
+ }
+ if (len) *len = i; /* set return value */
+ if (md && mg) { /* have special routine to slurp string? */
+ if (md->first) { /* partial fetch? */
+ md->first--; /* restore origin octet */
+ md->last = i; /* number of octets that we got */
+ }
+ else md->flags |= MG_COPY;/* otherwise flag need to copy */
+ string = (*mg) (net_getbuffer,LOCAL->netstream,i,md);
+ }
+ else { /* must slurp into free storage */
+ string = (char *) fs_get ((size_t) i + 1);
+ *string = '\0'; /* init in case getbuffer fails */
+ /* get the literal */
+ if (rp) for (k = 0; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ net_getbuffer (LOCAL->netstream,j,string + k);
+ (*rp) (md,k += j);
+ }
+ else net_getbuffer (LOCAL->netstream,i,string);
+ }
+ fs_give ((void **) &reply->line);
+ if (flags && string) /* need to filter newlines? */
+ for (st = string; st = strpbrk (st,"\015\012\011"); *st++ = ' ');
+ /* get new reply text line */
+ if (!(reply->line = net_getline (LOCAL->netstream)))
+ reply->line = cpystr ("");
+ if (stream->debug) mm_dlog (reply->line);
+ *txtptr = reply->line; /* set text pointer to point at it */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not a string: %c%.80s",c,(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0;
+ break;
+ }
+ return (unsigned char *) string;
+}
+
+/* Register text in IMAP cache
+ * Accepts: MAIL stream
+ * message number
+ * IMAP segment specifier
+ * header string list (if a HEADER section specifier)
+ * sized text to register
+ * Returns: non-zero if cache non-empty
+ */
+
+long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
+ STRINGLIST *stl,SIZEDTEXT *text)
+{
+ char *t,tmp[MAILTMPLEN];
+ unsigned long i;
+ BODY *b;
+ SIZEDTEXT *ret;
+ STRINGLIST *stc;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* top-level header never does mailgets */
+ if (!strcmp (seg,"HEADER") || !strcmp (seg,"0") ||
+ !strcmp (seg,"HEADER.FIELDS") || !strcmp (seg,"HEADER.FIELDS.NOT")) {
+ ret = &elt->private.msg.header.text;
+ if (text) { /* don't do this if no text */
+ if (ret->data) fs_give ((void **) &ret->data);
+ mail_free_stringlist (&elt->private.msg.lines);
+ elt->private.msg.lines = stl;
+ /* prevent cache reuse of .NOT */
+ if ((seg[0] == 'H') && (seg[6] == '.') && (seg[13] == '.'))
+ for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
+ if (stream->scache) { /* short caching puts it in the stream */
+ if (stream->msgno != msgno) {
+ /* flush old stuff */
+ mail_free_envelope (&stream->env);
+ mail_free_body (&stream->body);
+ stream->msgno = msgno;
+ }
+ imap_parse_header (stream,&stream->env,text,stl);
+ }
+ /* regular caching */
+ else imap_parse_header (stream,&elt->private.msg.env,text,stl);
+ }
+ }
+ /* top level text */
+ else if (!strcmp (seg,"TEXT")) {
+ ret = &elt->private.msg.text.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ else if (!*seg) { /* full message */
+ ret = &elt->private.msg.full.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+
+ else { /* nested, find non-contents specifier */
+ for (t = seg; *t && !((*t == '.') && (isalpha(t[1]) || !atol (t+1))); t++);
+ if (*t) *t++ = '\0'; /* tie off section from data specifier */
+ if (!(b = mail_body (stream,msgno,seg))) {
+ sprintf (tmp,"Unknown section number: %.80s",seg);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ if (*t) { /* if a non-numberic subpart */
+ if ((i = (b->type == TYPEMESSAGE) && (!strcmp (b->subtype,"RFC822"))) &&
+ (!strcmp (t,"HEADER") || !strcmp (t,"0") ||
+ !strcmp (t,"HEADER.FIELDS") || !strcmp (t,"HEADER.FIELDS.NOT"))) {
+ ret = &b->nested.msg->header.text;
+ if (text) {
+ if (ret->data) fs_give ((void **) &ret->data);
+ mail_free_stringlist (&b->nested.msg->lines);
+ b->nested.msg->lines = stl;
+ /* prevent cache reuse of .NOT */
+ if ((t[0] == 'H') && (t[6] == '.') && (t[13] == '.'))
+ for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
+ imap_parse_header (stream,&b->nested.msg->env,text,stl);
+ }
+ }
+ else if (i && !strcmp (t,"TEXT")) {
+ ret = &b->nested.msg->text.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ /* otherwise it must be MIME */
+ else if (!strcmp (t,"MIME")) {
+ ret = &b->mime.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ else {
+ sprintf (tmp,"Unknown section specifier: %.80s.%.80s",seg,t);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ }
+ else { /* ordinary contents */
+ ret = &b->contents.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ }
+ if (text) { /* update cache if requested */
+ ret->data = text->data;
+ ret->size = text->size;
+ }
+ return ret->data ? LONGT : NIL;
+}
+
+/* IMAP parse body structure
+ * Accepts: MAIL stream
+ * body structure to write into
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ int i;
+ char *s;
+ PART *part = NIL;
+ char c = *((*txtptr)++); /* grab first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *((*txtptr)++);
+ switch (c) { /* dispatch on first character */
+ case '(': /* body structure list */
+ if (**txtptr == '(') { /* multipart body? */
+ body->type= TYPEMULTIPART;/* yes, set its type */
+ do { /* instantiate new body part */
+ if (part) part = part->next = mail_newbody_part ();
+ else body->nested.part = part = mail_newbody_part ();
+ /* parse it */
+ imap_parse_body_structure (stream,&part->body,txtptr,reply);
+ } while (**txtptr == '(');/* for each body part */
+ if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+ ucase (body->subtype);
+ else {
+ mm_notify (stream,"Missing multipart subtype",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ }
+ if (**txtptr == ' ') /* multipart parameters */
+ body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
+ if (**txtptr == ' ') { /* disposition */
+ imap_parse_disposition (stream,body,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
+ }
+ if (**txtptr == ' ') { /* language */
+ body->language = imap_parse_language (stream,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTLANG)
+ LOCAL->cap.extlevel = BODYEXTLANG;
+ }
+ if (**txtptr == ' ') { /* location */
+ body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
+ }
+ while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ }
+
+ else { /* not multipart, parse type name */
+ if (**txtptr == ')') { /* empty body? */
+ ++*txtptr; /* bump past it */
+ break; /* and punt */
+ }
+ body->type = TYPEOTHER; /* assume unknown type */
+ body->encoding = ENCOTHER;/* and unknown encoding */
+ /* parse type */
+ if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+ ucase (s); /* application always gets uppercase form */
+ for (i = 0; /* look in existing table */
+ (i <= TYPEMAX) && body_types[i] && strcmp (s,body_types[i]); i++);
+ if (i <= TYPEMAX) { /* only if found a slot */
+ body->type = i; /* set body type */
+ if (body_types[i]) fs_give ((void **) &s);
+ else body_types[i]=s; /* assign empty slot */
+ }
+ }
+ if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+ ucase (body->subtype); /* parse subtype */
+ else {
+ mm_notify (stream,"Missing body subtype",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ }
+ body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
+ body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ body->description = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+ ucase (s); /* application always gets uppercase form */
+ for (i = 0; /* search for body encoding */
+ (i <= ENCMAX) && body_encodings[i] && strcmp(s,body_encodings[i]);
+ i++);
+ if (i > ENCMAX) body->encoding = ENCOTHER;
+ else { /* only if found a slot */
+ body->encoding = i; /* set body encoding */
+ if (body_encodings[i]) fs_give ((void **) &s);
+ /* assign empty slot */
+ else body_encodings[i] = s;
+ }
+ }
+ /* parse size of contents in bytes */
+ body->size.bytes = strtoul (*txtptr,(char **) txtptr,10);
+ switch (body->type) { /* possible extra stuff */
+ case TYPEMESSAGE: /* message envelope and body */
+ /* non MESSAGE/RFC822 is basic type */
+ if (strcmp (body->subtype,"RFC822")) break;
+ { /* make certain server sends an envelope */
+ ENVELOPE *env = NIL;
+ imap_parse_envelope (stream,&env,txtptr,reply);
+ if (!env) {
+ mm_notify (stream,"Missing body message envelope",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr ("RFC822_MISSING_ENVELOPE");
+ break;
+ }
+ (body->nested.msg = mail_newmsg ())->env = env;
+ }
+ body->nested.msg->body = mail_newbody ();
+ imap_parse_body_structure (stream,body->nested.msg->body,txtptr,reply);
+ /* drop into text case */
+ case TYPETEXT: /* size in lines */
+ body->size.lines = strtoul (*txtptr,(char **) txtptr,10);
+ break;
+ default: /* otherwise nothing special */
+ break;
+ }
+
+ if (**txtptr == ' ') { /* extension data - md5 */
+ body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTMD5) LOCAL->cap.extlevel = BODYEXTMD5;
+ }
+ if (**txtptr == ' ') { /* disposition */
+ imap_parse_disposition (stream,body,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
+ }
+ if (**txtptr == ' ') { /* language */
+ body->language = imap_parse_language (stream,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTLANG)
+ LOCAL->cap.extlevel = BODYEXTLANG;
+ }
+ if (**txtptr == ' ') { /* location */
+ body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
+ }
+ while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of body part: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ }
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default: /* otherwise quite bogus */
+ sprintf (LOCAL->tmp,"Bogus body structure: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+}
+
+/* IMAP parse body parameter
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: body parameter
+ * Updates text pointer
+ */
+
+PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,
+ unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ PARAMETER *ret = NIL;
+ PARAMETER *par = NIL;
+ char c,*s;
+ /* ignore leading spaces */
+ while ((c = *(*txtptr)++) == ' ');
+ /* parse parameter list */
+ if (c == '(') while (c != ')') {
+ /* append new parameter to tail */
+ if (ret) par = par->next = mail_newbody_parameter ();
+ else ret = par = mail_newbody_parameter ();
+ if(!(par->attribute=imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT))) {
+ mm_notify (stream,"Missing parameter attribute",WARN);
+ stream->unhealthy = T;
+ par->attribute = cpystr ("UNKNOWN");
+ }
+ if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT))){
+ sprintf (LOCAL->tmp,"Missing value for parameter %.80s",par->attribute);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ switch (c = **txtptr) { /* see what comes after */
+ case ' ': /* flush whitespace */
+ while ((c = *++*txtptr) == ' ');
+ break;
+ case ')': /* end of attribute/value pairs */
+ ++*txtptr; /* skip past closing paren */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Junk at end of parameter: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ }
+ /* empty parameter, must be NIL */
+ else if (((c == 'N') || (c == 'n')) &&
+ ((*(s = *txtptr) == 'I') || (*s == 'i')) &&
+ ((s[1] == 'L') || (s[1] == 'l'))) *txtptr += 2;
+ else {
+ sprintf (LOCAL->tmp,"Bogus body parameter: %c%.80s",c,
+ (char *) (*txtptr) - 1);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ return ret;
+}
+
+/* IMAP parse body disposition
+ * Accepts: MAIL stream
+ * body structure to write into
+ * current text pointer
+ * parsed reply
+ */
+
+void imap_parse_disposition (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ switch (*++*txtptr) {
+ case '(':
+ ++*txtptr; /* skip open paren */
+ body->disposition.type = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ body->disposition.parameter =
+ imap_parse_body_parameter (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of disposition: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Unknown body disposition: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* try to skip to next space */
+ while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
+ break;
+ }
+}
+
+/* IMAP parse body language
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: string list or NIL if empty or error
+ */
+
+STRINGLIST *imap_parse_language (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ unsigned long i;
+ char *s;
+ STRINGLIST *ret = NIL;
+ /* language is a list */
+ if (*++*txtptr == '(') ret = imap_parse_stringlist (stream,txtptr,reply);
+ else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i,LONGT)) {
+ (ret = mail_newstringlist ())->text.data = (unsigned char *) s;
+ ret->text.size = i;
+ }
+ return ret;
+}
+
+/* IMAP parse string list
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: string list or NIL if empty or error
+ */
+
+STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ STRINGLIST *stl = NIL;
+ STRINGLIST *stc = NIL;
+ unsigned char *t = *txtptr;
+ /* parse the list */
+ if (*t++ == '(') while (*t != ')') {
+ if (stl) stc = stc->next = mail_newstringlist ();
+ else stc = stl = mail_newstringlist ();
+ /* parse astring */
+ if (!(stc->text.data =
+ imap_parse_astring (stream,&t,reply,&stc->text.size))) {
+ sprintf (LOCAL->tmp,"Bogus string list member: %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_stringlist (&stl);
+ break;
+ }
+ else if (*t == ' ') ++t; /* another token follows */
+ }
+ if (stl) *txtptr = ++t; /* update return string */
+ return stl;
+}
+
+/* IMAP parse unknown body extension data
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_extension (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ unsigned long i,j;
+ switch (*++*txtptr) { /* action depends upon first character */
+ case '(':
+ while (**txtptr != ')') imap_parse_extension (stream,txtptr,reply);
+ ++*txtptr; /* bump past closing parenthesis */
+ break;
+ case '"': /* if quoted string */
+ while (*++*txtptr != '"') if (**txtptr == '\\') ++*txtptr;
+ ++*txtptr; /* bump past closing quote */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ case '{': /* get size of literal */
+ ++*txtptr; /* bump past open squiggle */
+ if (i = strtoul (*txtptr,(char **) txtptr,10)) do
+ net_getbuffer (LOCAL->netstream,j = min (i,(long) IMAPTMPLEN - 1),
+ LOCAL->tmp);
+ while (i -= j);
+ /* get new reply text line */
+ if (!(reply->line = net_getline (LOCAL->netstream)))
+ reply->line = cpystr ("");
+ if (stream->debug) mm_dlog (reply->line);
+ *txtptr = reply->line; /* set text pointer to point at it */
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ strtoul (*txtptr,(char **) txtptr,10);
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Unknown extension token: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* try to skip to next space */
+ while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
+ break;
+ }
+}
+
+/* IMAP parse capabilities
+ * Accepts: MAIL stream
+ * capability list
+ */
+
+void imap_parse_capabilities (MAILSTREAM *stream,char *t)
+{
+ char *s,*r;
+ unsigned long i;
+ THREADER *thr,*th;
+ if (!LOCAL->gotcapability) { /* need to save previous capabilities? */
+ /* no, flush threaders */
+ if (thr = LOCAL->cap.threader) while (th = thr) {
+ fs_give ((void **) &th->name);
+ thr = th->next;
+ fs_give ((void **) &th);
+ }
+ /* zap capabilities */
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ LOCAL->gotcapability = T; /* flag that capabilities arrived */
+ }
+ for (t = strtok_r (t," ",&r); t; t = strtok_r (NIL," ",&r)) {
+ if (!compare_cstring (t,"IMAP4"))
+ LOCAL->cap.imap4 = LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP4rev1"))
+ LOCAL->cap.imap4rev1 = LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP2")) LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP2bis"))
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"ACL")) LOCAL->cap.acl = T;
+ else if (!compare_cstring (t,"QUOTA")) LOCAL->cap.quota = T;
+ else if (!compare_cstring (t,"LITERAL+")) LOCAL->cap.litplus = T;
+ else if (!compare_cstring (t,"IDLE")) LOCAL->cap.idle = T;
+ else if (!compare_cstring (t,"MAILBOX-REFERRALS")) LOCAL->cap.mbx_ref = T;
+ else if (!compare_cstring (t,"LOGIN-REFERRALS")) LOCAL->cap.log_ref = T;
+ else if (!compare_cstring (t,"NAMESPACE")) LOCAL->cap.namespace = T;
+ else if (!compare_cstring (t,"UIDPLUS")) LOCAL->cap.uidplus = T;
+ else if (!compare_cstring (t,"STARTTLS")) LOCAL->cap.starttls = T;
+ else if (!compare_cstring (t,"LOGINDISABLED"))LOCAL->cap.logindisabled = T;
+ else if (!compare_cstring (t,"ID")) LOCAL->cap.id = T;
+ else if (!compare_cstring (t,"CHILDREN")) LOCAL->cap.children = T;
+ else if (!compare_cstring (t,"MULTIAPPEND")) LOCAL->cap.multiappend = T;
+ else if (!compare_cstring (t,"BINARY")) LOCAL->cap.binary = T;
+ else if (!compare_cstring (t,"UNSELECT")) LOCAL->cap.unselect = T;
+ else if (!compare_cstring (t,"SASL-IR")) LOCAL->cap.sasl_ir = T;
+ else if (!compare_cstring (t,"SCAN")) LOCAL->cap.scan = T;
+ else if (!compare_cstring (t,"URLAUTH")) LOCAL->cap.urlauth = T;
+ else if (!compare_cstring (t,"CATENATE")) LOCAL->cap.catenate = T;
+ else if (!compare_cstring (t,"CONDSTORE")) LOCAL->cap.condstore = T;
+ else if (!compare_cstring (t,"ESEARCH")) LOCAL->cap.esearch = T;
+ else if (((t[0] == 'S') || (t[0] == 's')) &&
+ ((t[1] == 'O') || (t[1] == 'o')) &&
+ ((t[2] == 'R') || (t[2] == 'r')) &&
+ ((t[3] == 'T') || (t[3] == 't'))) LOCAL->cap.sort = T;
+ /* capability with value? */
+ else if (s = strchr (t,'=')) {
+ *s++ = '\0'; /* separate token from value */
+ if (!compare_cstring (t,"THREAD") && !LOCAL->loser) {
+ THREADER *thread = (THREADER *) fs_get (sizeof (THREADER));
+ thread->name = cpystr (s);
+ thread->dispatch = NIL;
+ thread->next = LOCAL->cap.threader;
+ LOCAL->cap.threader = thread;
+ }
+ else if (!compare_cstring (t,"AUTH")) {
+ if ((i = mail_lookup_auth_name (s,LOCAL->authflags)) &&
+ (--i < MAXAUTHENTICATORS)) LOCAL->cap.auth |= (1 << i);
+ else if (!compare_cstring (s,"ANONYMOUS")) LOCAL->cap.authanon = T;
+ }
+ }
+ /* ignore other capabilities */
+ }
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
+ (LOCAL->cap.auth & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
+ LOCAL->cap.auth &= ~(1 << i);
+}
+
+/* IMAP load cache
+ * Accepts: MAIL stream
+ * sequence
+ * flags
+ * Returns: parsed reply from fetch
+ */
+
+IMAPPARSEDREPLY *imap_fetch (MAILSTREAM *stream,char *sequence,long flags)
+{
+ int i = 2;
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ?
+ "UID FETCH" : "FETCH";
+ IMAPARG *args[9],aseq,aarg,aenv,ahhr,axtr,ahtr,abdy,atrl;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & FT_UID);
+ args[0] = &aseq; aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ args[1] = &aarg; aarg.type = ATOM;
+ aenv.type = ATOM; aenv.text = (void *) "ENVELOPE";
+ ahhr.type = ATOM; ahhr.text = (void *) hdrheader[LOCAL->cap.extlevel];
+ axtr.type = ATOM; axtr.text = (void *) imap_extrahdrs;
+ ahtr.type = ATOM; ahtr.text = (void *) hdrtrailer;
+ abdy.type = ATOM; abdy.text = (void *) "BODYSTRUCTURE";
+ atrl.type = ATOM; atrl.text = (void *) "INTERNALDATE RFC822.SIZE FLAGS)";
+ if (LEVELIMAP4 (stream)) { /* include UID if IMAP4 or IMAP4rev1 */
+ aarg.text = (void *) "(UID";
+ if (flags & FT_NEEDENV) { /* if need envelopes */
+ args[i++] = &aenv; /* include envelope */
+ /* extra header poop if IMAP4rev1 */
+ if (!(flags & FT_NOHDRS) && LEVELIMAP4rev1 (stream)) {
+ args[i++] = &ahhr; /* header header */
+ if (axtr.text) args[i++] = &axtr;
+ args[i++] = &ahtr; /* header trailer */
+ }
+ /* fetch body if requested */
+ if (flags & FT_NEEDBODY) args[i++] = &abdy;
+ }
+ args[i++] = &atrl; /* fetch trailer */
+ }
+ /* easy if IMAP2 */
+ else aarg.text = (void *) (flags & FT_NEEDENV) ?
+ ((flags & FT_NEEDBODY) ?
+ "(RFC822.HEADER BODY INTERNALDATE RFC822.SIZE FLAGS)" :
+ "(RFC822.HEADER INTERNALDATE RFC822.SIZE FLAGS)") : "FAST";
+ args[i] = NIL; /* tie off command */
+ return imap_send (stream,cmd,args);
+}
+
+/* Reform sequence for losing server that doesn't handle ranges right
+ * Accepts: MAIL stream
+ * sequence
+ * non-zero if UID
+ * Returns: sequence
+ */
+
+char *imap_reform_sequence (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i,j,star;
+ char *s,*t,*tl,*rs;
+ /* can't win if empty */
+ if (!stream->nmsgs) return sequence;
+ /* get highest possible range value */
+ star = flags ? mail_uid (stream,stream->nmsgs) : stream->nmsgs;
+ /* flush old reformed sequence */
+ if (LOCAL->reform) fs_give ((void **) &LOCAL->reform);
+ rs = LOCAL->reform = (char *) fs_get (1+ strlen (sequence));
+ for (s = sequence; t = strpbrk (s,",:"); ) switch (*t++) {
+ case ',': /* single message */
+ strncpy (rs,s,i = t - s); /* copy string up to that point */
+ rs += i; /* advance destination pointer */
+ s += i; /* and source */
+ break;
+ case ':': /* message range */
+ i = (*s == '*') ? star : strtoul (s,NIL,10);
+ if (*t == '*') { /* range ends with star */
+ j = star;
+ tl = t+1;
+ }
+ else { /* numeric range end */
+ j = strtoul (t,(char **) &tl,10);
+ if (!tl) tl = t + strlen (t);
+ }
+ if (i <= j) { /* if first less than second */
+ if (*tl) tl++; /* skip past end of range if present */
+ strncpy (rs,s,i = tl - s);/* copy string up to that point */
+ rs += i; /* advance destination and source pointers */
+ s += i;
+ }
+ else { /* here's the workaround for losing servers */
+ strncpy (rs,t,i = tl - t);/* swap the order */
+ rs[i] = ':'; /* delimit */
+ strncpy (rs+i+1,s,j = (t-1) - s);
+ rs += i + 1 + j; /* advance destination pointer */
+ if (*tl) *rs++ = *tl++; /* write trailing delimiter if present */
+ s = tl; /* advance source pointer */
+ }
+ }
+ if (*s) strcpy (rs,s); /* write remainder of sequence */
+ else *rs = '\0'; /* tie off string */
+ return LOCAL->reform;
+}
+
+/* IMAP return host name
+ * Accepts: MAIL stream
+ * Returns: host name
+ */
+
+char *imap_host (MAILSTREAM *stream)
+{
+ if (stream->dtb != &imapdriver)
+ fatal ("imap_host called on non-IMAP stream!");
+ /* return host name on stream if open */
+ return (LOCAL && LOCAL->netstream) ? net_host (LOCAL->netstream) :
+ ".NO-IMAP-CONNECTION.";
+}
+
+
+/* IMAP return IMAP capability structure
+ * Accepts: MAIL stream
+ * Returns: IMAP capability structure
+ */
+
+IMAPCAP *imap_cap (MAILSTREAM *stream)
+{
+ if (stream->dtb != &imapdriver)
+ fatal ("imap_cap called on non-IMAP stream!");
+ return &LOCAL->cap; /* return capability structure */
+}
diff --git a/imap/src/c-client/imap4r1.h b/imap/src/c-client/imap4r1.h
new file mode 100644
index 00000000..8ee8a186
--- /dev/null
+++ b/imap/src/c-client/imap4r1.h
@@ -0,0 +1,281 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Interactive Mail Access Protocol 4rev1 (IMAP4R1) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 October 1988
+ * Last Edited: 5 September 2007
+ */
+
+
+/* This include file is provided for applications which need to look under
+ * the covers at the IMAP driver and in particular want to do different
+ * operations depending upon the IMAP server's protocol level and
+ * capabilities. It is NOT included in the normal c-client.h application
+ * export, and most applications do NOT need the definitions in this file.
+ *
+ * As of October 15, 2003, it is believed that:
+ *
+ * Version RFC Status Known Implementations
+ * ------- --- ------ ---------------------
+ * IMAP1 none extinct experimental TOPS-20 server
+ * IMAP2 1064 extinct old TOPS-20, SUMEX servers
+ * IMAP2 1176 rare TOPS-20, old UW servers
+ * IMAP2bis expired I-D uncommon old UW, Cyrus servers
+ * IMAP3 1203 extinct none (never implemented)
+ * IMAP4 1730 rare old UW, Cyrus, Netscape servers
+ * IMAP4rev1 2060, 3501 ubiquitous UW, Cyrus, and many others
+ *
+ * Most client implementations will only interoperate with an IMAP4rev1
+ * server. c-client based client implementations can interoperate with IMAP2,
+ * IMAP2bis, IMAP4, and IMAP4rev1 servers, but only if they are very careful.
+ *
+ * The LEVELxxx() macros in this file enable the client to determine the
+ * server protocol level and capabilities. This file also contains a few
+ * backdoor calls into the IMAP driver.
+ */
+
+/* Server protocol level and capabilities */
+
+typedef struct imap_cap {
+ unsigned int rfc1176 : 1; /* server is RFC-1176 IMAP2 */
+ unsigned int imap2bis : 1; /* server is IMAP2bis */
+ unsigned int imap4 : 1; /* server is IMAP4 (RFC 1730) */
+ unsigned int imap4rev1 : 1; /* server is IMAP4rev1 */
+ unsigned int acl : 1; /* server has ACL (RFC 2086) */
+ unsigned int quota : 1; /* server has QUOTA (RFC 2087) */
+ unsigned int litplus : 1; /* server has LITERAL+ (RFC 2088) */
+ unsigned int idle : 1; /* server has IDLE (RFC 2177) */
+ unsigned int mbx_ref : 1; /* server has mailbox referrals (RFC 2193) */
+ unsigned int log_ref : 1; /* server has login referrals (RFC 2221) */
+ unsigned int authanon : 1; /* server has anonymous SASL (RFC 2245) */
+ unsigned int namespace :1; /* server has NAMESPACE (RFC 2342) */
+ unsigned int uidplus : 1; /* server has UIDPLUS (RFC 2359) */
+ unsigned int starttls : 1; /* server has STARTTLS (RFC 2595) */
+ /* server disallows LOGIN command (RFC 2595) */
+ unsigned int logindisabled : 1;
+ unsigned int id : 1; /* server has ID (RFC 2971) */
+ unsigned int children : 1; /* server has CHILDREN (RFC 3348) */
+ unsigned int multiappend : 1; /* server has multi-APPEND (RFC 3502) ;*/
+ unsigned int binary : 1; /* server has BINARY (RFC 3516) */
+ unsigned int unselect : 1; /* server has UNSELECT */
+ unsigned int sasl_ir : 1; /* server has SASL-IR initial response */
+ unsigned int sort : 1; /* server has SORT */
+ unsigned int scan : 1; /* server has SCAN */
+ unsigned int urlauth : 1; /* server has URLAUTH (RFC 4467) */
+ unsigned int catenate : 1; /* server has CATENATE (RFC 4469) */
+ unsigned int condstore : 1; /* server has CONDSTORE (RFC 4551) */
+ unsigned int esearch : 1; /* server has ESEARCH (RFC 4731) */
+ unsigned int within : 1; /* server has WITHIN (RFC 5032) */
+ unsigned int extlevel; /* extension data level supported by server */
+ /* supported authenticators */
+ unsigned int auth : MAXAUTHENTICATORS;
+ THREADER *threader; /* list of threaders */
+} IMAPCAP;
+
+/* IMAP4rev1 level or better */
+
+#define LEVELIMAP4rev1(stream) imap_cap (stream)->imap4rev1
+
+#define LEVELSTATUS LEVELIMAP4rev1
+
+
+/* IMAP4 level or better (not including RFC 1730 design mistakes) */
+
+#define LEVELIMAP4(stream) (imap_cap (stream)->imap4rev1 || \
+ imap_cap (stream)->imap4)
+
+
+/* IMAP4 RFC-1730 level */
+
+#define LEVEL1730(stream) imap_cap (stream)->imap4
+
+
+/* IMAP2bis level or better */
+
+#define LEVELIMAP2bis(stream) imap_cap (stream)->imap2bis
+
+
+/* IMAP2 RFC-1176 level or better */
+
+#define LEVEL1176(stream) imap_cap (stream)->rfc1176
+
+
+/* IMAP2 RFC-1064 or better */
+
+#define LEVEL1064(stream) 1
+
+/* Has ACL extension */
+
+#define LEVELACL(stream) imap_cap (stream)->acl
+
+
+/* Has QUOTA extension */
+
+#define LEVELQUOTA(stream) imap_cap (stream)->quota
+
+
+/* Has LITERALPLUS extension */
+
+#define LEVELLITERALPLUS(stream) imap_cap (stream)->litplus
+
+
+/* Has IDLE extension */
+
+#define LEVELIDLE(stream) imap_cap (stream)->idle
+
+
+/* Has mailbox referrals */
+
+#define LEVELMBX_REF(stream) imap_cap (stream)->mbx_ref
+
+
+/* Has login referrals */
+
+#define LEVELLOG_REF(stream) imap_cap (stream)->log_ref
+
+
+/* Has AUTH=ANONYMOUS extension */
+
+#define LEVELANONYMOUS(stream) imap_cap (stream)->authanon
+
+
+/* Has NAMESPACE extension */
+
+#define LEVELNAMESPACE(stream) imap_cap (stream)->namespace
+
+
+/* Has UIDPLUS extension */
+
+#define LEVELUIDPLUS(stream) imap_cap (stream)->uidplus
+
+
+/* Has STARTTLS extension */
+
+#define LEVELSTARTTLS(stream) imap_cap (stream)->starttls
+
+
+/* Has LOGINDISABLED extension */
+
+#define LEVELLOGINDISABLED(stream) imap_cap (stream)->logindisabled
+
+/* Has ID extension */
+
+#define LEVELID(stream) imap_cap (stream)->id
+
+
+/* Has CHILDREN extension */
+
+#define LEVELCHILDREN(stream) imap_cap (stream)->children
+
+
+/* Has MULTIAPPEND extension */
+
+#define LEVELMULTIAPPEND(stream) imap_cap (stream)->multiappend
+
+
+/* Has BINARY extension */
+
+#define LEVELBINARY(stream) imap_cap (stream)->binary
+
+
+/* Has UNSELECT extension */
+
+#define LEVELUNSELECT(stream) imap_cap (stream)->unselect
+
+
+/* Has SASL initial response extension */
+
+#define LEVELSASLIR(stream) imap_cap (stream)->sasl_ir
+
+
+/* Has SORT extension */
+
+#define LEVELSORT(stream) imap_cap (stream)->sort
+
+
+/* Has at least one THREAD extension */
+
+#define LEVELTHREAD(stream) ((imap_cap (stream)->threader) ? T : NIL)
+
+
+/* Has SCAN extension */
+
+#define LEVELSCAN(stream) imap_cap (stream)->scan
+
+
+/* Has URLAUTH extension */
+
+#define LEVELURLAUTH(stream) imap_cap (stream)->urlauth
+
+
+/* Has CATENATE extension */
+
+#define LEVELCATENATE(stream) imap_cap (stream)->catenate
+
+
+/* Has CONDSTORE extension */
+
+#define LEVELCONDSTORE(stream) imap_cap (stream)->condstore
+
+
+/* Has ESEARCH extension */
+
+#define LEVELESEARCH(stream) imap_cap (stream)->esearch
+
+
+/* Has WITHIN extension */
+
+#define LEVELWITHIN(stream) imap_cap (stream)->within
+
+/* Body structure extension levels */
+
+/* These are in BODYSTRUCTURE order. Note that multipart bodies do not have
+ * body-fld-md5. This is alright, since all subsequent body structure
+ * extensions are in both singlepart and multipart bodies. If that ever
+ * changes, this will have to be split.
+ */
+
+#define BODYEXTMD5 1 /* body-fld-md5 */
+#define BODYEXTDSP 2 /* body-fld-dsp */
+#define BODYEXTLANG 3 /* body-fld-lang */
+#define BODYEXTLOC 4 /* body-fld-loc */
+
+
+/* Function prototypes */
+
+IMAPCAP *imap_cap (MAILSTREAM *stream);
+char *imap_host (MAILSTREAM *stream);
+long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
+ STRINGLIST *stl,SIZEDTEXT *text);
+
+
+/* Temporary */
+
+long imap_setacl (MAILSTREAM *stream,char *mailbox,char *id,char *rights);
+long imap_deleteacl (MAILSTREAM *stream,char *mailbox,char *id);
+long imap_getacl (MAILSTREAM *stream,char *mailbox);
+long imap_listrights (MAILSTREAM *stream,char *mailbox,char *id);
+long imap_myrights (MAILSTREAM *stream,char *mailbox);
+long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits);
+long imap_getquota (MAILSTREAM *stream,char *qroot);
+long imap_getquotaroot (MAILSTREAM *stream,char *mailbox);
diff --git a/imap/src/c-client/mail.c b/imap/src/c-client/mail.c
new file mode 100644
index 00000000..d80a01f6
--- /dev/null
+++ b/imap/src/c-client/mail.c
@@ -0,0 +1,6337 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mailbox Access routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 22 November 1989
+ * Last Edited: 15 April 2008
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+
+char *UW_copyright = "Copyright 1988-2007 University of Washington\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n";
+
+/* c-client global data */
+
+ /* version of this library */
+static char *mailcclientversion = CCLIENTVERSION;
+ /* list of mail drivers */
+static DRIVER *maildrivers = NIL;
+ /* list of authenticators */
+static AUTHENTICATOR *mailauthenticators = NIL;
+ /* SSL driver pointer */
+static NETDRIVER *mailssldriver = NIL;
+ /* pointer to alternate gets function */
+static mailgets_t mailgets = NIL;
+ /* pointer to read progress function */
+static readprogress_t mailreadprogress = NIL;
+ /* mail cache manipulation function */
+static mailcache_t mailcache = mm_cache;
+ /* RFC-822 output generator */
+static rfc822out_t mail822out = NIL;
+ /* RFC-822 output generator (new style) */
+static rfc822outfull_t mail822outfull = NIL;
+ /* SMTP verbose callback */
+static smtpverbose_t mailsmtpverbose = mm_dlog;
+ /* proxy copy routine */
+static mailproxycopy_t mailproxycopy = NIL;
+ /* RFC-822 external line parse */
+static parseline_t mailparseline = NIL;
+ /* RFC-822 external phrase parser */
+static parsephrase_t mailparsephrase = NIL;
+static kinit_t mailkinit = NIL; /* application kinit callback */
+ /* note network sent command */
+static sendcommand_t mailsendcommand = NIL;
+ /* newsrc file name decision function */
+static newsrcquery_t mailnewsrcquery = NIL;
+ /* ACL results callback */
+static getacl_t mailaclresults = NIL;
+ /* list rights results callback */
+static listrights_t maillistrightsresults = NIL;
+ /* my rights results callback */
+static myrights_t mailmyrightsresults = NIL;
+ /* quota results callback */
+static quota_t mailquotaresults = NIL;
+ /* quota root results callback */
+static quotaroot_t mailquotarootresults = NIL;
+ /* sorted results callback */
+static sortresults_t mailsortresults = NIL;
+ /* threaded results callback */
+static threadresults_t mailthreadresults = NIL;
+ /* COPY UID results */
+static copyuid_t mailcopyuid = NIL;
+ /* APPEND UID results */
+static appenduid_t mailappenduid = NIL;
+ /* free elt extra stuff callback */
+static freeeltsparep_t mailfreeeltsparep = NIL;
+ /* free envelope extra stuff callback */
+static freeenvelopesparep_t mailfreeenvelopesparep = NIL;
+ /* free body extra stuff callback */
+static freebodysparep_t mailfreebodysparep = NIL;
+ /* free stream extra stuff callback */
+static freestreamsparep_t mailfreestreamsparep = NIL;
+ /* SSL start routine */
+static sslstart_t mailsslstart = NIL;
+ /* SSL certificate query */
+static sslcertificatequery_t mailsslcertificatequery = NIL;
+ /* SSL client certificate */
+static sslclientcert_t mailsslclientcert = NIL;
+ /* SSL client private key */
+static sslclientkey_t mailsslclientkey = NIL;
+ /* SSL failure notify */
+static sslfailure_t mailsslfailure = NIL;
+ /* snarf interval */
+static long mailsnarfinterval = 60;
+ /* snarf preservation */
+static long mailsnarfpreserve = NIL;
+ /* newsrc name uses canonical host */
+static long mailnewsrccanon = LONGT;
+
+ /* supported threaders */
+static THREADER mailthreadordsub = {
+ "ORDEREDSUBJECT",mail_thread_orderedsubject,NIL
+};
+static THREADER mailthreadlist = {
+ "REFERENCES",mail_thread_references,&mailthreadordsub
+};
+
+ /* server name */
+static char *servicename = "unknown";
+ /* server externally-set authentication ID */
+static char *externalauthid = NIL;
+static int expungeatping = T; /* mail_ping() may call mm_expunged() */
+static int trysslfirst = NIL; /* always try SSL first */
+static int notimezones = NIL; /* write timezones in "From " header */
+static int trustdns = T; /* do DNS canonicalization */
+static int saslusesptrname = T; /* SASL uses name from DNS PTR lookup */
+ /* trustdns also must be set */
+static int debugsensitive = NIL;/* debug telemetry includes sensitive data */
+
+/* Default mail cache handler
+ * Accepts: pointer to cache handle
+ * message number
+ * caching function
+ * Returns: cache data
+ */
+
+void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
+{
+ size_t n;
+ void *ret = NIL;
+ unsigned long i;
+ switch ((int) op) { /* what function? */
+ case CH_INIT: /* initialize cache */
+ if (stream->cache) { /* flush old cache contents */
+ while (stream->cachesize) {
+ mm_cache (stream,stream->cachesize,CH_FREE);
+ mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE);
+ }
+ fs_give ((void **) &stream->cache);
+ fs_give ((void **) &stream->sc);
+ stream->nmsgs = 0; /* can't have any messages now */
+ }
+ break;
+ case CH_SIZE: /* (re-)size the cache */
+ if (!stream->cache) { /* have a cache already? */
+ /* no, create new cache */
+ n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
+ stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n);
+ stream->sc = (SORTCACHE **) memset (fs_get (n),0,n);
+ }
+ /* is existing cache size large neough */
+ else if (msgno > stream->cachesize) {
+ i = stream->cachesize; /* remember old size */
+ n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
+ fs_resize ((void **) &stream->cache,n);
+ fs_resize ((void **) &stream->sc,n);
+ while (i < stream->cachesize) {
+ stream->cache[i] = NIL;
+ stream->sc[i++] = NIL;
+ }
+ }
+ break;
+
+ case CH_MAKEELT: /* return elt, make if necessary */
+ if (!stream->cache[msgno - 1])
+ stream->cache[msgno - 1] = mail_new_cache_elt (msgno);
+ /* falls through */
+ case CH_ELT: /* return elt */
+ ret = (void *) stream->cache[msgno - 1];
+ break;
+ case CH_SORTCACHE: /* return sortcache entry, make if needed */
+ if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] =
+ (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE));
+ ret = (void *) stream->sc[msgno - 1];
+ break;
+ case CH_FREE: /* free elt */
+ mail_free_elt (&stream->cache[msgno - 1]);
+ break;
+ case CH_FREESORTCACHE:
+ if (stream->sc[msgno - 1]) {
+ if (stream->sc[msgno - 1]->from)
+ fs_give ((void **) &stream->sc[msgno - 1]->from);
+ if (stream->sc[msgno - 1]->to)
+ fs_give ((void **) &stream->sc[msgno - 1]->to);
+ if (stream->sc[msgno - 1]->cc)
+ fs_give ((void **) &stream->sc[msgno - 1]->cc);
+ if (stream->sc[msgno - 1]->subject)
+ fs_give ((void **) &stream->sc[msgno - 1]->subject);
+ if (stream->sc[msgno - 1]->unique &&
+ (stream->sc[msgno - 1]->unique != stream->sc[msgno - 1]->message_id))
+ fs_give ((void **) &stream->sc[msgno - 1]->unique);
+ if (stream->sc[msgno - 1]->message_id)
+ fs_give ((void **) &stream->sc[msgno - 1]->message_id);
+ if (stream->sc[msgno - 1]->references)
+ mail_free_stringlist (&stream->sc[msgno - 1]->references);
+ fs_give ((void **) &stream->sc[msgno - 1]);
+ }
+ break;
+ case CH_EXPUNGE: /* expunge cache slot */
+ for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) {
+ if (stream->cache[i] = stream->cache[msgno])
+ stream->cache[i]->msgno = msgno;
+ stream->sc[i] = stream->sc[msgno];
+ }
+ stream->cache[i] = NIL; /* top of cache goes away */
+ stream->sc[i] = NIL;
+ break;
+ default:
+ fatal ("Bad mm_cache op");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy string driver for complete in-memory strings */
+
+static void mail_string_init (STRING *s,void *data,unsigned long size);
+static char mail_string_next (STRING *s);
+static void mail_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER mail_string = {
+ mail_string_init, /* initialize string structure */
+ mail_string_next, /* get next byte in string structure */
+ mail_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize mail string structure for in-memory string
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void mail_string_init (STRING *s,void *data,unsigned long size)
+{
+ /* set initial string pointers */
+ s->chunk = s->curpos = (char *) (s->data = data);
+ /* and sizes */
+ s->size = s->chunksize = s->cursize = size;
+ s->data1 = s->offset = 0; /* never any offset */
+}
+
+
+/* Get next character from string
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char mail_string_next (STRING *s)
+{
+ return *s->curpos++; /* return the last byte */
+}
+
+
+/* Set string pointer position
+ * Accepts: string structure
+ * new position
+ */
+
+static void mail_string_setpos (STRING *s,unsigned long i)
+{
+ s->curpos = s->chunk + i; /* set new position */
+ s->cursize = s->chunksize - i;/* and new size */
+}
+
+/* Mail routines
+ *
+ * mail_xxx routines are the interface between this module and the outside
+ * world. Only these routines should be referenced by external callers.
+ *
+ * Note that there is an important difference between a "sequence" and a
+ * "message #" (msgno). A sequence is a string representing a sequence in
+ * {"n", "n:m", or combination separated by commas} format, whereas a msgno
+ * is a single integer.
+ *
+ */
+
+/* Mail version check
+ * Accepts: version
+ */
+
+void mail_versioncheck (char *version)
+{
+ /* attempt to protect again wrong .h */
+ if (strcmp (version,mailcclientversion)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"c-client library version skew, app=%.100s library=%.100s",
+ version,mailcclientversion);
+ fatal (tmp);
+ }
+}
+
+
+/* Mail link driver
+ * Accepts: driver to add to list
+ */
+
+void mail_link (DRIVER *driver)
+{
+ DRIVER **d = &maildrivers;
+ while (*d) d = &(*d)->next; /* find end of list of drivers */
+ *d = driver; /* put driver at the end */
+ driver->next = NIL; /* this driver is the end of the list */
+}
+
+/* Mail manipulate driver parameters
+ * Accepts: mail stream
+ * function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mail_parameters (MAILSTREAM *stream,long function,void *value)
+{
+ void *r,*ret = NIL;
+ DRIVER *d;
+ AUTHENTICATOR *a;
+ switch ((int) function) {
+ case SET_INBOXPATH:
+ fatal ("SET_INBOXPATH not permitted");
+ case GET_INBOXPATH:
+ if ((stream || (stream = mail_open (NIL,"INBOX",OP_PROTOTYPE))) &&
+ stream->dtb) ret = (*stream->dtb->parameters) (function,value);
+ break;
+ case SET_THREADERS:
+ fatal ("SET_THREADERS not permitted");
+ case GET_THREADERS: /* use stream dtb instead of global */
+ ret = (stream && stream->dtb) ?
+ /* KLUDGE ALERT: note stream passed as value */
+ (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
+ break;
+ case SET_NAMESPACE:
+ fatal ("SET_NAMESPACE not permitted");
+ break;
+ case SET_NEWSRC: /* too late on open stream */
+ if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL))))
+ fatal ("SET_NEWSRC not permitted");
+ else ret = env_parameters (function,value);
+ break;
+ case GET_NAMESPACE:
+ case GET_NEWSRC: /* use stream dtb instead of environment */
+ ret = (stream && stream->dtb) ?
+ /* KLUDGE ALERT: note stream passed as value */
+ (*stream->dtb->parameters) (function,stream) :
+ env_parameters (function,value);
+ break;
+ case ENABLE_DEBUG:
+ fatal ("ENABLE_DEBUG not permitted");
+ case DISABLE_DEBUG:
+ fatal ("DISABLE_DEBUG not permitted");
+ case SET_DIRFMTTEST:
+ fatal ("SET_DIRFMTTEST not permitted");
+ case GET_DIRFMTTEST:
+ if (!(stream && stream->dtb &&
+ (ret = (*stream->dtb->parameters) (function,NIL))))
+ fatal ("GET_DIRFMTTEST not permitted");
+ break;
+
+ case SET_DRIVERS:
+ fatal ("SET_DRIVERS not permitted");
+ case GET_DRIVERS: /* always return global */
+ ret = (void *) maildrivers;
+ break;
+ case SET_DRIVER:
+ fatal ("SET_DRIVER not permitted");
+ case GET_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ ret = (void *) d;
+ break;
+ case ENABLE_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ if (ret = (void *) d) d->flags &= ~DR_DISABLE;
+ break;
+ case DISABLE_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ if (ret = (void *) d) d->flags |= DR_DISABLE;
+ break;
+ case ENABLE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags &= ~AU_DISABLE;
+ break;
+ case DISABLE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags |= AU_DISABLE;
+ break;
+ case UNHIDE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags &= ~AU_HIDE;
+ break;
+ case HIDE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags |= AU_HIDE;
+ break;
+ case SET_EXTERNALAUTHID:
+ if (value) { /* setting external authentication ID */
+ externalauthid = cpystr ((char *) value);
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"EXTERNAL");
+ }
+ else { /* clearing external authentication ID */
+ if (externalauthid) fs_give ((void **) &externalauthid);
+ mail_parameters (NIL,HIDE_AUTHENTICATOR,"EXTERNAL");
+ }
+ case GET_EXTERNALAUTHID:
+ ret = (void *) externalauthid;
+ break;
+
+ case SET_GETS:
+ mailgets = (mailgets_t) value;
+ case GET_GETS:
+ ret = (void *) mailgets;
+ break;
+ case SET_READPROGRESS:
+ mailreadprogress = (readprogress_t) value;
+ case GET_READPROGRESS:
+ ret = (void *) mailreadprogress;
+ break;
+ case SET_CACHE:
+ mailcache = (mailcache_t) value;
+ case GET_CACHE:
+ ret = (void *) mailcache;
+ break;
+ case SET_RFC822OUTPUT:
+ mail822out = (rfc822out_t) value;
+ case GET_RFC822OUTPUT:
+ ret = (void *) mail822out;
+ break;
+ case SET_RFC822OUTPUTFULL:
+ mail822outfull = (rfc822outfull_t) value;
+ case GET_RFC822OUTPUTFULL:
+ ret = (void *) mail822outfull;
+ break;
+ case SET_SMTPVERBOSE:
+ mailsmtpverbose = (smtpverbose_t) value;
+ case GET_SMTPVERBOSE:
+ ret = (void *) mailsmtpverbose;
+ break;
+ case SET_MAILPROXYCOPY:
+ mailproxycopy = (mailproxycopy_t) value;
+ case GET_MAILPROXYCOPY:
+ ret = (void *) mailproxycopy;
+ break;
+ case SET_PARSELINE:
+ mailparseline = (parseline_t) value;
+ case GET_PARSELINE:
+ ret = (void *) mailparseline;
+ break;
+ case SET_PARSEPHRASE:
+ mailparsephrase = (parsephrase_t) value;
+ case GET_PARSEPHRASE:
+ ret = (void *) mailparsephrase;
+ break;
+ case SET_NEWSRCQUERY:
+ mailnewsrcquery = (newsrcquery_t) value;
+ case GET_NEWSRCQUERY:
+ ret = (void *) mailnewsrcquery;
+ break;
+ case SET_NEWSRCCANONHOST:
+ mailnewsrccanon = (long) value;
+ case GET_NEWSRCCANONHOST:
+ ret = (void *) mailnewsrccanon;
+ break;
+
+ case SET_COPYUID:
+ mailcopyuid = (copyuid_t) value;
+ case GET_COPYUID:
+ ret = (void *) mailcopyuid;
+ break;
+ case SET_APPENDUID:
+ mailappenduid = (appenduid_t) value;
+ case GET_APPENDUID:
+ ret = (void *) mailappenduid;
+ break;
+ case SET_FREEENVELOPESPAREP:
+ mailfreeenvelopesparep = (freeenvelopesparep_t) value;
+ case GET_FREEENVELOPESPAREP:
+ ret = (void *) mailfreeenvelopesparep;
+ break;
+ case SET_FREEELTSPAREP:
+ mailfreeeltsparep = (freeeltsparep_t) value;
+ case GET_FREEELTSPAREP:
+ ret = (void *) mailfreeeltsparep;
+ break;
+ case SET_FREESTREAMSPAREP:
+ mailfreestreamsparep = (freestreamsparep_t) value;
+ case GET_FREESTREAMSPAREP:
+ ret = (void *) mailfreestreamsparep;
+ break;
+ case SET_FREEBODYSPAREP:
+ mailfreebodysparep = (freebodysparep_t) value;
+ case GET_FREEBODYSPAREP:
+ ret = (void *) mailfreebodysparep;
+ break;
+
+ case SET_SSLSTART:
+ mailsslstart = (sslstart_t) value;
+ case GET_SSLSTART:
+ ret = (void *) mailsslstart;
+ break;
+ case SET_SSLCERTIFICATEQUERY:
+ mailsslcertificatequery = (sslcertificatequery_t) value;
+ case GET_SSLCERTIFICATEQUERY:
+ ret = (void *) mailsslcertificatequery;
+ break;
+ case SET_SSLCLIENTCERT:
+ mailsslclientcert = (sslclientcert_t) value;
+ case GET_SSLCLIENTCERT:
+ ret = (void *) mailsslclientcert;
+ break;
+ case SET_SSLCLIENTKEY:
+ mailsslclientkey = (sslclientkey_t) value;
+ case GET_SSLCLIENTKEY:
+ ret = (void *) mailsslclientkey;
+ break;
+ case SET_SSLFAILURE:
+ mailsslfailure = (sslfailure_t) value;
+ case GET_SSLFAILURE:
+ ret = (void *) mailsslfailure;
+ break;
+ case SET_KINIT:
+ mailkinit = (kinit_t) value;
+ case GET_KINIT:
+ ret = (void *) mailkinit;
+ break;
+ case SET_SENDCOMMAND:
+ mailsendcommand = (sendcommand_t) value;
+ case GET_SENDCOMMAND:
+ ret = (void *) mailsendcommand;
+ break;
+
+ case SET_SERVICENAME:
+ servicename = (char *) value;
+ case GET_SERVICENAME:
+ ret = (void *) servicename;
+ break;
+ case SET_EXPUNGEATPING:
+ expungeatping = (value ? T : NIL);
+ case GET_EXPUNGEATPING:
+ ret = (void *) (expungeatping ? VOIDT : NIL);
+ break;
+ case SET_SORTRESULTS:
+ mailsortresults = (sortresults_t) value;
+ case GET_SORTRESULTS:
+ ret = (void *) mailsortresults;
+ break;
+ case SET_THREADRESULTS:
+ mailthreadresults = (threadresults_t) value;
+ case GET_THREADRESULTS:
+ ret = (void *) mailthreadresults;
+ break;
+ case SET_SSLDRIVER:
+ mailssldriver = (NETDRIVER *) value;
+ case GET_SSLDRIVER:
+ ret = (void *) mailssldriver;
+ break;
+ case SET_TRYSSLFIRST:
+ trysslfirst = (value ? T : NIL);
+ case GET_TRYSSLFIRST:
+ ret = (void *) (trysslfirst ? VOIDT : NIL);
+ break;
+ case SET_NOTIMEZONES:
+ notimezones = (value ? T : NIL);
+ case GET_NOTIMEZONES:
+ ret = (void *) (notimezones ? VOIDT : NIL);
+ break;
+ case SET_TRUSTDNS:
+ trustdns = (value ? T : NIL);
+ case GET_TRUSTDNS:
+ ret = (void *) (trustdns ? VOIDT : NIL);
+ break;
+ case SET_SASLUSESPTRNAME:
+ saslusesptrname = (value ? T : NIL);
+ case GET_SASLUSESPTRNAME:
+ ret = (void *) (saslusesptrname ? VOIDT : NIL);
+ break;
+ case SET_DEBUGSENSITIVE:
+ debugsensitive = (value ? T : NIL);
+ case GET_DEBUGSENSITIVE:
+ ret = (void *) (debugsensitive ? VOIDT : NIL);
+ break;
+
+ case SET_ACL:
+ mailaclresults = (getacl_t) value;
+ case GET_ACL:
+ ret = (void *) mailaclresults;
+ break;
+ case SET_LISTRIGHTS:
+ maillistrightsresults = (listrights_t) value;
+ case GET_LISTRIGHTS:
+ ret = (void *) maillistrightsresults;
+ break;
+ case SET_MYRIGHTS:
+ mailmyrightsresults = (myrights_t) value;
+ case GET_MYRIGHTS:
+ ret = (void *) mailmyrightsresults;
+ break;
+ case SET_QUOTA:
+ mailquotaresults = (quota_t) value;
+ case GET_QUOTA:
+ ret = (void *) mailquotaresults;
+ break;
+ case SET_QUOTAROOT:
+ mailquotarootresults = (quotaroot_t) value;
+ case GET_QUOTAROOT:
+ ret = (void *) mailquotarootresults;
+ break;
+ case SET_SNARFINTERVAL:
+ mailsnarfinterval = (long) value;
+ case GET_SNARFINTERVAL:
+ ret = (void *) mailsnarfinterval;
+ break;
+ case SET_SNARFPRESERVE:
+ mailsnarfpreserve = (long) value;
+ case GET_SNARFPRESERVE:
+ ret = (void *) mailsnarfpreserve;
+ break;
+ case SET_SNARFMAILBOXNAME:
+ if (stream) { /* have a stream? */
+ if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
+ stream->snarf.name = cpystr ((char *) value);
+ }
+ else fatal ("SET_SNARFMAILBOXNAME with no stream");
+ case GET_SNARFMAILBOXNAME:
+ if (stream) ret = (void *) stream->snarf.name;
+ break;
+ default:
+ if (r = smtp_parameters (function,value)) ret = r;
+ if (r = env_parameters (function,value)) ret = r;
+ if (r = tcp_parameters (function,value)) ret = r;
+ if (stream && stream->dtb) {/* if have stream, do for its driver only */
+ if (r = (*stream->dtb->parameters) (function,value)) ret = r;
+ }
+ /* else do all drivers */
+ else for (d = maildrivers; d; d = d->next)
+ if (r = (d->parameters) (function,value)) ret = r;
+ break;
+ }
+ return ret;
+}
+
+/* Mail validate mailbox name
+ * Accepts: MAIL stream
+ * mailbox name
+ * purpose string for error message
+ * Return: driver factory on success, NIL on failure
+ */
+
+DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
+{
+ char tmp[MAILTMPLEN];
+ DRIVER *factory = NIL;
+ /* never allow names with newlines */
+ if (strpbrk (mailbox,"\015\012")) {
+ if (purpose) { /* if want an error message */
+ sprintf (tmp,"Can't %s with such a name",purpose);
+ MM_LOG (tmp,ERROR);
+ }
+ return NIL;
+ }
+ /* validate name, find driver factory */
+ if (strlen (mailbox) < (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50))
+ for (factory = maildrivers; factory &&
+ ((factory->flags & DR_DISABLE) ||
+ ((factory->flags & DR_LOCAL) && (*mailbox == '{')) ||
+ !(*factory->valid) (mailbox));
+ factory = factory->next);
+ /* validate factory against non-dummy stream */
+ if (factory && stream && stream->dtb && (stream->dtb != factory) &&
+ strcmp (stream->dtb->name,"dummy"))
+ /* factory invalid; if dummy, use stream */
+ factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
+ if (!factory && purpose) { /* if want an error message */
+ sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ }
+ return factory; /* return driver factory */
+}
+
+/* Mail validate network mailbox name
+ * Accepts: mailbox name
+ * mailbox driver to validate against
+ * pointer to where to return host name if non-NIL
+ * pointer to where to return mailbox name if non-NIL
+ * Returns: driver on success, NIL on failure
+ */
+
+DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
+{
+ NETMBX mb;
+ if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
+ return NIL;
+ if (host) strcpy (host,mb.host);
+ if (mailbox) strcpy (mailbox,mb.mailbox);
+ return drv;
+}
+
+
+/* Mail validate network mailbox name
+ * Accepts: mailbox name
+ * NETMBX structure to return values
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_valid_net_parse (char *name,NETMBX *mb)
+{
+ return mail_valid_net_parse_work (name,mb,"imap");
+}
+
+/* Mail validate network mailbox name worker routine
+ * Accepts: mailbox name
+ * NETMBX structure to return values
+ * default service
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service)
+{
+ int i,j;
+ char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN];
+ /* initialize structure */
+ memset (mb,'\0',sizeof (NETMBX));
+ /* must have host specification */
+ if (*name++ != '{') return NIL;
+ if (*name == '[') { /* if domain literal, find its ending */
+ if (!((v = strpbrk (name,"]}")) && (*v++ == ']'))) return NIL;
+ }
+ /* find end of host name */
+ else if (!(v = strpbrk (name,"/:}"))) return NIL;
+ /* validate length, find mailbox part */
+ if (!((i = v - name) && (i < NETMAXHOST) && (t = strchr (v,'}')) &&
+ ((j = t - v) < MAILTMPLEN) && (strlen (t+1) < (size_t) NETMAXMBX)))
+ return NIL; /* invalid mailbox */
+ strncpy (mb->host,name,i); /* set host name */
+ strncpy (mb->orighost,name,i);
+ mb->host[i] = mb->orighost[i] = '\0';
+ strcpy (mb->mailbox,t+1); /* set mailbox name */
+ if (t - v) { /* any switches or port specification? */
+ strncpy (t = tmp,v,j); /* copy it */
+ tmp[j] = '\0'; /* tie it off */
+ c = *t++; /* get first delimiter */
+ do switch (c) { /* act based upon the character */
+ case ':': /* port specification */
+ if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL;
+ c = t ? *t++ : '\0'; /* get delimiter, advance pointer */
+ break;
+ case '/': /* switch */
+ /* find delimiter */
+ if (t = strpbrk (s = t,"/:=")) {
+ c = *t; /* remember delimiter for later */
+ *t++ = '\0'; /* tie off switch name */
+ }
+ else c = '\0'; /* no delimiter */
+ if (c == '=') { /* parse switches which take arguments */
+ if (*t == '"') { /* quoted string? */
+ for (v = arg,i = 0,++t; (c = *t++) != '"';) {
+ if (!c) return NIL; /* unterminated string */
+ /* quote next character */
+ if (c == '\\') c = *t++;
+ if (!c) return NIL; /* can't quote NUL either */
+ arg[i++] = c;
+ }
+ c = *t++; /* remember delimiter for later */
+ arg[i] = '\0'; /* tie off argument */
+ }
+ else { /* non-quoted argument */
+ if (t = strpbrk (v = t,"/:")) {
+ c = *t; /* remember delimiter for later */
+ *t++ = '\0'; /* tie off switch name */
+ }
+ else c = '\0'; /* no delimiter */
+ i = strlen (v); /* length of argument */
+ }
+ if (!compare_cstring (s,"service") && (i < NETMAXSRV) && !*mb->service)
+ lcase (strcpy (mb->service,v));
+ else if (!compare_cstring (s,"user") && (i < NETMAXUSER) && !*mb->user)
+ strcpy (mb->user,v);
+ else if (!compare_cstring (s,"authuser") && (i < NETMAXUSER) &&
+ !*mb->authuser) strcpy (mb->authuser,v);
+ else return NIL;
+ }
+
+ else { /* non-argument switch */
+ if (!compare_cstring (s,"anonymous")) mb->anoflag = T;
+ else if (!compare_cstring (s,"debug")) mb->dbgflag = T;
+ else if (!compare_cstring (s,"readonly")) mb->readonlyflag = T;
+ else if (!compare_cstring (s,"secure")) mb->secflag = T;
+ else if (!compare_cstring (s,"norsh")) mb->norsh = T;
+ else if (!compare_cstring (s,"loser")) mb->loser = T;
+ else if (!compare_cstring (s,"tls") && !mb->notlsflag)
+ mb->tlsflag = T;
+ else if (!compare_cstring (s,"tls-sslv23") && !mb->notlsflag)
+ mb->tlssslv23 = mb->tlsflag = T;
+ else if (!compare_cstring (s,"notls") && !mb->tlsflag)
+ mb->notlsflag = T;
+ else if (!compare_cstring (s,"tryssl"))
+ mb->trysslflag = mailssldriver? T : NIL;
+ else if (mailssldriver && !compare_cstring (s,"ssl") && !mb->tlsflag)
+ mb->sslflag = mb->notlsflag = T;
+ else if (mailssldriver && !compare_cstring (s,"novalidate-cert"))
+ mb->novalidate = T;
+ /* hack for compatibility with the past */
+ else if (mailssldriver && !compare_cstring (s,"validate-cert"));
+ /* service switches below here */
+ else if (*mb->service) return NIL;
+ else if (!compare_cstring (s,"imap") ||
+ !compare_cstring (s,"nntp") ||
+ !compare_cstring (s,"pop3") ||
+ !compare_cstring (s,"smtp") ||
+ !compare_cstring (s,"submit"))
+ lcase (strcpy (mb->service,s));
+ else if (!compare_cstring (s,"imap2") ||
+ !compare_cstring (s,"imap2bis") ||
+ !compare_cstring (s,"imap4") ||
+ !compare_cstring (s,"imap4rev1"))
+ strcpy (mb->service,"imap");
+ else if (!compare_cstring (s,"pop"))
+ strcpy (mb->service,"pop3");
+ else return NIL; /* invalid non-argument switch */
+ }
+ break;
+ default: /* anything else is bogus */
+ return NIL;
+ } while (c); /* see if anything more to parse */
+ }
+ /* default mailbox name */
+ if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX");
+ /* default service name */
+ if (!*mb->service) strcpy (mb->service,service);
+ /* /norsh only valid if imap */
+ if (mb->norsh && strcmp (mb->service,"imap")) return NIL;
+ return T;
+}
+
+/* Mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * contents to search
+ */
+
+void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream) { /* if have a stream, do it for that stream */
+ if ((d = stream->dtb) && d->scan &&
+ !(((d->flags & DR_LOCAL) && remote)))
+ (*d->scan) (stream,ref,pat,contents);
+ }
+ /* otherwise do for all DTB's */
+ else for (d = maildrivers; d; d = d->next)
+ if (d->scan && !((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->scan) (NIL,ref,pat,contents);
+}
+
+/* Mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mail_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d = maildrivers;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream && stream->dtb) { /* if have a stream, do it for that stream */
+ if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
+ (*d->list) (stream,ref,pat);
+ }
+ /* otherwise do for all DTB's */
+ else do if (!((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->list) (NIL,ref,pat);
+ while (d = d->next); /* until at the end */
+}
+
+/* Mail list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void mail_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d = maildrivers;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LSUB reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LSUB pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream && stream->dtb) { /* if have a stream, do it for that stream */
+ if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
+ (*d->lsub) (stream,ref,pat);
+ }
+ /* otherwise do for all DTB's */
+ else do if (!((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->lsub) (NIL,ref,pat);
+ while (d = d->next); /* until at the end */
+}
+
+/* Mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
+ return factory ?
+ (factory->subscribe ?
+ (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL;
+}
+
+
+/* Mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *factory = mail_valid (stream,mailbox,NIL);
+ return (factory && factory->unsubscribe) ?
+ (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox);
+}
+
+/* Mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_create (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *ts;
+ char *s,*t,tmp[MAILTMPLEN];
+ size_t i;
+ DRIVER *d;
+ /* never allow names with newlines */
+ if (s = strpbrk (mailbox,"\015\012")) {
+ MM_LOG ("Can't create mailbox with such a name",ERROR);
+ return NIL;
+ }
+ if (strlen (mailbox) >= (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
+ sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* create of INBOX invalid */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ MM_LOG ("Can't create INBOX",ERROR);
+ return NIL;
+ }
+ /* validate name */
+ if (s = mail_utf7_valid (mailbox)) {
+ sprintf (tmp,"Can't create %s: %.80s",s,mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* see if special driver hack */
+ if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) &&
+ ((mailbox[2] == 'r') || (mailbox[2] == 'R')) &&
+ ((mailbox[3] == 'i') || (mailbox[3] == 'I')) &&
+ ((mailbox[4] == 'v') || (mailbox[4] == 'V')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'r') || (mailbox[6] == 'R')) && (mailbox[7] == '.')) {
+ /* copy driver until likely delimiter */
+ if ((s = strpbrk (t = mailbox+8,"/\\:")) && (i = s - t)) {
+ strncpy (tmp,t,i);
+ tmp[i] = '\0';
+ }
+ else {
+ sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next);
+ if (d) mailbox = ++s; /* skip past driver specification */
+ else {
+ sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* use stream if one given or deterministic */
+ else if ((stream && stream->dtb) ||
+ (((*mailbox == '{') || (*mailbox == '#')) &&
+ (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT))))
+ d = stream->dtb;
+ else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb;
+ else { /* failed utterly */
+ sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return (*d->create) (stream,mailbox);
+}
+
+/* Mail delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox");
+ if (!dtb) return NIL;
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
+ MM_LOG ("Can't delete INBOX",ERROR);
+ return NIL;
+ }
+ return SAFE_DELETE (dtb,stream,mailbox);
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char *s,tmp[MAILTMPLEN];
+ DRIVER *dtb = mail_valid (stream,old,"rename mailbox");
+ if (!dtb) return NIL;
+ /* validate name */
+ if (s = mail_utf7_valid (newname)) {
+ sprintf (tmp,"Can't rename to %s: %.80s",s,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) {
+ sprintf (tmp,"Can't rename %.80s: mailbox %.80s already exists",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return SAFE_RENAME (dtb,stream,old,newname);
+}
+
+/* Validate mailbox as Modified UTF-7
+ * Accepts: candidate mailbox name
+ * Returns: error string if error, NIL if valid
+ */
+
+char *mail_utf7_valid (char *mailbox)
+{
+ char *s;
+ for (s = mailbox; *s; s++) { /* make sure valid name */
+ /* reserved for future use with UTF-8 */
+ if (*s & 0x80) return "mailbox name with 8-bit octet";
+ /* validate modified UTF-7 */
+ else if (*s == '&') while (*++s != '-') switch (*s) {
+ case '\0':
+ return "unterminated modified UTF-7 name";
+ case '+': /* valid modified BASE64 */
+ case ',':
+ break; /* all OK so far */
+ default: /* must be alphanumeric */
+ if (!isalnum (*s)) return "invalid modified UTF-7 name";
+ break;
+ }
+ }
+ return NIL; /* all OK */
+}
+
+/* Mail status of mailbox
+ * Accepts: mail stream if open on this mailbox
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ DRIVER *dtb = mail_valid (stream,mbx,"get status of mailbox");
+ if (!dtb) return NIL; /* only if valid */
+ if (stream && ((dtb != stream->dtb) ||
+ ((dtb->flags & DR_LOCAL) && strcmp (mbx,stream->mailbox) &&
+ strcmp (mbx,stream->original_mailbox))))
+ stream = NIL; /* stream not suitable */
+ return SAFE_STATUS (dtb,stream,mbx,flags);
+}
+
+
+/* Mail status of mailbox default handler
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_status_default (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ return T; /* success */
+}
+
+/* Mail open
+ * Accepts: candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
+{
+ int i;
+ char c,*s,tmp[MAILTMPLEN];
+ NETMBX mb;
+ DRIVER *d;
+ switch (name[0]) { /* see if special handling */
+ case '#': /* possible special hacks */
+ if (((name[1] == 'M') || (name[1] == 'm')) &&
+ ((name[2] == 'O') || (name[2] == 'o')) &&
+ ((name[3] == 'V') || (name[3] == 'v')) &&
+ ((name[4] == 'E') || (name[4] == 'e')) && (c = name[5]) &&
+ (s = strchr (name+6,c)) && (i = s - (name + 6)) && (i < MAILTMPLEN)) {
+ if (stream = mail_open (stream,s+1,options)) {
+ strncpy (tmp,name+6,i); /* copy snarf mailbox name */
+ tmp[i] = '\0'; /* tie off name */
+ mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
+ stream->snarf.options = options;
+ mail_ping (stream); /* do initial snarf */
+ /* punt if can't do initial snarf */
+ if (!stream->snarf.time) stream = mail_close (stream);
+ }
+ return stream;
+ }
+ /* special POP hack */
+ else if (((name[1] == 'P') || (name[1] == 'p')) &&
+ ((name[2] == 'O') || (name[2] == 'o')) &&
+ ((name[3] == 'P') || (name[3] == 'p')) &&
+ mail_valid_net_parse_work (name+4,&mb,"pop3") &&
+ !strcmp (mb.service,"pop3") && !mb.anoflag && !mb.readonlyflag) {
+ if (stream = mail_open (stream,mb.mailbox,options)) {
+ sprintf (tmp,"{%.255s",mb.host);
+ if (mb.port) sprintf (tmp + strlen (tmp),":%lu",mb.port);
+ if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=%.64s",mb.user);
+ if (mb.dbgflag) strcat (tmp,"/debug");
+ if (mb.secflag) strcat (tmp,"/secure");
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.trysslflag) strcat (tmp,"/tryssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ strcat (tmp,"/pop3/loser}");
+ mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
+ mail_ping (stream); /* do initial snarf */
+ }
+ return stream; /* return local mailbox stream */
+ }
+
+ else if ((options & OP_PROTOTYPE) &&
+ ((name[1] == 'D') || (name[1] == 'd')) &&
+ ((name[2] == 'R') || (name[2] == 'r')) &&
+ ((name[3] == 'I') || (name[3] == 'i')) &&
+ ((name[4] == 'V') || (name[4] == 'v')) &&
+ ((name[5] == 'E') || (name[5] == 'e')) &&
+ ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) {
+ sprintf (tmp,"%.80s",name+8);
+ /* tie off name at likely delimiter */
+ if (s = strpbrk (tmp,"/\\:")) *s++ = '\0';
+ else {
+ sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name);
+ MM_LOG (tmp,ERROR);
+ return mail_close (stream);
+ }
+ for (d = maildrivers; d && compare_cstring (d->name,tmp); d = d->next);
+ if (d) return (*d->open) (NIL);
+ sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name);
+ MM_LOG (tmp,ERROR);
+ return mail_close (stream);
+ }
+ /* fall through to default case */
+ default: /* not special hack (but could be # name */
+ d = mail_valid (NIL,name,(options & OP_SILENT) ?
+ (char *) NIL : "open mailbox");
+ }
+ return d ? mail_open_work (d,stream,name,options) : stream;
+}
+
+/* Mail open worker routine
+ * Accepts: factory
+ * candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
+ long options)
+{
+ int i;
+ char tmp[MAILTMPLEN];
+ NETMBX mb;
+ if (options & OP_PROTOTYPE) return (*d->open) (NIL);
+ /* name is copied here in case the caller does a re-open using
+ * stream->mailbox or stream->original_mailbox as the argument.
+ */
+ name = cpystr (name); /* make copy of name */
+ if (stream) { /* recycling requested? */
+ if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
+ ((d->flags & DR_HALFOPEN) || !(options & OP_HALFOPEN)) &&
+ mail_usable_network_stream (stream,name)) {
+ /* yes, checkpoint if needed */
+ if (d->flags & DR_XPOINT) mail_check (stream);
+ mail_free_cache (stream); /* clean up stream */
+ if (stream->mailbox) fs_give ((void **) &stream->mailbox);
+ if (stream->original_mailbox)
+ fs_give ((void **) &stream->original_mailbox);
+ /* flush user flags */
+ for (i = 0; i < NUSERFLAGS; i++)
+ if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
+ }
+ else { /* stream not recycleable, babble if net */
+ if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) &&
+ mail_valid_net_parse (stream->mailbox,&mb)) {
+ sprintf (tmp,"Closing connection to %.80s",mb.host);
+ MM_LOG (tmp,(long) NIL);
+ }
+ /* flush the old stream */
+ stream = mail_close (stream);
+ }
+ }
+ /* check if driver does not support halfopen */
+ else if ((options & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)) {
+ fs_give ((void **) &name);
+ return NIL;
+ }
+
+ /* instantiate new stream if not recycling */
+ if (!stream) (*mailcache) (stream = (MAILSTREAM *)
+ memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM)),(long) 0,CH_INIT);
+ stream->dtb = d; /* set dispatch */
+ /* set mailbox name */
+ stream->mailbox = cpystr (stream->original_mailbox = name);
+ /* initialize stream flags */
+ stream->inbox = stream->lock = NIL;
+ stream->debug = (options & OP_DEBUG) ? T : NIL;
+ stream->rdonly = (options & OP_READONLY) ? T : NIL;
+ stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
+ stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
+ stream->silent = (options & OP_SILENT) ? T : NIL;
+ stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
+ stream->secure = (options & OP_SECURE) ? T : NIL;
+ stream->tryssl = (options & OP_TRYSSL) ? T : NIL;
+ stream->mulnewsrc = (options & OP_MULNEWSRC) ? T : NIL;
+ stream->nokod = (options & OP_NOKOD) ? T : NIL;
+ stream->sniff = (options & OP_SNIFF) ? T : NIL;
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL;
+ stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL;
+ stream->uid_last = 0; /* default UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ /* have driver open, flush if failed */
+ return ((*d->open) (stream)) ? stream : mail_close (stream);
+}
+
+/* Mail close
+ * Accepts: mail stream
+ * close options
+ * Returns: NIL, always
+ */
+
+MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
+{
+ int i;
+ if (stream) { /* make sure argument given */
+ /* do the driver's close action */
+ if (stream->dtb) (*stream->dtb->close) (stream,options);
+ stream->dtb = NIL; /* resign driver */
+ if (stream->mailbox) fs_give ((void **) &stream->mailbox);
+ if (stream->original_mailbox)
+ fs_give ((void **) &stream->original_mailbox);
+ if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
+ stream->sequence++; /* invalidate sequence */
+ /* flush user flags */
+ for (i = 0; i < NUSERFLAGS; i++)
+ if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
+ mail_free_cache (stream); /* finally free the stream's storage */
+ if (mailfreestreamsparep && stream->sparep)
+ (*mailfreestreamsparep) (&stream->sparep);
+ if (!stream->use) fs_give ((void **) &stream);
+ }
+ return NIL;
+}
+
+/* Mail make handle
+ * Accepts: mail stream
+ * Returns: handle
+ *
+ * Handles provide a way to have multiple pointers to a stream yet allow the
+ * stream's owner to nuke it or recycle it.
+ */
+
+MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
+{
+ MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
+ handle->stream = stream; /* copy stream */
+ /* and its sequence */
+ handle->sequence = stream->sequence;
+ stream->use++; /* let stream know another handle exists */
+ return handle;
+}
+
+
+/* Mail release handle
+ * Accepts: Mail handle
+ */
+
+void mail_free_handle (MAILHANDLE **handle)
+{
+ MAILSTREAM *s;
+ if (*handle) { /* only free if exists */
+ /* resign stream, flush unreferenced zombies */
+ if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
+ fs_give ((void **) handle); /* now flush the handle */
+ }
+}
+
+
+/* Mail get stream handle
+ * Accepts: Mail handle
+ * Returns: mail stream or NIL if stream gone
+ */
+
+MAILSTREAM *mail_stream (MAILHANDLE *handle)
+{
+ MAILSTREAM *s = handle->stream;
+ return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
+}
+
+/* Mail fetch cache element
+ * Accepts: mail stream
+ * message # to fetch
+ * Returns: cache element of this message
+ * Can also be used to create cache elements for new messages.
+ */
+
+MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ if (msgno < 1 || msgno > stream->nmsgs) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu, mbx=%.80s",
+ msgno,stream->nmsgs,stream->mailbox ? stream->mailbox : "???");
+ fatal (tmp);
+ }
+ return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
+}
+
+
+/* Mail fetch fast information
+ * Accepts: mail stream
+ * sequence
+ * option flags
+ *
+ * Generally, mail_fetch_structure is preferred
+ */
+
+void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ /* do the driver's action */
+ if (stream->dtb && stream->dtb->fast)
+ (*stream->dtb->fast) (stream,sequence,flags);
+}
+
+
+/* Mail fetch flags
+ * Accepts: mail stream
+ * sequence
+ * option flags
+ */
+
+void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ /* do the driver's action */
+ if (stream->dtb && stream->dtb->msgflags)
+ (*stream->dtb->msgflags) (stream,sequence,flags);
+}
+
+/* Mail fetch message overview
+ * Accepts: mail stream
+ * UID sequence to fetch
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
+{
+ if (stream->dtb && mail_uid_sequence (stream,sequence) &&
+ !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
+ mail_ping (stream))
+ mail_fetch_overview_default (stream,ofn);
+}
+
+
+/* Mail fetch message overview using sequence numbers instead of UIDs
+ * Accepts: mail stream
+ * sequence to fetch
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence,
+ overview_t ofn)
+{
+ if (stream->dtb && mail_sequence (stream,sequence) &&
+ !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
+ mail_ping (stream))
+ mail_fetch_overview_default (stream,ofn);
+}
+
+
+/* Mail fetch message overview default handler
+ * Accepts: mail stream with sequence bits lit
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn)
+{
+ MESSAGECACHE *elt;
+ ENVELOPE *env;
+ OVERVIEW ov;
+ unsigned long i;
+ ov.optional.lines = 0;
+ ov.optional.xref = NIL;
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (((elt = mail_elt (stream,i))->sequence) &&
+ (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
+ ov.subject = env->subject;
+ ov.from = env->from;
+ ov.date = env->date;
+ ov.message_id = env->message_id;
+ ov.references = env->references;
+ ov.optional.octets = elt->rfc822_size;
+ (*ofn) (stream,mail_uid (stream,i),&ov,i);
+ }
+}
+
+/* Mail fetch message structure
+ * Accepts: mail stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags)
+{
+ ENVELOPE **env;
+ BODY **b;
+ MESSAGECACHE *elt;
+ char c,*s,*hdr;
+ unsigned long hdrsize;
+ STRING bs;
+ /* do the driver's action if specified */
+ if (stream->dtb && stream->dtb->structure)
+ return (*stream->dtb->structure) (stream,msgno,body,flags);
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get elt for real message number */
+ if (stream->scache) { /* short caching */
+ if (msgno != stream->msgno){/* garbage collect if not same message */
+ mail_gc (stream,GC_ENV | GC_TEXTS);
+ stream->msgno = msgno; /* this is the current message now */
+ }
+ env = &stream->env; /* get pointers to envelope and body */
+ b = &stream->body;
+ }
+ else { /* get pointers to elt envelope and body */
+ env = &elt->private.msg.env;
+ b = &elt->private.msg.body;
+ }
+
+ if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) {
+ mail_free_envelope (env); /* flush old envelope and body */
+ mail_free_body (b);
+ /* see if need to fetch the whole thing */
+ if (body || !elt->rfc822_size) {
+ s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL);
+ /* make copy in case body fetch smashes it */
+ hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize);
+ hdr[hdrsize] = '\0'; /* tie off header */
+ (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK);
+ if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs);
+ if (body) /* only parse body if requested */
+ rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags);
+ else
+ rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
+ fs_give ((void **) &hdr); /* flush header */
+ }
+ else { /* can save memory doing it this way */
+ hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL);
+ if (hdrsize) { /* in case null header */
+ c = hdr[hdrsize]; /* preserve what's there */
+ hdr[hdrsize] = '\0'; /* tie off header */
+ rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
+ hdr[hdrsize] = c; /* restore in case cached data */
+ }
+ else *env = mail_newenvelope ();
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ if (body) *body = *b; /* return the body */
+ return *env; /* return the envelope */
+}
+
+/* Mail mark single message (internal use only)
+ * Accepts: mail stream
+ * elt to mark
+ * fetch flags
+ */
+
+static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags)
+{
+ unsigned long i;
+ char sequence[20];
+ MESSAGECACHE *e;
+ /* non-peeking and needs to set \Seen? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ if (stream->dtb->flagmsg){ /* driver wants per-message call? */
+ elt->valid = NIL; /* do pre-alteration driver call */
+ (*stream->dtb->flagmsg) (stream,elt);
+ /* set seen, do post-alteration driver call */
+ elt->seen = elt->valid = T;
+ (*stream->dtb->flagmsg) (stream,elt);
+ }
+ if (stream->dtb->flag) { /* driver wants one-time call? */
+ /* better safe than sorry, save seq bits */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ e = mail_elt (stream,i);
+ e->private.sequence = e->sequence;
+ }
+ /* call driver to set the message */
+ sprintf (sequence,"%lu",elt->msgno);
+ (*stream->dtb->flag) (stream,sequence,"\\Seen",ST_SET);
+ /* restore sequence bits */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ e = mail_elt (stream,i);
+ e->sequence = e->private.sequence;
+ }
+ }
+ /* notify mail program of flag change */
+ MM_FLAGS (stream,elt->msgno);
+ }
+}
+
+/* Mail fetch message
+ * Accepts: mail stream
+ * message # to fetch
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ SIZEDTEXT *t;
+ STRING bs;
+ MESSAGECACHE *elt;
+ char *s,*u;
+ unsigned long i,j;
+ if (len) *len = 0; /* default return size */
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,"",0,0);
+ /* is data already cached? */
+ if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) {
+ markseen (stream,elt,flags);/* mark message seen */
+ return mail_fetch_text_return (&md,t,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ?
+ mail_fetch_text_return (&md,t,len) : "";
+ /* ugh, have to do this the crufty way */
+ u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags);
+ /* copy in case text method stomps on it */
+ s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i);
+ if ((*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ t = &stream->text; /* build combined copy */
+ if (t->data) fs_give ((void **) &t->data);
+ t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1);
+ if (!elt->rfc822_size) elt->rfc822_size = t->size;
+ else if (elt->rfc822_size != t->size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Calculated RFC822.SIZE (%lu) != reported size (%lu)",
+ t->size,elt->rfc822_size);
+ mm_log (tmp,WARN); /* bug trap */
+ }
+ memcpy (t->data,s,(size_t) i);
+ for (u = (char *) t->data + i, j = SIZE (&bs); j;) {
+ memcpy (u,bs.curpos,bs.cursize);
+ u += bs.cursize; /* update text */
+ j -= bs.cursize;
+ bs.curpos += (bs.cursize -1);
+ bs.cursize = 0;
+ (*bs.dtb->next) (&bs); /* advance to next buffer's worth */
+ }
+ *u = '\0'; /* tie off data */
+ u = mail_fetch_text_return (&md,t,len);
+ }
+ else u = "";
+ fs_give ((void **) &s); /* finished with copy of header */
+ return u;
+}
+
+/* Mail fetch message header
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * list of lines to fetch
+ * pointer to returned length
+ * flags
+ * Returns: message header in RFC822 format
+ *
+ * Note: never calls a mailgets routine
+ */
+
+char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *lines,unsigned long *len,long flags)
+{
+ STRING bs;
+ BODY *b = NIL;
+ SIZEDTEXT *t = NIL,rt;
+ MESSAGE *m = NIL;
+ MESSAGECACHE *elt;
+ char tmp[MAILTMPLEN];
+ if (len) *len = 0; /* default return size */
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ if (section && *section) { /* nested body header wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return ""; /* lose if no body or not MESSAGE/RFC822 */
+ m = b->nested.msg; /* point to nested message */
+ }
+ /* else top-level message header wanted */
+ else m = &elt->private.msg;
+ if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) {
+ if (lines) textcpy (t = &stream->text,&m->header.text);
+ else t = &m->header.text; /* in cache, and cache is valid */
+ markseen (stream,elt,flags);/* mark message seen */
+ }
+
+ else if (stream->dtb) { /* not in cache, has live driver? */
+ if (stream->dtb->msgdata) { /* has driver section fetch? */
+ /* build driver section specifier */
+ if (section && *section) sprintf (tmp,"%s.HEADER",section);
+ else strcpy (tmp,"HEADER");
+ if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) {
+ t = &m->header.text; /* fetch data */
+ /* don't need to postprocess lines */
+ if (m->lines) lines = NIL;
+ else if (lines) textcpy (t = &stream->text,&m->header.text);
+ }
+ }
+ else if (b) { /* nested body wanted? */
+ if (stream->private.search.text) {
+ rt.data = (unsigned char *) stream->private.search.text +
+ b->nested.msg->header.offset;
+ rt.size = b->nested.msg->header.text.size;
+ t = &rt;
+ }
+ else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) {
+ if ((bs.dtb->next == mail_string_next) && !lines) {
+ rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset;
+ rt.size = b->nested.msg->header.text.size;
+ if (stream->private.search.string)
+ stream->private.search.text = bs.curpos;
+ t = &rt; /* special hack to avoid extra copy */
+ }
+ else textcpyoffstring (t = &stream->text,&bs,
+ b->nested.msg->header.offset,
+ b->nested.msg->header.text.size);
+ }
+ }
+ else { /* top-level header fetch */
+ /* mark message seen */
+ markseen (stream,elt,flags);
+ if (rt.data = (unsigned char *)
+ (*stream->dtb->header) (stream,msgno,&rt.size,flags)) {
+ /* make a safe copy if need to filter */
+ if (lines) textcpy (t = &stream->text,&rt);
+ else t = &rt; /* top level header */
+ }
+ }
+ }
+ if (!t || !t->data) return "";/* error if no string */
+ /* filter headers if requested */
+ if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags);
+ if (len) *len = t->size; /* return size if requested */
+ return (char *) t->data; /* and text */
+}
+
+/* Mail fetch message text
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ MESSAGECACHE *elt;
+ BODY *b = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long i;
+ if (len) *len = 0; /* default return size */
+ memset (&stream->private.string,NIL,sizeof (STRING));
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ if (section && *section) { /* nested body text wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return ""; /* lose if no body or not MESSAGE/RFC822 */
+ p = &b->nested.msg->text; /* point at nested message */
+ /* build IMAP-format section specifier */
+ sprintf (tmp,"%s.TEXT",section);
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ }
+ else { /* top-level message text wanted */
+ p = &elt->private.msg.text;
+ strcpy (tmp,"TEXT");
+ }
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,0,0);
+ if (p->text.data) { /* is data already cached? */
+ markseen (stream,elt,flags);/* mark message seen */
+ return mail_fetch_text_return (&md,&p->text,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)?
+ mail_fetch_text_return (&md,&p->text,len) : "";
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return "";
+ if (section && *section) { /* nested is more complex */
+ SETPOS (&bs,p->offset);
+ i = p->text.size; /* just want this much */
+ }
+ else i = SIZE (&bs); /* want entire text */
+ return mail_fetch_string_return (&md,&bs,i,len,flags);
+}
+
+/* Mail fetch message body part MIME headers
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ char tmp[MAILTMPLEN];
+ if (len) *len = 0; /* default return size */
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ if (!(section && *section && (b = mail_body (stream,msgno,section))))
+ return ""; /* not valid section */
+ /* in cache? */
+ if ((p = &b->mime)->text.data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ if (len) *len = p->text.size;
+ return (char *) p->text.data;
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) { /* has driver fetch? */
+ /* build driver section specifier */
+ sprintf (tmp,"%s.MIME",section);
+ if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) &&
+ p->text.data) {
+ if (len) *len = p->text.size;
+ return (char *) p->text.data;
+ }
+ else return "";
+ }
+ if (len) *len = b->mime.text.size;
+ if (!b->mime.text.size) { /* empty MIME header -- mark seen anyway */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return "";
+ }
+ /* have to get it from offset */
+ if (stream->private.search.text)
+ return stream->private.search.text + b->mime.offset;
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ if (len) *len = 0;
+ return "";
+ }
+ if (bs.dtb->next == mail_string_next) {
+ if (stream->private.search.string) stream->private.search.text = bs.curpos;
+ return bs.curpos + b->mime.offset;
+ }
+ return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size);
+}
+
+/* Mail fetch message body part
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message body
+ */
+
+char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ SIZEDTEXT *t;
+ char *s,tmp[MAILTMPLEN];
+ memset (&stream->private.string,NIL,sizeof (STRING));
+ if (!(section && *section)) /* top-level text wanted? */
+ return mail_fetch_message (stream,msgno,len,flags);
+ else if (strlen (section) > (MAILTMPLEN - 20)) return "";
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,0,0);
+ /* kludge for old section 0 header */
+ if (!strcmp (s = strcpy (tmp,section),"0") ||
+ ((s = strstr (tmp,".0")) && !s[2])) {
+ SIZEDTEXT ht;
+ *s = '\0'; /* tie off section */
+ /* this silly way so it does mailgets */
+ ht.data = (unsigned char *) mail_fetch_header (stream,msgno,
+ tmp[0] ? tmp : NIL,NIL,
+ &ht.size,flags);
+ /* may have UIDs here */
+ md.flags = (flags & FT_UID) ? MG_UID : NIL;
+ return mail_fetch_text_return (&md,&ht,len);
+ }
+ if (len) *len = 0; /* default return size */
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ /* must have body */
+ if (!(b = mail_body (stream,msgno,section))) return "";
+ /* have cached text? */
+ if ((t = &(p = &b->contents)->text)->data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return mail_fetch_text_return (&md,t,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ?
+ mail_fetch_text_return (&md,t,len) : "";
+ if (len) *len = t->size;
+ if (!t->size) { /* empty body part -- mark seen anyway */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return "";
+ }
+ /* copy body from stringstruct offset */
+ if (stream->private.search.text)
+ return stream->private.search.text + p->offset;
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ if (len) *len = 0;
+ return "";
+ }
+ if (bs.dtb->next == mail_string_next) {
+ if (stream->private.search.string) stream->private.search.text = bs.curpos;
+ return bs.curpos + p->offset;
+ }
+ SETPOS (&bs,p->offset);
+ return mail_fetch_string_return (&md,&bs,t->size,len,flags);
+}
+
+/* Mail fetch partial message text
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * offset of first designed byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p = NIL;
+ MESSAGECACHE *elt;
+ STRING bs;
+ BODY *b;
+ char tmp[MAILTMPLEN];
+ unsigned long i;
+ if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ flags &= ~FT_INTERNAL; /* bogus if this is set */
+ if (section && *section) { /* nested body text wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return NIL; /* lose if no body or not MESSAGE/RFC822 */
+ p = &b->nested.msg->text; /* point at nested message */
+ /* build IMAP-format section specifier */
+ sprintf (tmp,"%s.TEXT",section);
+ }
+ else { /* else top-level message text wanted */
+ p = &elt->private.msg.text;
+ strcpy (tmp,"TEXT");
+ }
+
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,tmp,first,last);
+ if (p->text.data) { /* is data already cached? */
+ INIT (&bs,mail_string,p->text.data,i = p->text.size);
+ markseen (stream,elt,flags);/* mark message seen */
+ }
+ else { /* else get data from driver */
+ if (!stream->dtb) return NIL;
+ if (stream->dtb->msgdata) /* driver will handle this */
+ return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags);
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
+ if (section && *section) { /* nexted if more complex */
+ SETPOS (&bs,p->offset); /* offset stringstruct to data */
+ i = p->text.size; /* maximum size of data */
+ }
+ else i = SIZE (&bs); /* just want this much */
+ }
+ if (i <= first) i = first = 0;/* first byte is beyond end of text */
+ /* truncate as needed */
+ else { /* offset and truncate */
+ SETPOS (&bs,first + GETPOS (&bs));
+ i -= first; /* reduced size */
+ if (last && (i > last)) i = last;
+ }
+ /* do the mailgets thing */
+ (*mailgets) (mail_read,&bs,i,&md);
+ return T; /* success */
+}
+
+/* Mail fetch partial message body part
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * offset of first designed byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ SIZEDTEXT *t;
+ unsigned long i;
+ if (!(section && *section)) /* top-level text wanted? */
+ return mail_partial_text (stream,msgno,NIL,first,last,flags);
+ if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ /* must have body */
+ if (!(b = mail_body (stream,msgno,section))) return NIL;
+ flags &= ~FT_INTERNAL; /* bogus if this is set */
+
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,first,last);
+ /* have cached text? */
+ if ((t = &(p = &b->contents)->text)->data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ INIT (&bs,mail_string,t->data,i = t->size);
+ }
+ else { /* else get data from driver */
+ if (!stream->dtb) return NIL;
+ if (stream->dtb->msgdata) /* driver will handle this */
+ return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL,
+ flags);
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
+ if (section && *section) { /* nexted if more complex */
+ SETPOS (&bs,p->offset); /* offset stringstruct to data */
+ i = t->size; /* maximum size of data */
+ }
+ else i = SIZE (&bs); /* just want this much */
+ }
+ if (i <= first) i = first = 0;/* first byte is beyond end of text */
+ else { /* offset and truncate */
+ SETPOS (&bs,first + GETPOS (&bs));
+ i -= first; /* reduced size */
+ if (last && (i > last)) i = last;
+ }
+ /* do the mailgets thing */
+ (*mailgets) (mail_read,&bs,i,&md);
+ return T; /* success */
+}
+
+/* Mail return message text
+ * Accepts: identifier data
+ * sized text
+ * pointer to returned length
+ * Returns: text
+ */
+
+char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len)
+{
+ STRING bs;
+ if (len) *len = t->size; /* return size */
+ if (t->size && mailgets) { /* have to do the mailgets thing? */
+ /* silly but do it anyway for consistency */
+ INIT (&bs,mail_string,t->data,t->size);
+ return (*mailgets) (mail_read,&bs,t->size,md);
+ }
+ return t->size ? (char *) t->data : "";
+}
+
+
+/* Mail return message string
+ * Accepts: identifier data
+ * stringstruct
+ * text length
+ * pointer to returned length
+ * flags
+ * Returns: text, or NIL if stringstruct returned
+ */
+
+char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
+ unsigned long *len,long flags)
+{
+ char *ret = NIL;
+ if (len) *len = i; /* return size */
+ /* return stringstruct hack */
+ if (flags & FT_RETURNSTRINGSTRUCT) {
+ memcpy (&md->stream->private.string,bs,sizeof (STRING));
+ SETPOS (&md->stream->private.string,GETPOS (&md->stream->private.string));
+ }
+ /* have to do the mailgets thing? */
+ else if (mailgets) ret = (*mailgets) (mail_read,bs,i,md);
+ /* special hack to avoid extra copy */
+ else if (bs->dtb->next == mail_string_next) ret = bs->curpos;
+ /* make string copy in memory */
+ else ret = textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i);
+ return ret;
+}
+
+/* Read data from stringstruct
+ * Accepts: stringstruct
+ * size of data to read
+ * buffer to read into
+ * Returns: T, always, stringstruct updated
+ */
+
+long mail_read (void *stream,unsigned long size,char *buffer)
+{
+ unsigned long i;
+ STRING *s = (STRING *) stream;
+ while (size) { /* until satisfied */
+ memcpy (buffer,s->curpos,i = min (s->cursize,size));
+ buffer += i; /* update buffer */
+ size -= i; /* note that we read this much */
+ s->curpos += --i; /* advance that many spaces minus 1 */
+ s->cursize -= i;
+ SNX (s); /* now use SNX to advance the last byte */
+ }
+ return T;
+}
+
+/* Mail fetch UID
+ * Accepts: mail stream
+ * message number
+ * Returns: UID or zero if dead stream
+ */
+
+unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno)
+{
+ unsigned long uid = mail_elt (stream,msgno)->private.uid;
+ return uid ? uid :
+ (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0;
+}
+
+
+/* Mail fetch msgno from UID
+ * Accepts: mail stream
+ * UID
+ * Returns: msgno or zero if failed
+ */
+
+unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid)
+{
+ unsigned long msgno,delta,first,firstuid,last,lastuid,middle,miduid;
+ if (stream->dtb) { /* active stream? */
+ if (stream->dtb->msgno) /* direct way */
+ return (*stream->dtb->msgno) (stream,uid);
+ else if (stream->dtb->uid) {/* indirect way */
+ /* Placeholder for now, since currently there are no drivers which
+ * have a uid method but not a msgno method
+ */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno;
+ }
+ /* binary search since have full map */
+ else for (first = 1,last = stream->nmsgs, delta = (first <= last) ? 1 : 0;
+ delta &&
+ (uid >= (firstuid = mail_elt (stream,first)->private.uid)) &&
+ (uid <= (lastuid = mail_elt (stream,last)->private.uid));) {
+ /* done if match at an endpoint */
+ if (uid == firstuid) return first;
+ if (uid == lastuid) return last;
+ /* have anything between endpoints? */
+ if (delta = ((last - first) / 2)) {
+ if ((miduid = mail_elt (stream,middle = first + delta)->private.uid)
+ == uid)
+ return middle; /* found match in middle */
+ else if (uid < miduid) last = middle - 1;
+ else first = middle + 1;
+ }
+ }
+ }
+ else { /* dead stream, do linear search for UID */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
+ }
+ return 0; /* didn't find the UID anywhere */
+}
+
+/* Mail fetch From string for menu
+ * Accepts: destination string
+ * mail stream
+ * message # to fetch
+ * desired string length
+ * Returns: string of requested length
+ */
+
+void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length)
+{
+ char *t;
+ char tmp[MAILTMPLEN];
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ ADDRESS *adr = env ? env->from : NIL;
+ memset (s,' ',(size_t)length);/* fill it with spaces */
+ s[length] = '\0'; /* tie off with null */
+ /* get first from address from envelope */
+ while (adr && !adr->host) adr = adr->next;
+ if (adr) { /* if a personal name exists use it */
+ if (!(t = adr->personal))
+ sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host);
+ memcpy (s,t,(size_t) min (length,(long) strlen (t)));
+ }
+}
+
+
+/* Mail fetch Subject string for menu
+ * Accepts: destination string
+ * mail stream
+ * message # to fetch
+ * desired string length
+ * Returns: string of no more than requested length
+ */
+
+void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length)
+{
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ memset (s,'\0',(size_t) length+1);
+ /* copy subject from envelope */
+ if (env && env->subject) strncpy (s,env->subject,(size_t) length);
+ else *s = ' '; /* if no subject then just a space */
+}
+
+/* Mail modify flags
+ * Accepts: mail stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf;
+ long f;
+ short nf;
+ if (!stream->dtb) return; /* no-op if no stream */
+ if ((stream->dtb->flagmsg || !stream->dtb->flag) &&
+ ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf))
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int valid : 1;
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.valid = elt->valid; old.seen = elt->seen;
+ old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ elt->valid = NIL; /* prepare for flag alteration */
+ if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ elt->valid = T; /* flags now altered */
+ if ((old.valid != elt->valid) || (old.seen != elt->seen) ||
+ (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,elt->msgno);
+ if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
+ }
+ /* call driver once */
+ if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags);
+}
+
+/* Mail search for messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T if successful, NIL if dead stream, NIL searchpgm or bad charset
+ */
+
+long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags)
+{
+ unsigned long i;
+ long ret = NIL;
+ if (!(flags & SE_RETAIN)) /* clear search vector unless retaining */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
+ if (pgm && stream->dtb) /* must have a search program and driver */
+ ret = (*(stream->dtb->search ? stream->dtb->search : mail_search_default))
+ (stream,charset,pgm,flags);
+ /* flush search program if requested */
+ if (flags & SE_FREE) mail_free_searchpgm (&pgm);
+ return ret;
+}
+
+
+/* Mail search for messages default handler
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T if successful, NIL if bad charset
+ */
+
+long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags)
+{
+ unsigned long i;
+ char *msg;
+ /* make sure that charset is good */
+ if (msg = utf8_badcharset (charset)) {
+ MM_LOG (msg,ERROR); /* output error */
+ fs_give ((void **) &msg);
+ return NIL;
+ }
+ utf8_searchpgm (pgm,charset);
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_search_msg (stream,i,NIL,pgm)) {
+ if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
+ else { /* mark as searched, notify mail program */
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ }
+ return LONGT; /* search completed */
+}
+
+/* Mail ping mailbox
+ * Accepts: mail stream
+ * Returns: stream if still open else NIL
+ */
+
+long mail_ping (MAILSTREAM *stream)
+{
+ unsigned long i,n,uf,len;
+ char *s,*f,tmp[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *snarf;
+ MESSAGECACHE *elt;
+ STRING bs;
+ long ret;
+ /* do driver action */
+ if ((ret = ((stream && stream->dtb) ? (stream->dtb->ping) (stream) : NIL)) &&
+ stream->snarf.name && /* time to snarf? */
+ /* prohibit faster than once/min */
+ (time (0) > (time_t) (stream->snarf.time + min(60,mailsnarfinterval))) &&
+ (snarf = mail_open (NIL,stream->snarf.name,
+ stream->snarf.options | OP_SILENT))) {
+ if ((n = snarf->nmsgs) && /* yes, have messages to snarf? */
+ mail_search_full (snarf,NIL,mail_criteria ("UNDELETED"),SE_FREE)) {
+ for (i = 1; ret && (i <= n); i++) /* for each message */
+ if ((elt = mail_elt (snarf,i))->searched &&
+ (s = mail_fetch_message (snarf,i,&len,FT_PEEK)) && len) {
+ INIT (&bs,mail_string,s,len);
+ if (mailsnarfpreserve) {
+ /* yes, make sure have fast data */
+ if (!elt->valid || !elt->day) {
+ sprintf (tmp,"%lu",n);
+ mail_fetch_fast (snarf,tmp,NIL);
+ }
+ /* initialize flag string */
+ memset (flags,0,MAILTMPLEN);
+ /* output system flags except \Deleted */
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ /* any user flags? */
+ for (uf = elt->user_flags,s = flags + strlen (flags);
+ uf && (f = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ ((MAILTMPLEN - (s - tmp)) > (long) (2 + strlen (f)));
+ s += strlen (s)) sprintf (s," %s",f);
+ ret = mail_append_full (stream,stream->mailbox,flags + 1,
+ mail_date (tmp,elt),&bs);
+ }
+ else ret = mail_append (stream,stream->mailbox,&bs);
+
+ if (ret) { /* did snarf succeed? */
+ /* driver has per-message (or no) flag call */
+ if (snarf->dtb->flagmsg || !snarf->dtb->flag) {
+ elt->valid = NIL; /* prepare for flag alteration */
+ if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
+ /* flags now altered */
+ elt->deleted = elt->seen = elt->valid = T;
+ if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
+ }
+ /* driver has one-time flag call */
+ if (snarf->dtb->flag) {
+ sprintf (tmp,"%lu",i);
+ (*snarf->dtb->flag) (snarf,tmp,"\\Deleted \\Seen",ST_SET);
+ }
+ }
+ else { /* copy failed */
+ sprintf (tmp,"Unable to move message %lu from %s mailbox",
+ i,snarf->dtb->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ }
+ /* expunge the messages */
+ mail_close_full (snarf,n ? CL_EXPUNGE : NIL);
+ stream->snarf.time = (unsigned long) time (0);
+ /* Even if the snarf failed, we don't want to return NIL if the stream
+ * is still alive. Or at least that's what we currently think.
+ */
+ /* redo the driver's action */
+ ret = stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
+ }
+ return ret;
+}
+
+/* Mail check mailbox
+ * Accepts: mail stream
+ */
+
+void mail_check (MAILSTREAM *stream)
+{
+ /* do the driver's action */
+ if (stream->dtb) (*stream->dtb->check) (stream);
+}
+
+
+/* Mail expunge mailbox
+ * Accepts: mail stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options)
+{
+ /* do the driver's action */
+ return stream->dtb ? (*stream->dtb->expunge) (stream,sequence,options) : NIL;
+}
+
+
+/* Mail copy message(s)
+ * Accepts: mail stream
+ * sequence
+ * destination mailbox
+ * flags
+ */
+
+long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options)
+{
+ return stream->dtb ?
+ SAFE_COPY (stream->dtb,stream,sequence,mailbox,options) : NIL;
+}
+
+/* Append data package to use for old single-message mail_append() interface */
+
+typedef struct mail_append_package {
+ char *flags; /* initial flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPENDPACKAGE;
+
+
+/* Single append message string
+ * Accepts: mail stream
+ * package pointer (cast as a void *)
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message to append
+ * Returns: T, always
+ */
+
+static long mail_append_single (MAILSTREAM *stream,void *data,char **flags,
+ char **date,STRING **message)
+{
+ APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+ *flags = ap->flags; /* get desired data from the package */
+ *date = ap->date;
+ *message = ap->message;
+ ap->message = NIL; /* so next callback puts a stop to it */
+ return LONGT; /* always return success */
+}
+
+
+/* Mail append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * initial flags
+ * message internal date
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message)
+{
+ APPENDPACKAGE ap;
+ ap.flags = flags; /* load append package */
+ ap.date = date;
+ ap.message = message;
+ return mail_append_multiple (stream,mailbox,mail_append_single,(void *) &ap);
+}
+
+/* Mail append message(s)
+ * Accepts: mail stream
+ * destination mailbox
+ * append data callback
+ * arbitrary data for callback use
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
+ void *data)
+{
+ char *s,tmp[MAILTMPLEN];
+ DRIVER *d = NIL;
+ long ret = NIL;
+ /* never allow names with newlines */
+ if (strpbrk (mailbox,"\015\012"))
+ MM_LOG ("Can't append to mailbox with such a name",ERROR);
+ else if (strlen (mailbox) >=
+ (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
+ sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ }
+ /* special driver hack? */
+ else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) {
+ /* yes, tie off name at likely delimiter */
+ if (!(s = strpbrk (tmp+8,"/\\:"))) {
+ sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ *s++ = '\0'; /* tie off at delimiter */
+ if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) {
+ sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data);
+ }
+ else if (d = mail_valid (stream,mailbox,NIL))
+ ret = SAFE_APPEND (d,stream,mailbox,af,data);
+ /* No driver, try for TRYCREATE if no stream. Note that we use the
+ * createProto here, not the appendProto, since the dummy driver already
+ * took care of the appendProto case. Otherwise, if appendProto is set to
+ * NIL, we won't get a TRYCREATE.
+ */
+ else if (!stream && (stream = default_proto (NIL)) && stream->dtb &&
+ SAFE_APPEND (stream->dtb,stream,mailbox,af,data))
+ /* timing race? */
+ MM_NOTIFY (stream,"Append validity confusion",WARN);
+ /* generate error message */
+ else mail_valid (stream,mailbox,"append to mailbox");
+ return ret;
+}
+
+/* Mail garbage collect stream
+ * Accepts: mail stream
+ * garbage collection flags
+ */
+
+void mail_gc (MAILSTREAM *stream,long gcflags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* do the driver's action first */
+ if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags);
+ stream->msgno = 0; /* nothing cached now */
+ if (gcflags & GC_ENV) { /* garbage collect envelopes? */
+ if (stream->env) mail_free_envelope (&stream->env);
+ if (stream->body) mail_free_body (&stream->body);
+ }
+ if (gcflags & GC_TEXTS) { /* free texts */
+ if (stream->text.data) fs_give ((void **) &stream->text.data);
+ stream->text.size = 0;
+ }
+ /* garbage collect per-message stuff */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT))
+ mail_gc_msg (&elt->private.msg,gcflags);
+}
+
+
+/* Mail garbage collect message
+ * Accepts: message structure
+ * garbage collection flags
+ */
+
+void mail_gc_msg (MESSAGE *msg,long gcflags)
+{
+ if (gcflags & GC_ENV) { /* garbage collect envelopes? */
+ mail_free_envelope (&msg->env);
+ mail_free_body (&msg->body);
+ }
+ if (gcflags & GC_TEXTS) { /* garbage collect texts */
+ if (msg->full.text.data) fs_give ((void **) &msg->full.text.data);
+ if (msg->header.text.data) {
+ mail_free_stringlist (&msg->lines);
+ fs_give ((void **) &msg->header.text.data);
+ }
+ if (msg->text.text.data) fs_give ((void **) &msg->text.text.data);
+ /* now GC all body components */
+ if (msg->body) mail_gc_body (msg->body);
+ }
+}
+
+/* Mail garbage collect texts in BODY structure
+ * Accepts: BODY structure
+ */
+
+void mail_gc_body (BODY *body)
+{
+ PART *part;
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ for (part = body->nested.part; part; part = part->next)
+ mail_gc_body (&part->body);
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ if (body->subtype && !strcmp (body->subtype,"RFC822")) {
+ mail_free_stringlist (&body->nested.msg->lines);
+ mail_gc_msg (body->nested.msg,GC_TEXTS);
+ }
+ break;
+ default:
+ break;
+ }
+ if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
+ if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
+}
+
+/* Mail get body part
+ * Accepts: mail stream
+ * message number
+ * section specifier
+ * Returns: pointer to body
+ */
+
+BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,unsigned char *section)
+{
+ BODY *b = NIL;
+ PART *pt;
+ unsigned long i;
+ /* make sure have a body */
+ if (section && *section && mail_fetchstructure (stream,msgno,&b) && b)
+ while (*section) { /* find desired section */
+ if (isdigit (*section)) { /* get section specifier */
+ /* make sure what follows is valid */
+ if (!(i = strtoul (section,(char **) &section,10)) ||
+ (*section && ((*section++ != '.') || !*section))) return NIL;
+ /* multipart content? */
+ if (b->type == TYPEMULTIPART) {
+ /* yes, find desired part */
+ if (pt = b->nested.part) while (--i && (pt = pt->next));
+ if (!pt) return NIL; /* bad specifier */
+ b = &pt->body; /* note new body */
+ }
+ /* otherwise must be section 1 */
+ else if (i != 1) return NIL;
+ /* need to go down further? */
+ if (*section) switch (b->type) {
+ case TYPEMULTIPART: /* multipart */
+ break;
+ case TYPEMESSAGE: /* embedded message */
+ if (!strcmp (b->subtype,"RFC822")) {
+ b = b->nested.msg->body;
+ break;
+ }
+ default: /* bogus subpart specification */
+ return NIL;
+ }
+ }
+ else return NIL; /* unknown section specifier */
+ }
+ return b;
+}
+
+/* Mail output date from elt fields
+ * Accepts: character string to write into
+ * elt to get data data from
+ * Returns: the character string
+ */
+
+const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+char *mail_date (char *string,MESSAGECACHE *elt)
+{
+ sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
+ elt->day ? elt->day : 1,
+ months[elt->month ? (elt->month - 1) : 0],
+ elt->year + BASEYEAR,elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
+ return string;
+}
+
+
+/* Mail output extended-ctime format date from elt fields
+ * Accepts: character string to write into
+ * elt to get data data from
+ * Returns: the character string
+ */
+
+char *mail_cdate (char *string,MESSAGECACHE *elt)
+{
+ char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02d\n";
+ int d = elt->day ? elt->day : 1;
+ int m = elt->month ? (elt->month - 1) : 0;
+ int y = elt->year + BASEYEAR;
+ const char *s = months[m];
+ if (m < 2) { /* if before March, */
+ m += 10; /* January = month 10 of previous year */
+ y--;
+ }
+ else m -= 2; /* March is month 0 */
+ sprintf (string,fmt,days[(int) (d + 2 + ((7 + 31 * m) / 12)
+#ifndef USEJULIANCALENDAR
+#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
+ + (y / 400)
+#ifdef Y4KBUGFIX
+ - (y / 4000)
+#endif
+#else /* Orthodox calendar */
+ + (2 * (y / 900)) + ((y % 900) >= 200)
+ + ((y % 900) >= 600)
+#endif
+ - (y / 100)
+#endif
+ + y + (y / 4)) % 7],
+ s,d,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR,
+ elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes);
+ return string;
+}
+
+/* Mail parse date into elt fields
+ * Accepts: elt to write into
+ * date string to parse
+ * Returns: T if parse successful, else NIL
+ * This routine parses dates as follows:
+ * . leading three alphas followed by comma and space are ignored
+ * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
+ * dd mmm yy, dd mmm yyyy, yyyy-mm-dd, yyyymmdd
+ * . two and three digit years interpreted according to RFC 2822 rules
+ * . mandatory end of string if yyyy-mm-dd or yyyymmdd; otherwise optional
+ * space followed by time:
+ * . time accepted in format hh:mm:ss or hh:mm
+ * . end of string accepted
+ * . timezone accepted: hyphen followed by symbolic timezone, or space
+ * followed by signed numeric timezone or symbolic timezone
+ * Examples of normal input:
+ * . IMAP date-only (SEARCH):
+ * dd-mmm-yyyy
+ * . IMAP date-time (INTERNALDATE):
+ * dd-mmm-yyyy hh:mm:ss +zzzz
+ * . RFC-822:
+ * www, dd mmm yy hh:mm:ss zzz
+ * . RFC-2822:
+ * www, dd mmm yyyy hh:mm:ss +zzzz
+ */
+
+long mail_parse_date (MESSAGECACHE *elt,unsigned char *s)
+{
+ unsigned long d,m,y;
+ int mi,ms;
+ struct tm *t;
+ time_t tn;
+ char tmp[MAILTMPLEN];
+ static unsigned long maxyear = 0;
+ if (!maxyear) { /* know the end of time yet? */
+ MESSAGECACHE tmpelt;
+ memset (&tmpelt,0xff,sizeof (MESSAGECACHE));
+ maxyear = BASEYEAR + tmpelt.year;
+ }
+ /* clear elt */
+ elt->zoccident = elt->zhours = elt->zminutes =
+ elt->hours = elt->minutes = elt->seconds =
+ elt->day = elt->month = elt->year = 0;
+ /* make a writeable uppercase copy */
+ if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s));
+ else return NIL;
+ /* skip over possible day of week */
+ if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
+ (s[4] == ' ')) s += 5;
+ while (*s == ' ') s++; /* parse first number (probable month) */
+ if (!(m = strtoul (s,(char **) &s,10))) return NIL;
+
+ switch (*s) { /* different parse based on delimiter */
+ case '/': /* mm/dd/yy format */
+ if (isdigit (*++s) && (d = strtoul (s,(char **) &s,10)) &&
+ (*s == '/') && isdigit (*++s)) {
+ y = strtoul (s,(char **) &s,10);
+ if (*s == '\0') break; /* must end here */
+ }
+ return NIL; /* bogon */
+ case ' ': /* dd mmm yy format */
+ while (s[1] == ' ') s++; /* slurp extra whitespace */
+ case '-':
+ if (isdigit (s[1])) { /* possible ISO 8601 date format? */
+ y = m; /* yes, first number is year */
+ /* get month and day */
+ if ((m = strtoul (s+1,(char **) &s,10)) && (*s++ == '-') &&
+ (d = strtoul (s,(char **) &s,10)) && !*s) break;
+ return NIL; /* syntax error or time present */
+ }
+ d = m; /* dd-mmm-yy[yy], so first number is a day */
+ /* make sure string long enough! */
+ if (strlen (s) < (size_t) 5) return NIL;
+ /* Some compilers don't allow `<<' and/or longs in case statements. */
+ /* slurp up the month string */
+ ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
+ switch (ms) { /* determine the month */
+ case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
+ case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
+ case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
+ case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
+ case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
+ case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
+ case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
+ case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
+ default: return NIL; /* unknown month */
+ }
+ if (s[4] == *s) s += 5; /* advance to year */
+ else { /* first three were OK, possibly full name */
+ mi = *s; /* note delimiter, skip alphas */
+ for (s += 4; isalpha (*s); s++);
+ /* error if delimiter not here */
+ if (mi != *s++) return NIL;
+ }
+ while (*s == ' ') s++; /* parse year */
+ if (isdigit (*s)) { /* must be a digit here */
+ y = strtoul (s,(char **) &s,10);
+ if (*s == '\0' || *s == ' ') break;
+ }
+ case '\0': /* ISO 8601 compact date */
+ if (m < (BASEYEAR * 10000)) return NIL;
+ y = m / 10000; /* get year */
+ d = (m %= 10000) % 100; /* get day */
+ m /= 100; /* and month */
+ break;
+ default:
+ return NIL; /* unknown date format */
+ }
+
+ /* minimal validity check of date */
+ if ((d > 31) || (m > 12)) return NIL;
+ if (y < 49) y += 2000; /* RFC 2282 rules for two digit years 00-49 */
+ else if (y < 999) y += 1900; /* 2-digit years 50-99 and 3-digit years */
+ /* reject prehistoric and far future years */
+ if ((y < BASEYEAR) || (y > maxyear)) return NIL;
+ /* set values in elt */
+ elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
+ ms = '\0'; /* initially no time zone string */
+ if (*s) { /* time specification present? */
+ /* parse time */
+ d = strtoul (s+1,(char **) &s,10);
+ if (*s != ':') return NIL;
+ m = strtoul (++s,(char **) &s,10);
+ y = (*s == ':') ? strtoul (++s,(char **) &s,10) : 0;
+ /* validity check time */
+ if ((d > 23) || (m > 59) || (y > 60)) return NIL;
+ /* set values in elt */
+ elt->hours = d; elt->minutes = m; elt->seconds = y;
+ switch (*s) { /* time zone specifier? */
+ case ' ': /* numeric time zone */
+ while (s[1] == ' ') s++; /* slurp extra whitespace */
+ if (!isalpha (s[1])) { /* treat as '-' case if alphabetic */
+ /* test for sign character */
+ if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
+ /* validate proper timezone */
+ if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && (s[2] < '6') &&
+ isdigit(s[3])) {
+ elt->zhours = (*s - '0') * 10 + (s[1] - '0');
+ elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
+ }
+ return T; /* all done! */
+ }
+ /* falls through */
+ case '-': /* symbolic time zone */
+ if (!(ms = *++s)) ms = 'Z';
+ else if (*++s) { /* multi-character? */
+ ms -= 'A'; ms *= 1024; /* yes, make compressed three-byte form */
+ ms += ((*s++ - 'A') * 32);
+ if (*s) ms += *s++ - 'A';
+ if (*s) ms = '\0'; /* more than three characters */
+ }
+ default: /* ignore anything else */
+ break;
+ }
+ }
+
+ /* This is not intended to be a comprehensive list of all possible
+ * timezone strings. Such a list would be impractical. Rather, this
+ * listing is intended to incorporate all military, North American, and
+ * a few special cases such as Japan and the major European zone names,
+ * such as what might be expected to be found in a Tenex format mailbox
+ * and spewed from an IMAP server. The trend is to migrate to numeric
+ * timezones which lack the flavor but also the ambiguity of the names.
+ *
+ * RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the
+ * 4 CONUS timezones and their summer time variants. [Sorry, Canadian
+ * Atlantic Provinces, Alaska, and Hawaii.]
+ */
+ switch (ms) { /* determine the timezone */
+ /* Universal */
+ case (('U'-'A')*1024)+(('T'-'A')*32):
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A':
+#endif
+ /* Greenwich */
+ case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
+ case 'Z': elt->zhours = 0; break;
+
+ /* oriental (from Greenwich) timezones */
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Middle Europe */
+ case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
+#endif
+#ifdef BRITISH_SUMMER_TIME
+ /* British Summer */
+ case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'A': elt->zhours = 1; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Eastern Europe */
+ case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
+#endif
+ case 'B': elt->zhours = 2; break;
+ case 'C': elt->zhours = 3; break;
+ case 'D': elt->zhours = 4; break;
+ case 'E': elt->zhours = 5; break;
+ case 'F': elt->zhours = 6; break;
+ case 'G': elt->zhours = 7; break;
+ case 'H': elt->zhours = 8; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Japan */
+ case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'I': elt->zhours = 9; break;
+ case 'K': elt->zhours = 10; break;
+ case 'L': elt->zhours = 11; break;
+ case 'M': elt->zhours = 12; break;
+
+ /* occidental (from Greenwich) timezones */
+ case 'N': elt->zoccident = 1; elt->zhours = 1; break;
+ case 'O': elt->zoccident = 1; elt->zhours = 2; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+#endif
+ case 'P': elt->zoccident = 1; elt->zhours = 3; break;
+#ifdef NEWFOUNDLAND_STANDARD_TIME
+ /* Newfoundland */
+ case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break;
+#endif
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Atlantic */
+ case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ /* CONUS */
+ case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
+ /* Eastern */
+ case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'R': elt->zoccident = 1; elt->zhours = 5; break;
+ /* Central */
+ case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'S': elt->zoccident = 1; elt->zhours = 6; break;
+ /* Mountain */
+ case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'T': elt->zoccident = 1; elt->zhours = 7; break;
+ /* Pacific */
+ case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+#endif
+ case 'U': elt->zoccident = 1; elt->zhours = 8; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Yukon */
+ case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'V': elt->zoccident = 1; elt->zhours = 9; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Hawaii */
+ case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'W': elt->zoccident = 1; elt->zhours = 10; break;
+ /* Nome/Bering/Samoa */
+#ifdef NOME_STANDARD_TIME
+ case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+#ifdef BERING_STANDARD_TIME
+ case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+#ifdef SAMOA_STANDARD_TIME
+ case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'X': elt->zoccident = 1; elt->zhours = 11; break;
+ case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
+
+ default: /* unknown time zones treated as local */
+ tn = time (0); /* time now... */
+ t = localtime (&tn); /* get local minutes since midnight */
+ mi = t->tm_hour * 60 + t->tm_min;
+ ms = t->tm_yday; /* note Julian day */
+ if (t = gmtime (&tn)) { /* minus UTC minutes since midnight */
+ mi -= t->tm_hour * 60 + t->tm_min;
+ /* ms can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (ms -= t->tm_yday) /* correct offset if different Julian day */
+ mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
+ if (mi < 0) { /* occidental? */
+ mi = abs (mi); /* yup, make positive number */
+ elt->zoccident = 1; /* and note west of UTC */
+ }
+ elt->zhours = mi / 60; /* now break into hours and minutes */
+ elt->zminutes = mi % 60;
+ }
+ break;
+ }
+ return T;
+}
+
+/* Mail n messages exist
+ * Accepts: mail stream
+ * number of messages
+ */
+
+void mail_exists (MAILSTREAM *stream,unsigned long nmsgs)
+{
+ char tmp[MAILTMPLEN];
+ if (nmsgs > MAXMESSAGES) {
+ sprintf (tmp,"Mailbox has more messages (%lu) exist than maximum (%lu)",
+ nmsgs,MAXMESSAGES);
+ mm_log (tmp,ERROR);
+ nmsgs = MAXMESSAGES; /* cap to maximum */
+ /* probably will crash in mail_elt() soon enough... */
+ }
+ /* make sure cache is large enough */
+ (*mailcache) (stream,nmsgs,CH_SIZE);
+ stream->nmsgs = nmsgs; /* update stream status */
+ /* notify main program of change */
+ if (!stream->silent) MM_EXISTS (stream,nmsgs);
+}
+
+
+/* Mail n messages are recent
+ * Accepts: mail stream
+ * number of recent messages
+ */
+
+void mail_recent (MAILSTREAM *stream,unsigned long recent)
+{
+ char tmp[MAILTMPLEN];
+ if (recent <= stream->nmsgs) stream->recent = recent;
+ else {
+ sprintf (tmp,"Non-existent recent message(s) %lu, nmsgs=%lu",
+ recent,stream->nmsgs);
+ mm_log (tmp,ERROR);
+ }
+}
+
+
+/* Mail message n is expunged
+ * Accepts: mail stream
+ * message #
+ */
+
+void mail_expunged (MAILSTREAM *stream,unsigned long msgno)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (msgno > stream->nmsgs) {
+ sprintf (tmp,"Expunge of non-existent message %lu, nmsgs=%lu",
+ msgno,stream->nmsgs);
+ mm_log (tmp,ERROR);
+ }
+ else {
+ elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
+ /* notify main program of change */
+ if (!stream->silent) MM_EXPUNGED (stream,msgno);
+ if (elt) { /* if an element is there */
+ elt->msgno = 0; /* invalidate its message number and free */
+ (*mailcache) (stream,msgno,CH_FREE);
+ (*mailcache) (stream,msgno,CH_FREESORTCACHE);
+ }
+ /* expunge the slot */
+ (*mailcache) (stream,msgno,CH_EXPUNGE);
+ --stream->nmsgs; /* update stream status */
+ if (stream->msgno) { /* have stream pointers? */
+ /* make sure the short cache is nuked */
+ if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS);
+ else stream->msgno = 0; /* make sure invalidated in any case */
+ }
+ }
+}
+
+/* Mail stream status routines */
+
+
+/* Mail lock stream
+ * Accepts: mail stream
+ */
+
+void mail_lock (MAILSTREAM *stream)
+{
+ if (stream->lock) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Lock when already locked, mbx=%.80s",
+ stream->mailbox ? stream->mailbox : "???");
+ fatal (tmp);
+ }
+ else stream->lock = T; /* lock stream */
+}
+
+
+/* Mail unlock stream
+ * Accepts: mail stream
+ */
+
+void mail_unlock (MAILSTREAM *stream)
+{
+ if (!stream->lock) fatal ("Unlock when not locked");
+ else stream->lock = NIL; /* unlock stream */
+}
+
+
+/* Mail turn on debugging telemetry
+ * Accepts: mail stream
+ */
+
+void mail_debug (MAILSTREAM *stream)
+{
+ stream->debug = T; /* turn on debugging telemetry */
+ if (stream->dtb) (*stream->dtb->parameters) (ENABLE_DEBUG,stream);
+}
+
+
+/* Mail turn off debugging telemetry
+ * Accepts: mail stream
+ */
+
+void mail_nodebug (MAILSTREAM *stream)
+{
+ stream->debug = NIL; /* turn off debugging telemetry */
+ if (stream->dtb) (*stream->dtb->parameters) (DISABLE_DEBUG,stream);
+}
+
+
+/* Mail log to debugging telemetry
+ * Accepts: message
+ * flag that data is "sensitive"
+ */
+
+void mail_dlog (char *string,long flag)
+{
+ mm_dlog ((debugsensitive || !flag) ? string : "<suppressed>");
+}
+
+/* Mail parse UID sequence
+ * Accepts: mail stream
+ * sequence to parse
+ * Returns: T if parse successful, else NIL
+ */
+
+long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence)
+{
+ unsigned long i,j,k,x,y;
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
+ while (sequence && *sequence){/* while there is something to parse */
+ if (*sequence == '*') { /* maximum message */
+ i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
+ sequence++; /* skip past * */
+ }
+ /* parse and validate message number */
+ /* parse and validate message number */
+ else if (!isdigit (*sequence)) {
+ MM_LOG ("Syntax error in sequence",ERROR);
+ return NIL;
+ }
+ else if (!(i = strtoul (sequence,(char **) &sequence,10))) {
+ MM_LOG ("UID may not be zero",ERROR);
+ return NIL;
+ }
+ switch (*sequence) { /* see what the delimiter is */
+ case ':': /* sequence range */
+ if (*++sequence == '*') { /* maximum message */
+ j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
+ sequence++; /* skip past * */
+ }
+ /* parse end of range */
+ else if (!(j = strtoul (sequence,(char **) &sequence,10))) {
+ MM_LOG ("UID sequence range invalid",ERROR);
+ return NIL;
+ }
+ if (*sequence && *sequence++ != ',') {
+ MM_LOG ("UID sequence range syntax error",ERROR);
+ return NIL;
+ }
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ x = mail_msgno (stream,i);/* get msgnos */
+ y = mail_msgno (stream,j);/* for both UIDS (don't && it) */
+ /* easy if both UIDs valid */
+ if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T;
+ /* start UID valid, end is not */
+ else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j))
+ mail_elt (stream,x++)->sequence = T;
+ /* end UID valid, start is not */
+ else if (y) for (x = 1; x <= y; x++) {
+ if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T;
+ }
+ /* neither is valid, ugh */
+ else for (x = 1; x <= stream->nmsgs; x++)
+ if (((k = mail_uid (stream,x)) >= i) && (k <= j))
+ mail_elt (stream,x)->sequence = T;
+ break;
+ case ',': /* single message */
+ ++sequence; /* skip the delimiter, fall into end case */
+ case '\0': /* end of sequence, mark this message */
+ if (x = mail_msgno (stream,i)) mail_elt (stream,x)->sequence = T;
+ break;
+ default: /* anything else is a syntax error! */
+ MM_LOG ("UID sequence syntax error",ERROR);
+ return NIL;
+ }
+ }
+ return T; /* successfully parsed sequence */
+}
+
+/* Mail see if line list matches that in cache
+ * Accepts: candidate line list
+ * cached line list
+ * matching flags
+ * Returns: T if match, NIL if no match
+ */
+
+long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ STRINGLIST *m;
+ if (!msglines) return T; /* full header is in cache */
+ /* need full header but filtered in cache */
+ if ((flags & FT_NOT) || !lines) return NIL;
+ do { /* make sure all present & accounted for */
+ for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) {
+ for (s = lines->text.data,t = m->text.data,i = lines->text.size;
+ i && !compare_uchar (*s,*t); s++,t++,i--);
+ if (!i) break; /* this line matches */
+ }
+ if (!m) return NIL; /* didn't find in the list */
+ }
+ while (lines = lines->next);
+ return T; /* all lines found */
+}
+
+/* Mail filter text by header lines
+ * Accepts: text to filter, with trailing null
+ * length of text
+ * list of lines
+ * fetch flags
+ * Returns: new text size, text overwritten
+ */
+
+unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
+ long flags)
+{
+ STRINGLIST *hdrs;
+ int notfound;
+ unsigned long i;
+ char c,*s,*e,*t,tmp[MAILTMPLEN];
+ char *src = text;
+ char *dst = src;
+ char *end = text + len;
+ text[len] = '\012'; /* guard against running off buffer */
+ while (src < end) { /* process header */
+ /* slurp header line name */
+ for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp;
+ (s < e) && ((c = (*s ? *s : (*s = ' '))) != ':') &&
+ ((c > ' ') ||
+ ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012')));
+ *t++ = *s++);
+ *t = '\0'; /* tie off */
+ notfound = T; /* not found yet */
+ if (i = t - tmp) /* see if found in header */
+ for (hdrs = lines; hdrs && notfound; hdrs = hdrs->next)
+ if ((hdrs->text.size == i) && !compare_csizedtext (tmp,&hdrs->text))
+ notfound = NIL;
+ /* skip header line if not wanted */
+ if (i && ((flags & FT_NOT) ? !notfound : notfound))
+ while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012')) || ((*src == ' ') || (*src == '\t')));
+ else if (src == dst) { /* copy to self */
+ while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012')) || ((*src == ' ') || (*src == '\t')));
+ dst = src; /* update destination */
+ }
+ else { /* copy line and any continuation line */
+ while ((((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012'))||
+ ((*src == ' ') || (*src == '\t')));
+ /* in case hit the guard LF */
+ if (src > end) dst -= (src - end);
+ }
+ }
+ *dst = '\0'; /* tie off destination */
+ return dst - text;
+}
+
+/* Local mail search message
+ * Accepts: MAIL stream
+ * message number
+ * optional section specification
+ * search program
+ * Returns: T if found, NIL otherwise
+ */
+
+long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
+ SEARCHPGM *pgm)
+{
+ unsigned short d;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ SEARCHHEADER *hdr;
+ SEARCHOR *or;
+ SEARCHPGMLIST *not;
+ unsigned long now = (unsigned long) time (0);
+ if (pgm->msgno || pgm->uid) { /* message set searches */
+ SEARCHSET *set;
+ /* message sequences */
+ if (pgm->msgno) { /* inside this message sequence set */
+ for (set = pgm->msgno; set; set = set->next)
+ if (set->last ? ((set->first <= set->last) ?
+ ((msgno >= set->first) && (msgno <= set->last)) :
+ ((msgno >= set->last) && (msgno <= set->first))) :
+ msgno == set->first) break;
+ if (!set) return NIL; /* not found within sequence */
+ }
+ if (pgm->uid) { /* inside this unique identifier set */
+ unsigned long uid = mail_uid (stream,msgno);
+ for (set = pgm->uid; set; set = set->next)
+ if (set->last ? ((set->first <= set->last) ?
+ ((uid >= set->first) && (uid <= set->last)) :
+ ((uid >= set->last) && (uid <= set->first))) :
+ uid == set->first) break;
+ if (!set) return NIL; /* not found within sequence */
+ }
+ }
+
+ /* Fast data searches */
+ /* need to fetch fast data? */
+ if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) ||
+ (!elt->year && (pgm->before || pgm->on || pgm->since ||
+ pgm->older || pgm->younger)) ||
+ (!elt->valid && (pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted ||
+ pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged ||
+ pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword))) {
+ unsigned long i;
+ MESSAGECACHE *ielt;
+ for (i = elt->msgno; /* find last unloaded message in range */
+ (i < stream->nmsgs) && (ielt = mail_elt (stream,i+1)) &&
+ ((!ielt->rfc822_size && (pgm->larger || pgm->smaller)) ||
+ (!ielt->year && (pgm->before || pgm->on || pgm->since ||
+ pgm->older || pgm->younger)) ||
+ (!ielt->valid && (pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted ||
+ pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged ||
+ pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword))); ++i);
+ if (i == elt->msgno) sprintf (tmp,"%lu",elt->msgno);
+ else sprintf (tmp,"%lu:%lu",elt->msgno,i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* size ranges */
+ if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) ||
+ (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL;
+ /* message flags */
+ if ((pgm->answered && !elt->answered) ||
+ (pgm->unanswered && elt->answered) ||
+ (pgm->deleted && !elt->deleted) ||
+ (pgm->undeleted && elt->deleted) ||
+ (pgm->draft && !elt->draft) ||
+ (pgm->undraft && elt->draft) ||
+ (pgm->flagged && !elt->flagged) ||
+ (pgm->unflagged && elt->flagged) ||
+ (pgm->recent && !elt->recent) ||
+ (pgm->old && elt->recent) ||
+ (pgm->seen && !elt->seen) ||
+ (pgm->unseen && elt->seen)) return NIL;
+ /* keywords */
+ if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
+ (pgm->unkeyword && !mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
+ return NIL;
+ /* internal date ranges */
+ if (pgm->before || pgm->on || pgm->since) {
+ d = mail_shortdate (elt->year,elt->month,elt->day);
+ if (pgm->before && (d >= pgm->before)) return NIL;
+ if (pgm->on && (d != pgm->on)) return NIL;
+ if (pgm->since && (d < pgm->since)) return NIL;
+ }
+ if (pgm->older || pgm->younger) {
+ unsigned long msgd = mail_longdate (elt);
+ if (pgm->older && msgd > (now - pgm->older)) return NIL;
+ if (pgm->younger && msgd < (now - pgm->younger)) return NIL;
+ }
+
+ /* envelope searches */
+ if (pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject ||
+ pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to ||
+ pgm->message_id || pgm->newsgroups || pgm->followup_to ||
+ pgm->references) {
+ ENVELOPE *env;
+ MESSAGECACHE delt;
+ if (section) { /* use body part envelope */
+ BODY *body = mail_body (stream,msgno,section);
+ env = (body && (body->type == TYPEMESSAGE) && body->subtype &&
+ !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL;
+ }
+ else { /* use top level envelope if no section */
+ if (pgm->header && !stream->scache && !(stream->dtb->flags & DR_LOCAL))
+ mail_fetch_header(stream,msgno,NIL,NIL,NIL,FT_PEEK|FT_SEARCHLOOKAHEAD);
+ env = mail_fetchenvelope (stream,msgno);
+ }
+ if (!env) return NIL; /* no envelope obtained */
+ /* sent date ranges */
+ if ((pgm->sentbefore || pgm->senton || pgm->sentsince) &&
+ (!mail_parse_date (&delt,env->date) ||
+ !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
+ (pgm->sentbefore && (d >= pgm->sentbefore)) ||
+ (pgm->senton && (d != pgm->senton)) ||
+ (pgm->sentsince && (d < pgm->sentsince)))) return NIL;
+ /* search headers */
+ if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
+ (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
+ (pgm->from && !mail_search_addr (env->from,pgm->from)) ||
+ (pgm->to && !mail_search_addr (env->to,pgm->to)) ||
+ (pgm->subject && !mail_search_header_text (env->subject,pgm->subject)))
+ return NIL;
+ /* These criteria are not supported by IMAP and have to be emulated */
+ if ((pgm->return_path &&
+ !mail_search_addr (env->return_path,pgm->return_path)) ||
+ (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
+ (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
+ (pgm->in_reply_to &&
+ !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
+ (pgm->message_id &&
+ !mail_search_header_text (env->message_id,pgm->message_id)) ||
+ (pgm->newsgroups &&
+ !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
+ (pgm->followup_to &&
+ !mail_search_header_text (env->followup_to,pgm->followup_to)) ||
+ (pgm->references &&
+ !mail_search_header_text (env->references,pgm->references)))
+ return NIL;
+ }
+
+ /* search header lines */
+ for (hdr = pgm->header; hdr; hdr = hdr->next) {
+ char *t,*e,*v;
+ SIZEDTEXT s;
+ STRINGLIST sth,stc;
+ sth.next = stc.next = NIL; /* only one at a time */
+ sth.text.data = hdr->line.data;
+ sth.text.size = hdr->line.size;
+ /* get the header text */
+ if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
+ FT_INTERNAL | FT_PEEK |
+ (section ? NIL : FT_SEARCHLOOKAHEAD))) &&
+ strchr (t,':')) {
+ if (hdr->text.size) { /* anything matches empty search string */
+ /* non-empty, copy field data */
+ s.data = (unsigned char *) fs_get (s.size + 1);
+ /* for each line */
+ for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
+ default: /* non-continuation, skip leading field name */
+ while ((t < e) && (*t++ != ':'));
+ if ((t < e) && (*t == ':')) t++;
+ case '\t': case ' ': /* copy field data */
+ while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
+ *v++ = '\n'; /* tie off line */
+ while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
+ }
+ /* calculate true size */
+ s.size = v - (char *) s.data;
+ *v = '\0'; /* tie off results */
+ stc.text.data = hdr->text.data;
+ stc.text.size = hdr->text.size;
+ /* search header */
+ if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
+ else { /* search failed */
+ fs_give ((void **) &s.data);
+ return NIL;
+ }
+ }
+ }
+ else return NIL; /* no matching header text */
+ }
+ /* search strings */
+ if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))||
+ (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL)))
+ return NIL;
+ /* logical conditions */
+ for (or = pgm->or; or; or = or->next)
+ if (!(mail_search_msg (stream,msgno,section,or->first) ||
+ mail_search_msg (stream,msgno,section,or->second))) return NIL;
+ for (not = pgm->not; not; not = not->next)
+ if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL;
+ return T;
+}
+
+/* Mail search message header null-terminated text
+ * Accepts: header text
+ * strings to search
+ * Returns: T if search found a match
+ */
+
+long mail_search_header_text (char *s,STRINGLIST *st)
+{
+ SIZEDTEXT h;
+ /* have any text? */
+ if (h.data = (unsigned char *) s) {
+ h.size = strlen (s); /* yes, get its size */
+ return mail_search_header (&h,st);
+ }
+ return NIL;
+}
+
+
+/* Mail search message header
+ * Accepts: header as sized text
+ * strings to search
+ * Returns: T if search found a match
+ */
+
+long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st)
+{
+ SIZEDTEXT h;
+ long ret = LONGT;
+ /* make UTF-8 version of header */
+ utf8_mime2text (hdr,&h,U8T_CANONICAL);
+ while (h.size && ((h.data[h.size-1]=='\015') || (h.data[h.size-1]=='\012')))
+ --h.size; /* slice off trailing newlines */
+ do if (h.size ? /* search non-empty string */
+ !ssearch (h.data,h.size,st->text.data,st->text.size) : st->text.size)
+ ret = NIL;
+ while (ret && (st = st->next));
+ if (h.data != hdr->data) fs_give ((void **) &h.data);
+ return ret;
+}
+
+/* Mail search message body
+ * Accepts: MAIL stream
+ * message number
+ * optional section specification
+ * string list
+ * flags
+ * Returns: T if search found a match
+ */
+
+long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *st,long flags)
+{
+ BODY *body;
+ long ret = NIL;
+ STRINGLIST *s = mail_newstringlist ();
+ mailgets_t omg = mailgets;
+ if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets;
+ /* strings to search */
+ for (stream->private.search.string = s; st;) {
+ s->text.data = st->text.data;
+ s->text.size = st->text.size;
+ if (st = st->next) s = s->next = mail_newstringlist ();
+ }
+ stream->private.search.text = NIL;
+ if (flags) { /* want header? */
+ SIZEDTEXT s,t;
+ s.data = (unsigned char *)
+ mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK);
+ utf8_mime2text (&s,&t,U8T_CANONICAL);
+ ret = mail_search_string_work (&t,&stream->private.search.string);
+ if (t.data != s.data) fs_give ((void **) &t.data);
+ }
+ if (!ret) { /* still looking for match? */
+ /* no section, get top-level body */
+ if (!section) mail_fetchstructure (stream,msgno,&body);
+ /* get body of nested message */
+ else if ((body = mail_body (stream,msgno,section)) &&
+ (body->type == TYPEMULTIPART) && body->subtype &&
+ !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body;
+ if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags);
+ }
+ mailgets = omg; /* restore former gets routine */
+ /* clear searching */
+ for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL;
+ mail_free_stringlist (&stream->private.search.string);
+ stream->private.search.text = NIL;
+ return ret;
+}
+
+/* Mail search message body text parts
+ * Accepts: MAIL stream
+ * message number
+ * current body pointer
+ * hierarchical level prefix
+ * position at current hierarchical level
+ * string list
+ * flags
+ * Returns: T if search found a match
+ */
+
+long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
+ char *prefix,unsigned long section,long flags)
+{
+ long ret = NIL;
+ unsigned long i;
+ char *s,*t,sect[MAILTMPLEN];
+ SIZEDTEXT st,h;
+ PART *part;
+ PARAMETER *param;
+ if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL;
+ sprintf (sect,"%s%lu",prefix ? prefix : "",section++);
+ if (flags && prefix) { /* want to search MIME header too? */
+ st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size,
+ FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
+ else {
+ /* make UTF-8 version of header */
+ utf8_mime2text (&st,&h,U8T_CANONICAL);
+ ret = mail_search_string_work (&h,&stream->private.search.string);
+ if (h.data != st.data) fs_give ((void **) &h.data);
+ }
+ }
+ if (!ret) switch (body->type) {
+ case TYPEMULTIPART:
+ /* extend prefix if not first time */
+ s = prefix ? strcat (sect,".") : "";
+ for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next)
+ ret = mail_search_body (stream,msgno,&part->body,s,i,flags);
+ break;
+ case TYPEMESSAGE:
+ if (!strcmp (body->subtype,"RFC822")) {
+ if (flags) { /* want to search nested message header? */
+ st.data = (unsigned char *)
+ mail_fetch_header (stream,msgno,sect,NIL,&st.size,
+ FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret =stream->private.search.result;
+ else {
+ /* make UTF-8 version of header */
+ utf8_mime2text (&st,&h,U8T_CANONICAL);
+ ret = mail_search_string_work (&h,&stream->private.search.string);
+ if (h.data != st.data) fs_give ((void **) &h.data);
+ }
+ }
+ if (body = body->nested.msg->body)
+ ret = (body->type == TYPEMULTIPART) ?
+ mail_search_body (stream,msgno,body,(prefix ? prefix : ""),
+ section - 1,flags) :
+ mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags);
+ break;
+ }
+ /* non-MESSAGE/RFC822 falls into text case */
+
+ case TYPETEXT:
+ s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
+ else {
+ for (t = NIL,param = body->parameter; param && !t; param = param->next)
+ if (!strcmp (param->attribute,"CHARSET")) t = param->value;
+ switch (body->encoding) { /* what encoding? */
+ case ENCBASE64:
+ if (st.data = (unsigned char *)
+ rfc822_base64 ((unsigned char *) s,i,&st.size)) {
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ fs_give ((void **) &st.data);
+ }
+ break;
+ case ENCQUOTEDPRINTABLE:
+ if (st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) {
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ fs_give ((void **) &st.data);
+ }
+ break;
+ default:
+ st.data = (unsigned char *) s;
+ st.size = i;
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ break;
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+/* Mail search text
+ * Accepts: sized text to search
+ * character set of sized text
+ * string list of search keys
+ * Returns: T if search found a match
+ */
+
+long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st)
+{
+ SIZEDTEXT u;
+ long ret;
+ STRINGLIST **sc = st;
+ /* convert to UTF-8 as best we can */
+ if (!utf8_text (s,charset,&u,U8T_CANONICAL))
+ utf8_text (s,NIL,&u,U8T_CANONICAL);
+ ret = mail_search_string_work (&u,st);
+ if (u.data != s->data) fs_give ((void **) &u.data);
+ return ret;
+}
+
+
+/* Mail search text worker routine
+ * Accepts: sized text to search
+ * string list of search keys
+ * Returns: T if search found a match
+ */
+
+long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st)
+{
+ void *t;
+ STRINGLIST **sc = st;
+ while (*sc) { /* run down criteria list */
+ if (ssearch (s->data,s->size,(*sc)->text.data,(*sc)->text.size)) {
+ t = (void *) (*sc); /* found one, need to flush this */
+ *sc = (*sc)->next; /* remove it from the list */
+ fs_give (&t); /* flush the buffer */
+ }
+ else sc = &(*sc)->next; /* move to next in list */
+ }
+ return *st ? NIL : LONGT;
+}
+
+
+/* Mail search keyword
+ * Accepts: MAIL stream
+ * elt to get flags from
+ * keyword list
+ * T for keyword search, NIL for unkeyword search
+ * Returns: T if search found a match
+ */
+
+long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st,
+ long flag)
+{
+ int i,j;
+ unsigned long f = 0;
+ unsigned long tf;
+ do {
+ for (i = 0; (j = (i < NUSERFLAGS) && stream->user_flags[i]); ++i)
+ if (!compare_csizedtext (stream->user_flags[i],&st->text)) {
+ f |= (1 << i);
+ break;
+ }
+ if (flag && !j) return NIL;
+ } while (st = st->next);
+ tf = elt->user_flags & f; /* get set flags which match */
+ return flag ? (f == tf) : !tf;
+}
+
+/* Mail search an address list
+ * Accepts: address list
+ * string list
+ * Returns: T if search found a match
+ */
+
+#define SEARCHBUFLEN (size_t) 2000
+#define SEARCHBUFSLOP (size_t) 5
+
+long mail_search_addr (ADDRESS *adr,STRINGLIST *st)
+{
+ ADDRESS *a,tadr;
+ SIZEDTEXT txt;
+ char tmp[SENDBUFLEN + 1];
+ size_t i = SEARCHBUFLEN;
+ size_t k;
+ long ret = NIL;
+ if (adr) {
+ txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP);
+ /* never an error or next */
+ tadr.error = NIL,tadr.next = NIL;
+ /* write address list */
+ for (txt.size = 0,a = adr; a; a = a->next) {
+ k = (tadr.mailbox = a->mailbox) ? 4 + 2*strlen (a->mailbox) : 3;
+ if (tadr.personal = a->personal) k += 3 + 2*strlen (a->personal);
+ if (tadr.adl = a->adl) k += 3 + 2*strlen (a->adl);
+ if (tadr.host = a->host) k += 3 + 2*strlen (a->host);
+ if (tadr.personal || tadr.adl) k += 2;
+ if (k < (SENDBUFLEN-10)) {/* ignore ridiculous addresses */
+ tmp[0] = '\0';
+ rfc822_write_address (tmp,&tadr);
+ /* resize buffer if necessary */
+ if (((k = strlen (tmp)) + txt.size) > i)
+ fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN));
+ /* add new address */
+ memcpy (txt.data + txt.size,tmp,k);
+ txt.size += k;
+ /* another address follows */
+ if (a->next) txt.data[txt.size++] = ',';
+ }
+ }
+ txt.data[txt.size] = '\0'; /* tie off string */
+ ret = mail_search_header (&txt,st);
+ fs_give ((void **) &txt.data);
+ }
+ return ret;
+}
+
+/* Get string for low-memory searching
+ * Accepts: readin function pointer
+ * stream to use
+ * number of bytes
+ * gets data packet
+
+ * mail stream
+ * message number
+ * descriptor string
+ * option flags
+ * Returns: NIL, always
+ */
+
+#define SEARCHSLOP 128
+
+char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN+SEARCHSLOP+1];
+ SIZEDTEXT st;
+ /* better not be called unless searching */
+ if (!md->stream->private.search.string) {
+ sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
+ md->stream->mailbox,
+ (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
+ fatal (tmp);
+ }
+ /* initially no match for search */
+ md->stream->private.search.result = NIL;
+ /* make sure buffer clear */
+ memset (st.data = (unsigned char *) tmp,'\0',
+ (size_t) MAILTMPLEN+SEARCHSLOP+1);
+ /* read first buffer */
+ (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp);
+ /* search for text */
+ if (mail_search_string (&st,NIL,&md->stream->private.search.string))
+ md->stream->private.search.result = T;
+ else if (size -= i) { /* more to do, blat slop down */
+ memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP);
+ do { /* read subsequent buffers one at a time */
+ (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP);
+ st.size = i + SEARCHSLOP;
+ if (mail_search_string (&st,NIL,&md->stream->private.search.string))
+ md->stream->private.search.result = T;
+ else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP);
+ }
+ while ((size -= i) && !md->stream->private.search.result);
+ }
+ if (size) { /* toss out everything after that */
+ do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp);
+ while (size -= i);
+ }
+ return NIL;
+}
+
+/* Mail parse search criteria
+ * Accepts: criteria
+ * Returns: search program if parse successful, else NIL
+ */
+
+SEARCHPGM *mail_criteria (char *criteria)
+{
+ SEARCHPGM *pgm = NIL;
+ char *criterion,*r,tmp[MAILTMPLEN];
+ int f;
+ if (criteria) { /* only if criteria defined */
+ /* make writeable copy of criteria */
+ criteria = cpystr (criteria);
+ /* for each criterion */
+ for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
+ criterion; (criterion = strtok_r (NIL," ",&r))) {
+ f = NIL; /* init then scan the criterion */
+ switch (*ucase (criterion)) {
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (criterion+1,"LL")) f = T;
+ else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
+ break;
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (criterion+1,"CC"))
+ f = mail_criteria_string (&pgm->bcc,&r);
+ else if (!strcmp (criterion+1,"EFORE"))
+ f = mail_criteria_date (&pgm->before,&r);
+ else if (!strcmp (criterion+1,"ODY"))
+ f = mail_criteria_string (&pgm->body,&r);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
+ break;
+ case 'D': /* possible DELETED */
+ if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
+ else if (!strcmp (criterion+1,"ROM"))
+ f = mail_criteria_string (&pgm->from,&r);
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (criterion+1,"EYWORD"))
+ f = mail_criteria_string (&pgm->keyword,&r);
+ break;
+
+ case 'N': /* possible NEW */
+ if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
+ break;
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
+ else if (!strcmp (criterion+1,"N"))
+ f = mail_criteria_date (&pgm->on,&r);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SINCE, SUBJECT */
+ if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
+ else if (!strcmp (criterion+1,"INCE"))
+ f = mail_criteria_date (&pgm->since,&r);
+ else if (!strcmp (criterion+1,"UBJECT"))
+ f = mail_criteria_string (&pgm->subject,&r);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (criterion+1,"EXT"))
+ f = mail_criteria_string (&pgm->text,&r);
+ else if (!strcmp (criterion+1,"O"))
+ f = mail_criteria_string (&pgm->to,&r);
+ break;
+ case 'U': /* possible UN* */
+ if (criterion[1] == 'N') {
+ if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
+ else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
+ else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
+ else if (!strcmp (criterion+2,"KEYWORD"))
+ f = mail_criteria_string (&pgm->unkeyword,&r);
+ else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
+ }
+ break;
+ default: /* we will barf below */
+ break;
+ }
+ if (!f) { /* if can't identify criterion */
+ sprintf (tmp,"Unknown search criterion: %.30s",criterion);
+ MM_LOG (tmp,ERROR);
+ mail_free_searchpgm (&pgm);
+ break;
+ }
+ }
+ /* no longer need copy of criteria */
+ fs_give ((void **) &criteria);
+ }
+ return pgm;
+}
+
+/* Parse a date
+ * Accepts: pointer to date integer to return
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int mail_criteria_date (unsigned short *date,char **r)
+{
+ STRINGLIST *s = NIL;
+ MESSAGECACHE elt;
+ /* parse the date and return fn if OK */
+ int ret = (mail_criteria_string (&s,r) &&
+ mail_parse_date (&elt,(char *) s->text.data) &&
+ (*date = mail_shortdate (elt.year,elt.month,elt.day))) ?
+ T : NIL;
+ if (s) mail_free_stringlist (&s);
+ return ret;
+}
+
+/* Calculate shortdate from elt values
+ * Accepts: year (0 = BASEYEAR)
+ * month (1 = January)
+ * day
+ * Returns: shortdate
+ */
+
+unsigned short mail_shortdate (unsigned int year,unsigned int month,
+ unsigned int day)
+{
+ return (year << 9) + (month << 5) + day;
+}
+
+/* Parse a string
+ * Accepts: pointer to stringlist
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int mail_criteria_string (STRINGLIST **s,char **r)
+{
+ unsigned long n;
+ char e,*d,*end = " ",*c = strtok_r (NIL,"",r);
+ if (!c) return NIL; /* missing argument */
+ switch (*c) { /* see what the argument is */
+ case '{': /* literal string */
+ n = strtoul (c+1,&d,10); /* get its length */
+ if ((*d++ == '}') && (*d++ == '\015') && (*d++ == '\012') &&
+ (!(*(c = d + n)) || (*c == ' '))) {
+ e = *--c; /* store old delimiter */
+ *c = '\377'; /* make sure not a space */
+ strtok_r (c," ",r); /* reset the strtok mechanism */
+ *c = e; /* put character back */
+ break;
+ }
+ case '\0': /* catch bogons */
+ case ' ':
+ return NIL;
+ case '"': /* quoted string */
+ if (strchr (c+1,'"')) end = "\"";
+ else return NIL; /* falls through */
+ default: /* atomic string */
+ if (d = strtok_r (c,end,r)) n = strlen (d);
+ else return NIL;
+ break;
+ }
+ while (*s) s = &(*s)->next; /* find tail of list */
+ *s = mail_newstringlist (); /* make new entry */
+ /* return the data */
+ (*s)->text.data = (unsigned char *) cpystr (d);
+ (*s)->text.size = n;
+ return T;
+}
+
+/* Mail parse set from string
+ * Accepts: string to parse
+ * pointer to updated string pointer for return
+ * Returns: set with pointer updated, or NIL if error
+ */
+
+SEARCHSET *mail_parse_set (char *s,char **ret)
+{
+ SEARCHSET *cur;
+ SEARCHSET *set = NIL;
+ while (isdigit (*s)) {
+ if (!set) cur = set = mail_newsearchset ();
+ else cur = cur->next = mail_newsearchset ();
+ /* parse value */
+ if (!(cur->first = strtoul (s,&s,10)) ||
+ ((*s == ':') && !(isdigit (*++s) && (cur->last = strtoul (s,&s,10)))))
+ break; /* bad value or range */
+ if (*s == ',') ++s; /* point to next value if more */
+ else { /* end of set */
+ *ret = s; /* set return pointer */
+ return set; /* return set */
+ }
+ }
+ mail_free_searchset (&set); /* failure, punt partial set */
+ return NIL;
+}
+
+
+/* Mail append to set
+ * Accepts: head of search set or NIL to do nothing
+ * message to add
+ * Returns: tail of search set or NIL if did nothing
+ */
+
+SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno)
+{
+ if (set) { /* find tail */
+ while (set->next) set = set->next;
+ /* start of set if no first member */
+ if (!set->first) set->first = msgno;
+ else if (msgno == (set->last ? set->last : set->first) + 1)
+ set->last = msgno; /* extend range if 1 past current */
+ else (set = set->next = mail_newsearchset ())->first = msgno;
+ }
+ return set;
+}
+
+/* Mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret = NIL;
+ if (stream->dtb) /* do the driver's action */
+ ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs))
+ (stream,charset,spg,pgm,flags);
+ /* flush search/sort programs if requested */
+ if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
+ if (flags & SO_FREE) mail_free_sortpgm (&pgm);
+ return ret;
+}
+
+/* Mail sort messages work routine
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i;
+ SORTCACHE **sc;
+ unsigned long *ret = NIL;
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) pgm->nmsgs++;
+ if (pgm->nmsgs) { /* pass 2: sort cache */
+ sc = mail_sort_loadcache (stream,pgm);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ }
+ /* empty sort results */
+ else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
+ sizeof (unsigned long));
+ /* also return via callback if requested */
+ if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs);
+ return ret; /* return sort results */
+}
+
+/* Mail sort sortcache vector
+ * Accepts: mail stream
+ * sort program
+ * sortcache vector
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
+ long flags)
+{
+ unsigned long i,*ret;
+ /* pass 3: sort messages */
+ qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare);
+ /* optional post sorting */
+ if (pgm->postsort) (*pgm->postsort) ((void *) sc);
+ /* pass 4: return results */
+ ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long));
+ if (flags & SE_UID) /* UID or msgno? */
+ for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num);
+ else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num;
+ ret[pgm->nmsgs] = 0; /* tie off message list */
+ return ret;
+}
+
+/* Mail load sortcache
+ * Accepts: mail stream, already searched
+ * sort program
+ * Returns: vector of sortcache pointers matching search
+ */
+
+static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL};
+static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL};
+static STRINGLIST mailfromline = {{(unsigned char *) "from",4},
+ &mailrnfromline};
+static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL};
+static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL};
+static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL};
+
+SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
+{
+ char *t,*v,*x,tmp[MAILTMPLEN];
+ SORTPGM *pg;
+ SORTCACHE *s,**sc;
+ MESSAGECACHE *elt,telt;
+ ENVELOPE *env;
+ ADDRESS *adr = NIL;
+ unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *);
+ sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ /* see what needs to be loaded */
+ for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->searched) {
+ sc[pgm->progress.cached++] =
+ s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ s->pgm = pgm; /* note sort program */
+ s->num = i;
+ /* get envelope if cached */
+ if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL;
+ else env = elt->private.msg.env;
+ for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
+ case SORTARRIVAL: /* sort by arrival date */
+ if (!s->arrival) {
+ /* internal date unknown but can get? */
+ if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->arrival = elt->day ? mail_longdate (elt) : 1;
+ s->dirty = T;
+ }
+ break;
+ case SORTSIZE: /* sort by message size */
+ if (!s->size) {
+ if (!elt->rfc822_size) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ s->size = elt->rfc822_size ? elt->rfc822_size : 1;
+ s->dirty = T;
+ }
+ break;
+
+ case SORTDATE: /* sort by date */
+ if (!s->date) {
+ if (env) t = env->date;
+ else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':')))
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ /* skip leading whitespace */
+ if (t) while ((*t == ' ') || (*t == '\t')) t++;
+ /* parse date from Date: header */
+ if (!(t && mail_parse_date (&telt,t) &&
+ (s->date = mail_longdate (&telt)))) {
+ /* failed, use internal date */
+ if (!(s->date = s->arrival)) {
+ /* internal date unknown but can get? */
+ if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
+ }
+ }
+ s->dirty = T;
+ }
+ break;
+
+ case SORTFROM: /* sort by first from */
+ if (!s->from) {
+ if (env) s->from = env->from && env->from->mailbox ?
+ cpystr (env->from->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 'f': /* continuation but with extra "From:" */
+ case 'F':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->from = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->from) s->from = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTTO: /* sort by first to */
+ if (!s->to) {
+ if (env) s->to = env->to && env->to->mailbox ?
+ cpystr (env->to->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 't': /* continuation but with extra "To:" */
+ case 'T':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->to = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->to) s->to = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTCC: /* sort by first cc */
+ if (!s->cc) {
+ if (env) s->cc = env->cc && env->cc->mailbox ?
+ cpystr (env->cc->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 't': /* continuation but with extra "To:" */
+ case 'T':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->cc = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->cc) s->cc = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTSUBJECT: /* sort by subject */
+ if (!s->subject) {
+ /* get subject from envelope if have one */
+ if (env) t = env->subject ? env->subject : "";
+ /* otherwise snarf from header text */
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline,
+ NIL,FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':')))
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ else t = ""; /* empty subject */
+ /* strip and cache subject */
+ s->refwd = mail_strip_subject (t,&s->subject);
+ s->dirty = T;
+ }
+ break;
+ default:
+ fatal ("Unknown sort function");
+ }
+ }
+ return sc;
+}
+
+/* Strip subjects of extra spaces and leading and trailing cruft for sorting
+ * Accepts: unstripped subject
+ * pointer to return stripped subject, in cpystr form
+ * Returns: T if subject had a re/fwd, NIL otherwise
+ */
+
+unsigned int mail_strip_subject (char *t,char **ret)
+{
+ SIZEDTEXT src,dst;
+ unsigned long i,slen;
+ char c,*s,*x;
+ unsigned int refwd = NIL;
+ if (src.size = strlen (t)) { /* have non-empty subject? */
+ src.data = (unsigned char *) t;
+ /* Step 1 */
+ /* make copy, convert MIME2 if needed */
+ *ret = s = (utf8_mime2text (&src,&dst,U8T_CANONICAL) &&
+ (src.data != dst.data)) ? (char *) dst.data : cpystr (t);
+ /* convert spaces to tab, strip extra spaces */
+ for (x = t = s, c = 'x'; *t; t++) {
+ if (c != ' ') c = *x++ = ((*t == '\t') ? ' ' : *t);
+ else if ((*t != '\t') && (*t != ' ')) c = *x++ = *t;
+ }
+ *x = '\0'; /* tie off string */
+ /* Step 2 */
+ for (slen = dst.size; s; slen = strlen (s)) {
+ for (t = s + slen; t > s; ) switch (t[-1]) {
+ case ' ': case '\t': /* WSP */
+ *--t = '\0'; /* just remove it */
+ break;
+ case ')': /* possible "(fwd)" */
+ if ((t >= (s + 5)) && (t[-5] == '(') &&
+ ((t[-4] == 'F') || (t[-4] == 'f')) &&
+ ((t[-3] == 'W') || (t[-3] == 'w')) &&
+ ((t[-2] == 'D') || (t[-2] == 'd'))) {
+ *(t -= 5) = '\0'; /* remove "(fwd)" */
+ refwd = T; /* note a re/fwd */
+ break;
+ }
+ default: /* not a subj-trailer */
+ t = s;
+ break;
+ }
+ /* Steps 3-5 */
+ for (t = s; t; ) switch (*s) {
+ case ' ': case '\t': /* WSP */
+ s = t = mail_strip_subject_wsp (s + 1);
+ break;
+ case 'r': case 'R': /* possible "re" */
+ if (((s[1] == 'E') || (s[1] == 'e')) &&
+ (t = mail_strip_subject_wsp (s + 2)) &&
+ (t = mail_strip_subject_blob (t)) && (*t == ':')) {
+ s = ++t; /* found "re" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else t = NIL; /* found subj-middle */
+ break;
+ case 'f': case 'F': /* possible "fw" or "fwd" */
+ if (((s[1] == 'w') || (s[1] == 'W')) &&
+ (((s[2] == 'd') || (s[2] == 'D')) ?
+ (t = mail_strip_subject_wsp (s + 3)) :
+ (t = mail_strip_subject_wsp (s + 2))) &&
+ (t = mail_strip_subject_blob (t)) && (*t == ':')) {
+ s = ++t; /* found "fwd" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else t = NIL; /* found subj-middle */
+ break;
+ case '[': /* possible subj-blob */
+ if ((t = mail_strip_subject_blob (s)) && *t) s = t;
+ else t = NIL; /* found subj-middle */
+ break;
+ default:
+ t = NIL; /* found subj-middle */
+ break;
+ }
+ /* Step 6 */
+ /* Netscape-style "[Fwd: ...]"? */
+ if ((*s == '[') && ((s[1] == 'F') || (s[1] == 'f')) &&
+ ((s[2] == 'W') || (s[2] == 'w')) &&
+ ((s[3] == 'D') || (s[3] == 'd')) && (s[4] == ':') &&
+ (s[i = strlen (s) - 1] == ']')) {
+ s[i] = '\0'; /* flush closing "]" */
+ s += 5; /* and leading "[Fwd:" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else break; /* don't need to loop back to step 2 */
+ }
+ if (s != (t = *ret)) { /* removed leading text? */
+ s = *ret = cpystr (s); /* yes, make a fresh return copy */
+ fs_give ((void **) &t); /* flush old copy */
+ }
+ }
+ else *ret = cpystr (""); /* empty subject */
+ return refwd; /* return re/fwd state */
+}
+
+/* Strip subject wsp helper routine
+ * Accepts: text
+ * Returns: pointer to text after blob
+ */
+
+char *mail_strip_subject_wsp (char *s)
+{
+ while ((*s == ' ') || (*s == '\t')) s++;
+ return s;
+}
+
+
+/* Strip subject blob helper routine
+ * Accepts: text
+ * Returns: pointer to text after any blob, NIL if blob-like but not blob
+ */
+
+char *mail_strip_subject_blob (char *s)
+{
+ if (*s != '[') return s; /* not a blob, ignore */
+ /* search for end of blob */
+ while (*++s != ']') if ((*s == '[') || !*s) return NIL;
+ return mail_strip_subject_wsp (s + 1);
+}
+
+/* Sort compare messages
+ * Accept: first message sort cache element
+ * second message sort cache element
+ * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
+ */
+
+int mail_sort_compare (const void *a1,const void *a2)
+{
+ int i = 0;
+ SORTCACHE *s1 = *(SORTCACHE **) a1;
+ SORTCACHE *s2 = *(SORTCACHE **) a2;
+ SORTPGM *pgm = s1->pgm;
+ if (!s1->sorted) { /* this one sorted yet? */
+ s1->sorted = T;
+ pgm->progress.sorted++; /* another sorted message */
+ }
+ if (!s2->sorted) { /* this one sorted yet? */
+ s2->sorted = T;
+ pgm->progress.sorted++; /* another sorted message */
+ }
+ do {
+ switch (pgm->function) { /* execute search program */
+ case SORTDATE: /* sort by date */
+ i = compare_ulong (s1->date,s2->date);
+ break;
+ case SORTARRIVAL: /* sort by arrival date */
+ i = compare_ulong (s1->arrival,s2->arrival);
+ break;
+ case SORTSIZE: /* sort by message size */
+ i = compare_ulong (s1->size,s2->size);
+ break;
+ case SORTFROM: /* sort by first from */
+ i = compare_cstring (s1->from,s2->from);
+ break;
+ case SORTTO: /* sort by first to */
+ i = compare_cstring (s1->to,s2->to);
+ break;
+ case SORTCC: /* sort by first cc */
+ i = compare_cstring (s1->cc,s2->cc);
+ break;
+ case SORTSUBJECT: /* sort by subject */
+ i = compare_cstring (s1->subject,s2->subject);
+ break;
+ }
+ if (pgm->reverse) i = -i; /* flip results if necessary */
+ }
+ while (pgm = i ? NIL : pgm->next);
+ /* return result, avoid 0 if at all possible */
+ return i ? i : compare_ulong (s1->num,s2->num);
+}
+
+/* Return message date as an unsigned long seconds since time began
+ * Accepts: message cache pointer
+ * Returns: unsigned long of date
+ *
+ * This routine, like most UNIX systems, is clueless about leap seconds.
+ * Thus, it treats 23:59:60 as equivalent to 00:00:00 the next day.
+ *
+ * This routine forces any early hours on 1-Jan-1970 in oriental timezones
+ * to be 1-Jan-1970 00:00:00 UTC, so as to avoid negative longdates.
+ */
+
+unsigned long mail_longdate (MESSAGECACHE *elt)
+{
+ unsigned long m = elt->month ? elt->month : 1;
+ unsigned long yr = elt->year + BASEYEAR;
+ /* number of days since time began */
+ unsigned long ret = (elt->day ? (elt->day - 1) : 0)
+ + 30 * (m - 1) + ((m + (m > 8)) / 2)
+#ifndef USEJULIANCALENDAR
+#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
+ + ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100))
+#ifdef Y4KBUGFIX
+ - ((yr / 4000) - (BASEYEAR / 4000))
+#endif
+ - ((m < 3) ?
+ !(yr % 4) && ((yr % 100) || (!(yr % 400)
+#ifdef Y4KBUGFIX
+ && (yr % 4000)
+#endif
+ )) : 2)
+#else /* Orthodox calendar */
+ + ((2*(yr / 900)) - (2*(BASEYEAR / 900)))
+ + (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200))
+ + (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600))
+ - ((yr / 100) - (BASEYEAR / 100))
+ - ((m < 3) ?
+ !(yr % 4) && ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600))
+ : 2)
+#endif
+#endif
+ + elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4);
+ ret *= 24; ret += elt->hours; /* date value in hours */
+ ret *= 60; ret +=elt->minutes;/* date value in minutes */
+ yr = (elt->zhours * 60) + elt->zminutes;
+ if (elt->zoccident) ret += yr;/* occidental timezone, make UTC */
+ else if (ret < yr) return 0; /* still 31-Dec-1969 in UTC */
+ else ret -= yr; /* oriental timezone, make UTC */
+ ret *= 60; ret += elt->seconds;
+ return ret;
+}
+
+/* Mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret = NIL;
+ if (stream->dtb) /* must have a live driver */
+ ret = stream->dtb->thread ? /* do driver's action if available */
+ (*stream->dtb->thread) (stream,type,charset,spg,flags) :
+ mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ /* flush search/sort programs if requested */
+ if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
+ return ret;
+}
+
+
+/* Mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter)
+{
+ THREADER *t;
+ for (t = &mailthreadlist; t; t = t->next)
+ if (!compare_cstring (type,t->name)) {
+ THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter);
+ if (mailthreadresults) (*mailthreadresults) (stream,ret);
+ return ret;
+ }
+ MM_LOG ("No such thread type",ERROR);
+ return NIL;
+}
+
+/* Mail thread ordered subject
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree
+ */
+
+THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter)
+{
+ THREADNODE *thr = NIL;
+ THREADNODE *cur,*top,**tc;
+ SORTPGM pgm,pgm2;
+ SORTCACHE *s;
+ unsigned long i,j,*lst,*ls;
+ /* sort by subject+date */
+ memset (&pgm,0,sizeof (SORTPGM));
+ memset (&pgm2,0,sizeof (SORTPGM));
+ pgm.function = SORTSUBJECT;
+ pgm.next = &pgm2;
+ pgm2.function = SORTDATE;
+ if (lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))){
+ if (*(ls = lst)) { /* create thread */
+ /* note first subject */
+ cur = top = thr = mail_newthreadnode
+ ((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE));
+ /* note its number */
+ cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst;
+ i = 1; /* number of threads */
+ while (*ls) { /* build tree */
+ /* subjects match? */
+ s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE);
+ if (compare_cstring (top->sc->subject,s->subject)) {
+ i++; /* have a new thread */
+ top = top->branch = cur = mail_newthreadnode (s);
+ }
+ /* start a child of the top */
+ else if (cur == top) cur = cur->next = mail_newthreadnode (s);
+ /* sibling of child */
+ else cur = cur->branch = mail_newthreadnode (s);
+ /* set to msgno or UID as needed */
+ cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
+ }
+ /* make threadnode cache */
+ tc = (THREADNODE **) fs_get (i * sizeof (THREADNODE *));
+ /* load threadnode cache */
+ for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
+ if (i != j) fatal ("Threadnode cache confusion");
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ thr = tc[0]; /* head of data */
+ fs_give ((void **) &tc);
+ }
+ fs_give ((void **) &lst);
+ }
+ return thr;
+}
+
+/* Mail thread references
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree
+ */
+
+#define REFHASHSIZE 1009 /* arbitrary prime for hash table size */
+
+/* Reference threading container, as described in Jamie Zawinski's web page
+ * (http://www.jwz.org/doc/threading.html) for this algorithm. These are
+ * stored as extended data in the hash table (called "id_table" in JWZ's
+ * document) and are maintained by the hash table routines. The hash table
+ * routines implement extended data as additional void* words at the end of
+ * each bucket, hence these strange macros instead of a struct which would
+ * have been more straightforward.
+ */
+
+#define THREADLINKS 3 /* number of thread links */
+
+#define CACHE(data) ((SORTCACHE *) (data)[0])
+#define PARENT(data) ((container_t) (data)[1])
+#define SETPARENT(data,value) ((container_t) (data[1] = value))
+#define SIBLING(data) ((container_t) (data)[2])
+#define SETSIBLING(data,value) ((container_t) (data[2] = value))
+#define CHILD(data) ((container_t) (data)[3])
+#define SETCHILD(data,value) ((container_t) (data[3] = value))
+
+THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter)
+{
+ MESSAGECACHE *elt,telt;
+ ENVELOPE *env;
+ SORTCACHE *s;
+ STRINGLIST *st;
+ HASHENT *he;
+ THREADNODE **tc,*cur,*lst,*nxt,*sis,*msg;
+ container_t con,nxc,prc,sib;
+ void **sub;
+ char *t,tmp[MAILTMPLEN];
+ unsigned long j,nmsgs;
+ unsigned long i = stream->nmsgs * sizeof (SORTCACHE *);
+ SORTCACHE **sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ HASHTAB *ht = hash_create (REFHASHSIZE);
+ THREADNODE *root = NIL;
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+
+ /* create SORTCACHE vector of requested msgs */
+ for (i = 1, nmsgs = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched)
+ (sc[nmsgs++] = (SORTCACHE *)(*mailcache)(stream,i,CH_SORTCACHE))->num =i;
+ /* separate pass so can do overview fetch lookahead */
+ for (i = 0; i < nmsgs; ++i) { /* for each requested message */
+ /* is anything missing in its SORTCACHE? */
+ if (!((s = sc[i])->date && s->subject && s->message_id && s->references)) {
+ /* driver has an overview mechanism? */
+ if (stream->dtb && stream->dtb->overview) {
+ /* yes, find following unloaded entries */
+ for (j = i + 1; (j < nmsgs) && !sc[j]->references; ++j);
+ sprintf (tmp,"%lu",mail_uid (stream,s->num));
+ if (i != --j) /* end of range different? */
+ sprintf (tmp + strlen (tmp),":%lu",mail_uid (stream,sc[j]->num));
+ /* load via overview mechanism */
+ mail_fetch_overview (stream,tmp,mail_thread_loadcache);
+ }
+ /* still missing data? */
+ if (!s->date || !s->subject || !s->message_id || !s->references) {
+ /* try to load data from envelope */
+ if (env = mail_fetch_structure (stream,s->num,NIL,NIL)) {
+ if (!s->date && env->date && mail_parse_date (&telt,env->date))
+ s->date = mail_longdate (&telt);
+ if (!s->subject && env->subject)
+ s->refwd =
+ mail_strip_subject (env->subject,&s->subject);
+ if (!s->message_id && env->message_id && *env->message_id)
+ s->message_id = mail_thread_parse_msgid (env->message_id,NIL);
+ if (!s->references && /* use References: or In-Reply-To: */
+ !(s->references =
+ mail_thread_parse_references (env->references,T)))
+ s->references = mail_thread_parse_references(env->in_reply_to,NIL);
+ }
+ /* last resort */
+ if (!s->date && !(s->date = s->arrival)) {
+ /* internal date unknown but can get? */
+ if (!(elt = mail_elt (stream,s->num))->day &&
+ !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",s->num);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
+ }
+ if (!s->subject) s->subject = cpystr ("");
+ if (!s->references) s->references = mail_newstringlist ();
+ s->dirty = T;
+ }
+ }
+
+ /* Step 1 (preliminary) */
+ /* generate unique string */
+ sprintf (tmp,"%s.%lx.%lx@%s",stream->mailbox,stream->uid_validity,
+ mail_uid (stream,s->num),mylocalhost ());
+ /* flush old unique string if not message-id */
+ if (s->unique && (s->unique != s->message_id))
+ fs_give ((void **) &s->unique);
+ s->unique = s->message_id ? /* don't permit Message ID duplicates */
+ (hash_lookup (ht,s->message_id) ? cpystr (tmp) : s->message_id) :
+ (s->message_id = cpystr (tmp));
+ /* add unique string to hash table */
+ hash_add (ht,s->unique,s,THREADLINKS);
+ }
+ /* Step 1 */
+ for (i = 0; i < nmsgs; ++i) { /* for each message in sortcache */
+ /* Step 1A */
+ if ((st = (s = sc[i])->references) && st->text.data)
+ for (con = hash_lookup_and_add (ht,(char *) st->text.data,NIL,
+ THREADLINKS); st = st->next; con = nxc) {
+ nxc = hash_lookup_and_add (ht,(char *) st->text.data,NIL,THREADLINKS);
+ /* only if no parent & won't introduce loop */
+ if (!PARENT (nxc) && !mail_thread_check_child (con,nxc)) {
+ SETPARENT (nxc,con); /* establish parent/child link */
+ /* other children become sibling of this one */
+ SETSIBLING (nxc,CHILD (con));
+ SETCHILD (con,nxc); /* set as child of parent */
+ }
+ }
+ else con = NIL; /* else message has no ancestors */
+ /* Step 1B */
+ if ((prc = PARENT ((nxc = hash_lookup (ht,s->unique)))) &&
+ (prc != con)) { /* break links if have a different parent */
+ SETPARENT (nxc,NIL); /* easy if direct child */
+ if (nxc == CHILD (prc)) SETCHILD (prc,SIBLING (nxc));
+ else { /* otherwise hunt through sisters */
+ for (sib = CHILD (prc); nxc != SIBLING (sib); sib = SIBLING (sib));
+ SETSIBLING (sib,SIBLING (nxc));
+ }
+ SETSIBLING (nxc,NIL); /* no more little sisters either */
+ prc = NIL; /* no more parent set */
+ }
+ /* need to set parent, and parent is good? */
+ if (!prc && !mail_thread_check_child (con,nxc)) {
+ SETPARENT (nxc,con); /* establish parent/child link */
+ if (con) { /* if non-root parent, set parent's child */
+ if (CHILD (con)) { /* have a child already */
+ /* find youngest daughter */
+ for (con = CHILD (con); SIBLING (con); con = SIBLING (con));
+ SETSIBLING (con,nxc); /* add new baby sister */
+ }
+ else SETCHILD (con,nxc);/* set as only child */
+ }
+ }
+ }
+ fs_give ((void **) &sc); /* finished with sortcache vector */
+
+ /* Step 2 */
+ /* search hash table for parentless messages */
+ for (i = 0, prc = con = NIL; i < ht->size; i++)
+ for (he = ht->table[i]; he; he = he->next)
+ if (!PARENT ((nxc = he->data))) {
+ /* sibling of previous parentless message */
+ if (con) con = SETSIBLING (con,nxc);
+ else prc = con = nxc; /* first parentless message */
+ }
+ /* Once the dummy containers are pruned, we no longer need the parent
+ * information, so we can convert the containers to THREADNODEs. Since
+ * we don't need the id_table any more either, we can reset the hash table
+ * and reuse it as a subject_table. Resetting the hash table will also
+ * destroy the containers.
+ */
+ /* Step 3 */
+ /* prune dummies, convert to threadnode */
+ root = mail_thread_c2node (stream,mail_thread_prune_dummy (prc,NIL),flags);
+ /* Step 4 */
+ /* make buffer for sorting */
+ tc = (THREADNODE **) fs_get (nmsgs * sizeof (THREADNODE *));
+ /* load threadcache and count nodes to sort */
+ for (i = 0, cur = root; cur ; cur = cur->branch) tc[i++] = cur;
+ if (i > 1) { /* only if need to sort */
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ /* relink siblings */
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ root = tc[0]; /* establish new root */
+ }
+ /* Step 5A */
+ hash_reset (ht); /* discard containers, reset ht */
+ /* Step 5B */
+ for (cur = root; cur; cur = cur->branch)
+ if ((t = (nxt = (cur->sc ? cur : cur->next))->sc->subject) && *t) {
+ /* add new subject to hash table */
+ if (!(sub = hash_lookup (ht,t))) hash_add (ht,t,cur,0);
+ /* if one in table not dummy and */
+ else if ((s = (lst = (THREADNODE *) sub[0])->sc) &&
+ /* current dummy, or not re/fwd and table is */
+ (!cur->sc || (!nxt->sc->refwd && s->refwd)))
+ sub[0] = (void *) cur; /* replace with this message */
+ }
+
+ /* Step 5C */
+ for (cur = root, sis = NIL; cur; cur = msg) {
+ /* do nothing if current message or no sub */
+ if (!(t = (cur->sc ? cur : cur->next)->sc->subject) || !*t ||
+ ((lst = (THREADNODE *) (sub = hash_lookup (ht,t))[0]) == cur))
+ msg = (sis = cur)->branch;
+ else if (!lst->sc) { /* is message in the table a dummy? */
+ /* find youngest daughter of msg in table */
+ for (msg = lst->next; msg->branch; msg = msg->branch);
+ if (!cur->sc) { /* current message a dummy? */
+ msg->branch = cur->next;/* current's daughter now dummy's youngest */
+ msg = cur->branch; /* continue scan at younger sister */
+ /* now delete this node */
+ cur->branch = cur->next = NIL;
+ mail_free_threadnode (&cur);
+ }
+ else { /* current message not a dummy */
+ msg->branch = cur; /* append as youngest daughter */
+ msg = cur->branch; /* continue scan at younger sister */
+ cur->branch = NIL; /* lose our younger sisters */
+ }
+ }
+ else { /* no dummies, is current re/fwd, table not? */
+ if (cur->sc->refwd && !lst->sc->refwd) {
+ if (lst->next) { /* find youngest daughter of msg in table */
+ for (msg = lst->next; msg->branch; msg = msg->branch);
+ msg->branch = cur; /* append as youngest daughter */
+ }
+ else lst->next = cur; /* no children, so make the eldest daughter */
+ }
+
+ else { /* no re/fwd, create a new dummy */
+ msg = mail_newthreadnode (NIL);
+ if (lst == root) { /* msg in table is root? */
+ root = lst->branch; /* younger sister becomes new root */
+ /* no longer older sister either */
+ if (lst == sis) sis = NIL;
+ }
+ else { /* find older sister of msg in table */
+ for (nxt = root; lst != nxt->branch; nxt = nxt->branch);
+ /* remove from older sister */
+ nxt->branch = lst->branch;
+ }
+ msg->next = lst; /* msg in table becomes child */
+ lst->branch = cur; /* current now little sister of msg in table */
+ if (sis) { /* have an elder sister? */
+ if (sis == lst) /* rescan if lost her */
+ for (sis = root; cur != sis->branch; sis = sis->branch);
+ sis->branch = msg; /* make dummy younger sister of big sister */
+ }
+ else root = msg; /* otherwise this is the new root */
+ sub[0] = sis = msg; /* set new msg in table and new big sister */
+ }
+ msg = cur->branch; /* continue scan at younger sister */
+ cur->branch = NIL; /* lose our younger sisters */
+ }
+ if (sis) sis->branch = msg; /* older sister gets this as younger sister */
+ else root = msg; /* otherwise this is the new root */
+ }
+ hash_destroy (&ht); /* finished with hash table */
+ /* Step 6 */
+ /* sort threads */
+ root = mail_thread_sort (root,tc);
+ fs_give ((void **) &tc); /* finished with sort buffer */
+ return root; /* return sorted list */
+}
+
+/* Fetch overview callback to load sortcache for threading
+ * Accepts: MAIL stream
+ * UID of this message
+ * overview of this message
+ * msgno of this message
+ */
+
+void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno)
+{
+ if (msgno && ov) { /* just in case */
+ MESSAGECACHE telt, *elt;
+ ENVELOPE *env;
+ SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE);
+ if (!s->subject && ov->subject) {
+ s->refwd = mail_strip_subject (ov->subject,&s->subject);
+ s->dirty = T;
+ }
+ if (!s->from && ov->from && ov->from->mailbox) {
+ s->from = cpystr (ov->from->mailbox);
+ s->dirty = T;
+ }
+ if (!s->date && ov->date && mail_parse_date (&telt,ov->date)) {
+ s->date = mail_longdate (&telt);
+ s->dirty = T;
+ }
+ if (!s->message_id && ov->message_id) {
+ s->message_id = mail_thread_parse_msgid (ov->message_id,NIL);
+ s->dirty = T;
+ }
+ if (!s->references &&
+ !(s->references = mail_thread_parse_references (ov->references,T))
+ && stream->dtb && !strcmp(stream->dtb->name, "imap")
+ && (elt = mail_elt (stream, msgno)) != NULL
+ && (env = elt->private.msg.env) != NULL
+ && env->in_reply_to
+ && !(s->references = mail_thread_parse_references(env->in_reply_to, NIL))) {
+ /* don't do In-Reply-To with NNTP mailboxes */
+ s->references = mail_newstringlist ();
+ s->dirty = T;
+ }
+ if (!s->size && ov->optional.octets) {
+ s->size = ov->optional.octets;
+ s->dirty = T;
+ }
+ }
+}
+
+/* Thread parse Message ID
+ * Accepts: pointer to purported Message ID
+ * pointer to return pointer
+ * Returns: Message ID or NIL, return pointer updated
+ */
+
+char *mail_thread_parse_msgid (char *s,char **ss)
+{
+ char *ret = NIL;
+ char *t = NIL;
+ ADDRESS *adr;
+ if (s) { /* only for non-NIL strings */
+ rfc822_skipws (&s); /* skip whitespace */
+ /* ignore phrases */
+ if (((*s == '<') || (s = rfc822_parse_phrase (s))) &&
+ (adr = rfc822_parse_routeaddr (s,&t,BADHOST))) {
+ /* make return msgid */
+ if (adr->mailbox && adr->host)
+ sprintf (ret = (char *) fs_get (strlen (adr->mailbox) +
+ strlen (adr->host) + 2),"%s@%s",
+ adr->mailbox,adr->host);
+ mail_free_address (&adr); /* don't need temporary address */
+ }
+ }
+ if (ss) *ss = t; /* update return pointer */
+ return ret;
+}
+
+
+/* Thread parse references
+ * Accepts: pointer to purported references
+ * parse multiple references flag
+ * Returns: references or NIL
+ */
+
+STRINGLIST *mail_thread_parse_references (char *s,long flag)
+{
+ char *t;
+ STRINGLIST *ret = NIL;
+ STRINGLIST *cur;
+ /* found first reference? */
+ if (t = mail_thread_parse_msgid (s,&s)) {
+ (ret = mail_newstringlist ())->text.data = (unsigned char *) t;
+ ret->text.size = strlen (t);
+ if (flag) /* parse subsequent references */
+ for (cur = ret; t = mail_thread_parse_msgid (s,&s); cur = cur->next) {
+ (cur->next = mail_newstringlist ())->text.data = (unsigned char *) t;
+ cur->next->text.size = strlen (t);
+ }
+ }
+ return ret;
+}
+
+/* Prune dummy messages
+ * Accepts: candidate container to prune
+ * older sibling of container, if any
+ * Returns: container in this position, possibly pruned
+ * All children and younger siblings are also pruned
+ */
+
+container_t mail_thread_prune_dummy (container_t msg,container_t ane)
+{
+ /* prune container and children */
+ container_t ret = msg ? mail_thread_prune_dummy_work (msg,ane) : NIL;
+ /* prune all younger sisters */
+ if (ret) for (ane = ret; ane && (msg = SIBLING (ane)); ane = msg)
+ msg = mail_thread_prune_dummy_work (msg,ane);
+ return ret;
+}
+
+
+/* Prune dummy messages worker routine
+ * Accepts: candidate container to prune
+ * older sibling of container, if any
+ * Returns: container in this position, possibly pruned
+ * All children are also pruned
+ */
+
+container_t mail_thread_prune_dummy_work (container_t msg,container_t ane)
+{
+ container_t cur;
+ /* get children, if any */
+ container_t nxt = mail_thread_prune_dummy (CHILD (msg),NIL);
+ /* just update children if container has msg */
+ if (CACHE (msg)) SETCHILD (msg,nxt);
+ else if (!nxt) { /* delete dummy with no children */
+ nxt = SIBLING (msg); /* get younger sister */
+ if (ane) SETSIBLING (ane,nxt);
+ /* prune younger sister if exists */
+ msg = nxt ? mail_thread_prune_dummy_work (nxt,ane) : NIL;
+ }
+ /* not if parent root & multiple children */
+ else if ((cur = PARENT (msg)) || !SIBLING (nxt)) {
+ /* OK to promote, try younger sister of aunt */
+ if (ane) SETSIBLING (ane,nxt);
+ /* otherwise promote to child of grandmother */
+ else if (cur) SETCHILD (cur,nxt);
+ SETPARENT (nxt,cur); /* set parent as well */
+ /* look for end of siblings in new container */
+ for (cur = nxt; SIBLING (cur); cur = SIBLING (cur));
+ /* reattach deleted container's siblings */
+ SETSIBLING (cur,SIBLING (msg));
+ /* prune and return new container */
+ msg = mail_thread_prune_dummy_work (nxt,ane);
+ }
+ else SETCHILD (msg,nxt); /* in case child pruned */
+ return msg; /* return this message */
+}
+
+/* Test that purported mother is not a child of purported daughter
+ * Accepts: mother
+ * purported daugher
+ * Returns: T if circular parentage exists, else NIL
+ */
+
+long mail_thread_check_child (container_t mother,container_t daughter)
+{
+ if (mother) { /* only if mother non-NIL */
+ if (mother == daughter) return T;
+ for (daughter = CHILD (daughter); daughter; daughter = SIBLING (daughter))
+ if (mail_thread_check_child (mother,daughter)) return T;
+ }
+ return NIL;
+}
+
+
+/* Generate threadnodes from containers
+ * Accepts: Mail stream
+ * container
+ * flags
+ * Return: threadnode list
+ */
+
+THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags)
+{
+ THREADNODE *ret,*cur;
+ SORTCACHE *s;
+ container_t nxt;
+ /* for each container */
+ for (ret = cur = NIL; con; con = SIBLING (con)) {
+ s = CACHE (con); /* yes, get its sortcache */
+ /* create node for it */
+ if (ret) cur = cur->branch = mail_newthreadnode (s);
+ else ret = cur = mail_newthreadnode (s);
+ /* attach sequence or UID for non-dummy */
+ if (s) cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
+ /* attach the children */
+ if (nxt = CHILD (con)) cur->next = mail_thread_c2node (stream,nxt,flags);
+ }
+ return ret;
+}
+
+/* Sort thread tree by date
+ * Accepts: thread tree to sort
+ * qsort vector to sort
+ * Returns: sorted thread tree
+ */
+
+THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc)
+{
+ unsigned long i,j;
+ THREADNODE *cur;
+ /* sort children of each thread */
+ for (cur = thr; cur; cur = cur->branch)
+ if (cur->next) cur->next = mail_thread_sort (cur->next,tc);
+ /* Must do this in a separate pass since recursive call will clobber tc */
+ /* load threadcache and count nodes to sort */
+ for (i = 0, cur = thr; cur; cur = cur->branch) tc[i++] = cur;
+ if (i > 1) { /* only if need to sort */
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ /* relink root siblings */
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ }
+ return i ? tc[0] : NIL; /* return new head of list */
+}
+
+
+/* Thread compare date
+ * Accept: first message sort cache element
+ * second message sort cache element
+ * Returns: -1 if a1 < a2, 1 if a1 > a2
+ *
+ * This assumes that a sort cache element is either a message (with a
+ * sortcache entry) or a dummy with a message (with sortcache entry) child.
+ * This is true of both the ORDEREDSUBJECT (no dummies) and REFERENCES
+ * (dummies only at top-level, and with non-dummy children).
+ *
+ * If a new algorithm allows a dummy parent to have a dummy child, this
+ * routine must be changed if it is to be used by that algorithm.
+ *
+ * Messages with bogus dates are always sorted at the top.
+ */
+
+int mail_thread_compare_date (const void *a1,const void *a2)
+{
+ THREADNODE *t1 = *(THREADNODE **) a1;
+ THREADNODE *t2 = *(THREADNODE **) a2;
+ SORTCACHE *s1 = t1->sc ? t1->sc : t1->next->sc;
+ SORTCACHE *s2 = t2->sc ? t2->sc : t2->next->sc;
+ int ret = compare_ulong (s1->date,s2->date);
+ /* use number as final tie-breaker */
+ return ret ? ret : compare_ulong (s1->num,s2->num);
+}
+
+/* Mail parse sequence
+ * Accepts: mail stream
+ * sequence to parse
+ * Returns: T if parse successful, else NIL
+ */
+
+long mail_sequence (MAILSTREAM *stream,unsigned char *sequence)
+{
+ unsigned long i,j,x;
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
+ while (sequence && *sequence){/* while there is something to parse */
+ if (*sequence == '*') { /* maximum message */
+ if (stream->nmsgs) i = stream->nmsgs;
+ else {
+ MM_LOG ("No messages, so no maximum message number",ERROR);
+ return NIL;
+ }
+ sequence++; /* skip past * */
+ }
+ /* parse and validate message number */
+ else if (!isdigit (*sequence)) {
+ MM_LOG ("Syntax error in sequence",ERROR);
+ return NIL;
+ }
+ else if (!(i = strtoul (sequence,(char **) &sequence,10)) ||
+ (i > stream->nmsgs)) {
+ MM_LOG ("Sequence out of range",ERROR);
+ return NIL;
+ }
+ switch (*sequence) { /* see what the delimiter is */
+ case ':': /* sequence range */
+ if (*++sequence == '*') { /* maximum message */
+ if (stream->nmsgs) j = stream->nmsgs;
+ else {
+ MM_LOG ("No messages, so no maximum message number",ERROR);
+ return NIL;
+ }
+ sequence++; /* skip past * */
+ }
+ /* parse end of range */
+ else if (!(j = strtoul (sequence,(char **) &sequence,10)) ||
+ (j > stream->nmsgs)) {
+ MM_LOG ("Sequence range invalid",ERROR);
+ return NIL;
+ }
+ if (*sequence && *sequence++ != ',') {
+ MM_LOG ("Sequence range syntax error",ERROR);
+ return NIL;
+ }
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ /* mark each item in the sequence */
+ while (i <= j) mail_elt (stream,j--)->sequence = T;
+ break;
+ case ',': /* single message */
+ ++sequence; /* skip the delimiter, fall into end case */
+ case '\0': /* end of sequence, mark this message */
+ mail_elt (stream,i)->sequence = T;
+ break;
+ default: /* anything else is a syntax error! */
+ MM_LOG ("Sequence syntax error",ERROR);
+ return NIL;
+ }
+ }
+ return T; /* successfully parsed sequence */
+}
+
+/* Parse flag list
+ * Accepts: MAIL stream
+ * flag list as a character string
+ * pointer to user flags to return
+ * Returns: system flags
+ */
+
+long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf)
+{
+ char *t,*n,*s,tmp[MAILTMPLEN],msg[MAILTMPLEN];
+ short f = 0;
+ long i,j;
+ *uf = 0; /* initially no user flags */
+ if (flag && *flag) { /* no-op if no flag string */
+ /* check if a list and make sure valid */
+ if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) ||
+ (strlen (flag) >= MAILTMPLEN)) {
+ MM_LOG ("Bad flag list",ERROR);
+ return NIL;
+ }
+ /* copy the flag string w/o list construct */
+ strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i)));
+ tmp[j] = '\0';
+ while ((t = n) && *t) { /* parse the flags */
+ /* find end of flag */
+ if (n = strchr (t,' ')) *n++ = '\0';
+ if (*t == '\\') { /* system flag? */
+ if (!compare_cstring (t+1,"SEEN")) f |= fSEEN;
+ else if (!compare_cstring (t+1,"DELETED")) f |= fDELETED;
+ else if (!compare_cstring (t+1,"FLAGGED")) f |= fFLAGGED;
+ else if (!compare_cstring (t+1,"ANSWERED")) f |= fANSWERED;
+ else if (!compare_cstring (t+1,"DRAFT")) f |= fDRAFT;
+ else {
+ sprintf (msg,"Unsupported system flag: %.80s",t);
+ MM_LOG (msg,WARN);
+ }
+ }
+
+ else { /* keyword flag */
+ for (i = j = 0; /* user flag, search through table */
+ !i && (j < NUSERFLAGS) && (s = stream->user_flags[j]); ++j)
+ if (!compare_cstring (t,s)) *uf |= i = 1 << j;
+ if (!i) { /* flag not found, can it be created? */
+ if (stream->kwd_create && (j < NUSERFLAGS) && *t &&
+ (strlen (t) <= MAXUSERFLAG)) {
+ for (s = t; t && *s; s++) switch (*s) {
+ default: /* all other characters */
+ /* SPACE, CTL, or not CHAR */
+ if ((*s > ' ') && (*s < 0x7f)) break;
+ case '*': case '%': /* list_wildcards */
+ case '"': case '\\':/* quoted-specials */
+ /* atom_specials */
+ case '(': case ')': case '{':
+ case ']': /* resp-specials */
+ sprintf (msg,"Invalid flag: %.80s",t);
+ MM_LOG (msg,WARN);
+ t = NIL;
+ }
+ if (t) { /* only if valid */
+ *uf |= 1 << j; /* set the bit */
+ stream->user_flags[j] = cpystr (t);
+ /* if out of user flags */
+ if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
+ }
+ }
+ else {
+ if (*t) sprintf (msg,"Unknown flag: %.80s",t);
+ else strcpy (msg,"Empty flag invalid");
+ MM_LOG (msg,WARN);
+ }
+ }
+ }
+ }
+ }
+ return f;
+}
+
+/* Mail check network stream for usability with new name
+ * Accepts: MAIL stream
+ * candidate new name
+ * Returns: T if stream can be used, NIL otherwise
+ */
+
+long mail_usable_network_stream (MAILSTREAM *stream,char *name)
+{
+ NETMBX smb,nmb,omb;
+ return (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) &&
+ mail_valid_net_parse (name,&nmb) &&
+ mail_valid_net_parse (stream->mailbox,&smb) &&
+ mail_valid_net_parse (stream->original_mailbox,&omb) &&
+ ((!compare_cstring (smb.host,
+ trustdns ? tcp_canonical (nmb.host) : nmb.host)&&
+ !strcmp (smb.service,nmb.service) &&
+ (!nmb.port || (smb.port == nmb.port)) &&
+ (nmb.anoflag == stream->anonymous) &&
+ (!nmb.user[0] || !strcmp (smb.user,nmb.user))) ||
+ (!compare_cstring (omb.host,nmb.host) &&
+ !strcmp (omb.service,nmb.service) &&
+ (!nmb.port || (omb.port == nmb.port)) &&
+ (nmb.anoflag == stream->anonymous) &&
+ (!nmb.user[0] || !strcmp (omb.user,nmb.user))))) ? LONGT : NIL;
+}
+
+/* Mail data structure instantiation routines */
+
+
+/* Mail instantiate cache elt
+ * Accepts: initial message number
+ * Returns: new cache elt
+ */
+
+MESSAGECACHE *mail_new_cache_elt (unsigned long msgno)
+{
+ MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)),
+ 0,sizeof (MESSAGECACHE));
+ elt->lockcount = 1; /* initially only cache references it */
+ elt->msgno = msgno; /* message number */
+ return elt;
+}
+
+
+/* Mail instantiate envelope
+ * Returns: new envelope
+ */
+
+ENVELOPE *mail_newenvelope (void)
+{
+ return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE));
+}
+
+
+/* Mail instantiate address
+ * Returns: new address
+ */
+
+ADDRESS *mail_newaddr (void)
+{
+ return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
+}
+
+/* Mail instantiate body
+ * Returns: new body
+ */
+
+BODY *mail_newbody (void)
+{
+ return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
+}
+
+
+/* Mail initialize body
+ * Accepts: body
+ * Returns: body
+ */
+
+BODY *mail_initbody (BODY *body)
+{
+ memset ((void *) body,0,sizeof (BODY));
+ body->type = TYPETEXT; /* content type */
+ body->encoding = ENC7BIT; /* content encoding */
+ return body;
+}
+
+
+/* Mail instantiate body parameter
+ * Returns: new body part
+ */
+
+PARAMETER *mail_newbody_parameter (void)
+{
+ return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER));
+}
+
+
+/* Mail instantiate body part
+ * Returns: new body part
+ */
+
+PART *mail_newbody_part (void)
+{
+ PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART));
+ mail_initbody (&part->body); /* initialize the body */
+ return part;
+}
+
+
+/* Mail instantiate body message part
+ * Returns: new body message part
+ */
+
+MESSAGE *mail_newmsg (void)
+{
+ return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE));
+}
+
+/* Mail instantiate string list
+ * Returns: new string list
+ */
+
+STRINGLIST *mail_newstringlist (void)
+{
+ return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0,
+ sizeof (STRINGLIST));
+}
+
+
+/* Mail instantiate new search program
+ * Returns: new search program
+ */
+
+SEARCHPGM *mail_newsearchpgm (void)
+{
+ return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM));
+}
+
+
+/* Mail instantiate new search program
+ * Accepts: header line name
+ * Returns: new search program
+ */
+
+SEARCHHEADER *mail_newsearchheader (char *line,char *text)
+{
+ SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)),
+ 0,sizeof (SEARCHHEADER));
+ hdr->line.size = strlen ((char *) (hdr->line.data =
+ (unsigned char *) cpystr (line)));
+ hdr->text.size = strlen ((char *) (hdr->text.data =
+ (unsigned char *) cpystr (text)));
+ return hdr;
+}
+
+
+/* Mail instantiate new search set
+ * Returns: new search set
+ */
+
+SEARCHSET *mail_newsearchset (void)
+{
+ return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET));
+}
+
+
+/* Mail instantiate new search or
+ * Returns: new search or
+ */
+
+SEARCHOR *mail_newsearchor (void)
+{
+ SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0,
+ sizeof (SEARCHOR));
+ or->first = mail_newsearchpgm ();
+ or->second = mail_newsearchpgm ();
+ return or;
+}
+
+/* Mail instantiate new searchpgmlist
+ * Returns: new searchpgmlist
+ */
+
+SEARCHPGMLIST *mail_newsearchpgmlist (void)
+{
+ SEARCHPGMLIST *pgl = (SEARCHPGMLIST *)
+ memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST));
+ pgl->pgm = mail_newsearchpgm ();
+ return pgl;
+}
+
+
+/* Mail instantiate new sortpgm
+ * Returns: new sortpgm
+ */
+
+SORTPGM *mail_newsortpgm (void)
+{
+ return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM));
+}
+
+
+/* Mail instantiate new threadnode
+ * Accepts: sort cache for thread node
+ * Returns: new threadnode
+ */
+
+THREADNODE *mail_newthreadnode (SORTCACHE *sc)
+{
+ THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0,
+ sizeof (THREADNODE));
+ if (sc) thr->sc = sc; /* initialize sortcache */
+ return thr;
+}
+
+
+/* Mail instantiate new acllist
+ * Returns: new acllist
+ */
+
+ACLLIST *mail_newacllist (void)
+{
+ return (ACLLIST *) memset (fs_get (sizeof (ACLLIST)),0,sizeof (ACLLIST));
+}
+
+
+/* Mail instantiate new quotalist
+ * Returns: new quotalist
+ */
+
+QUOTALIST *mail_newquotalist (void)
+{
+ return (QUOTALIST *) memset (fs_get (sizeof (QUOTALIST)),0,
+ sizeof (QUOTALIST));
+}
+
+/* Mail garbage collection routines */
+
+
+/* Mail garbage collect body
+ * Accepts: pointer to body pointer
+ */
+
+void mail_free_body (BODY **body)
+{
+ if (*body) { /* only free if exists */
+ mail_free_body_data (*body);/* free its data */
+ fs_give ((void **) body); /* return body to free storage */
+ }
+}
+
+
+/* Mail garbage collect body data
+ * Accepts: body pointer
+ */
+
+void mail_free_body_data (BODY *body)
+{
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ mail_free_body_part (&body->nested.part);
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ if (body->subtype && !strcmp (body->subtype,"RFC822")) {
+ mail_free_stringlist (&body->nested.msg->lines);
+ mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS);
+ }
+ if (body->nested.msg) fs_give ((void **) &body->nested.msg);
+ break;
+ default:
+ break;
+ }
+ if (body->subtype) fs_give ((void **) &body->subtype);
+ mail_free_body_parameter (&body->parameter);
+ if (body->id) fs_give ((void **) &body->id);
+ if (body->description) fs_give ((void **) &body->description);
+ if (body->disposition.type) fs_give ((void **) &body->disposition.type);
+ if (body->disposition.parameter)
+ mail_free_body_parameter (&body->disposition.parameter);
+ if (body->language) mail_free_stringlist (&body->language);
+ if (body->location) fs_give ((void **) &body->location);
+ if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
+ if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
+ if (body->md5) fs_give ((void **) &body->md5);
+ if (mailfreebodysparep && body->sparep)
+ (*mailfreebodysparep) (&body->sparep);
+}
+
+/* Mail garbage collect body parameter
+ * Accepts: pointer to body parameter pointer
+ */
+
+void mail_free_body_parameter (PARAMETER **parameter)
+{
+ if (*parameter) { /* only free if exists */
+ if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
+ if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
+ /* run down the list as necessary */
+ mail_free_body_parameter (&(*parameter)->next);
+ /* return body part to free storage */
+ fs_give ((void **) parameter);
+ }
+}
+
+
+/* Mail garbage collect body part
+ * Accepts: pointer to body part pointer
+ */
+
+void mail_free_body_part (PART **part)
+{
+ if (*part) { /* only free if exists */
+ mail_free_body_data (&(*part)->body);
+ /* run down the list as necessary */
+ mail_free_body_part (&(*part)->next);
+ fs_give ((void **) part); /* return body part to free storage */
+ }
+}
+
+/* Mail garbage collect message cache
+ * Accepts: mail stream
+ *
+ * The message cache is set to NIL when this function finishes.
+ */
+
+void mail_free_cache (MAILSTREAM *stream)
+{
+ /* do driver specific stuff first */
+ mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS);
+ /* flush the cache */
+ (*mailcache) (stream,(long) 0,CH_INIT);
+}
+
+
+/* Mail garbage collect cache element
+ * Accepts: pointer to cache element pointer
+ */
+
+void mail_free_elt (MESSAGECACHE **elt)
+{
+ /* only free if exists and no sharers */
+ if (*elt && !--(*elt)->lockcount) {
+ mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS);
+ if (mailfreeeltsparep && (*elt)->sparep)
+ (*mailfreeeltsparep) (&(*elt)->sparep);
+ fs_give ((void **) elt);
+ }
+ else *elt = NIL; /* else simply drop pointer */
+}
+
+/* Mail garbage collect envelope
+ * Accepts: pointer to envelope pointer
+ */
+
+void mail_free_envelope (ENVELOPE **env)
+{
+ if (*env) { /* only free if exists */
+ if ((*env)->remail) fs_give ((void **) &(*env)->remail);
+ mail_free_address (&(*env)->return_path);
+ if ((*env)->date) fs_give ((void **) &(*env)->date);
+ mail_free_address (&(*env)->from);
+ mail_free_address (&(*env)->sender);
+ mail_free_address (&(*env)->reply_to);
+ if ((*env)->subject) fs_give ((void **) &(*env)->subject);
+ mail_free_address (&(*env)->to);
+ mail_free_address (&(*env)->cc);
+ mail_free_address (&(*env)->bcc);
+ if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
+ if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
+ if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
+ if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
+ if ((*env)->references) fs_give ((void **) &(*env)->references);
+ if (mailfreeenvelopesparep && (*env)->sparep)
+ (*mailfreeenvelopesparep) (&(*env)->sparep);
+ fs_give ((void **) env); /* return envelope to free storage */
+ }
+}
+
+
+/* Mail garbage collect address
+ * Accepts: pointer to address pointer
+ */
+
+void mail_free_address (ADDRESS **address)
+{
+ if (*address) { /* only free if exists */
+ if ((*address)->personal) fs_give ((void **) &(*address)->personal);
+ if ((*address)->adl) fs_give ((void **) &(*address)->adl);
+ if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
+ if ((*address)->host) fs_give ((void **) &(*address)->host);
+ if ((*address)->error) fs_give ((void **) &(*address)->error);
+ if ((*address)->orcpt.type) fs_give ((void **) &(*address)->orcpt.type);
+ if ((*address)->orcpt.addr) fs_give ((void **) &(*address)->orcpt.addr);
+ mail_free_address (&(*address)->next);
+ fs_give ((void **) address);/* return address to free storage */
+ }
+}
+
+
+/* Mail garbage collect stringlist
+ * Accepts: pointer to stringlist pointer
+ */
+
+void mail_free_stringlist (STRINGLIST **string)
+{
+ if (*string) { /* only free if exists */
+ if ((*string)->text.data) fs_give ((void **) &(*string)->text.data);
+ mail_free_stringlist (&(*string)->next);
+ fs_give ((void **) string); /* return string to free storage */
+ }
+}
+
+/* Mail garbage collect searchpgm
+ * Accepts: pointer to searchpgm pointer
+ */
+
+void mail_free_searchpgm (SEARCHPGM **pgm)
+{
+ if (*pgm) { /* only free if exists */
+ mail_free_searchset (&(*pgm)->msgno);
+ mail_free_searchset (&(*pgm)->uid);
+ mail_free_searchor (&(*pgm)->or);
+ mail_free_searchpgmlist (&(*pgm)->not);
+ mail_free_searchheader (&(*pgm)->header);
+ mail_free_stringlist (&(*pgm)->bcc);
+ mail_free_stringlist (&(*pgm)->body);
+ mail_free_stringlist (&(*pgm)->cc);
+ mail_free_stringlist (&(*pgm)->from);
+ mail_free_stringlist (&(*pgm)->keyword);
+ mail_free_stringlist (&(*pgm)->subject);
+ mail_free_stringlist (&(*pgm)->text);
+ mail_free_stringlist (&(*pgm)->to);
+ fs_give ((void **) pgm); /* return program to free storage */
+ }
+}
+
+
+/* Mail garbage collect searchheader
+ * Accepts: pointer to searchheader pointer
+ */
+
+void mail_free_searchheader (SEARCHHEADER **hdr)
+{
+ if (*hdr) { /* only free if exists */
+ if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data);
+ if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data);
+ mail_free_searchheader (&(*hdr)->next);
+ fs_give ((void **) hdr); /* return header to free storage */
+ }
+}
+
+
+/* Mail garbage collect searchset
+ * Accepts: pointer to searchset pointer
+ */
+
+void mail_free_searchset (SEARCHSET **set)
+{
+ if (*set) { /* only free if exists */
+ mail_free_searchset (&(*set)->next);
+ fs_give ((void **) set); /* return set to free storage */
+ }
+}
+
+/* Mail garbage collect searchor
+ * Accepts: pointer to searchor pointer
+ */
+
+void mail_free_searchor (SEARCHOR **orl)
+{
+ if (*orl) { /* only free if exists */
+ mail_free_searchpgm (&(*orl)->first);
+ mail_free_searchpgm (&(*orl)->second);
+ mail_free_searchor (&(*orl)->next);
+ fs_give ((void **) orl); /* return searchor to free storage */
+ }
+}
+
+
+/* Mail garbage collect search program list
+ * Accepts: pointer to searchpgmlist pointer
+ */
+
+void mail_free_searchpgmlist (SEARCHPGMLIST **pgl)
+{
+ if (*pgl) { /* only free if exists */
+ mail_free_searchpgm (&(*pgl)->pgm);
+ mail_free_searchpgmlist (&(*pgl)->next);
+ fs_give ((void **) pgl); /* return searchpgmlist to free storage */
+ }
+}
+
+
+/* Mail garbage collect namespace
+ * Accepts: poiner to namespace
+ */
+
+void mail_free_namespace (NAMESPACE **n)
+{
+ if (*n) {
+ fs_give ((void **) &(*n)->name);
+ mail_free_namespace (&(*n)->next);
+ mail_free_body_parameter (&(*n)->param);
+ fs_give ((void **) n); /* return namespace to free storage */
+ }
+}
+
+/* Mail garbage collect sort program
+ * Accepts: pointer to sortpgm pointer
+ */
+
+void mail_free_sortpgm (SORTPGM **pgm)
+{
+ if (*pgm) { /* only free if exists */
+ mail_free_sortpgm (&(*pgm)->next);
+ fs_give ((void **) pgm); /* return sortpgm to free storage */
+ }
+}
+
+
+/* Mail garbage collect thread node
+ * Accepts: pointer to threadnode pointer
+ */
+
+void mail_free_threadnode (THREADNODE **thr)
+{
+ if (*thr) { /* only free if exists */
+ mail_free_threadnode (&(*thr)->branch);
+ mail_free_threadnode (&(*thr)->next);
+ fs_give ((void **) thr); /* return threadnode to free storage */
+ }
+}
+
+
+/* Mail garbage collect acllist
+ * Accepts: pointer to acllist pointer
+ */
+
+void mail_free_acllist (ACLLIST **al)
+{
+ if (*al) { /* only free if exists */
+ if ((*al)->identifier) fs_give ((void **) &(*al)->identifier);
+ if ((*al)->rights) fs_give ((void **) &(*al)->rights);
+ mail_free_acllist (&(*al)->next);
+ fs_give ((void **) al); /* return acllist to free storage */
+ }
+}
+
+
+/* Mail garbage collect quotalist
+ * Accepts: pointer to quotalist pointer
+ */
+
+void mail_free_quotalist (QUOTALIST **ql)
+{
+ if (*ql) { /* only free if exists */
+ if ((*ql)->name) fs_give ((void **) &(*ql)->name);
+ mail_free_quotalist (&(*ql)->next);
+ fs_give ((void **) ql); /* return quotalist to free storage */
+ }
+}
+
+/* Link authenicator
+ * Accepts: authenticator to add to list
+ */
+
+void auth_link (AUTHENTICATOR *auth)
+{
+ if (!auth->valid || (*auth->valid) ()) {
+ AUTHENTICATOR **a = &mailauthenticators;
+ while (*a) a = &(*a)->next; /* find end of list of authenticators */
+ *a = auth; /* put authenticator at the end */
+ auth->next = NIL; /* this authenticator is the end of the list */
+ }
+}
+
+
+/* Authenticate access
+ * Accepts: mechanism name
+ * responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[])
+{
+ AUTHENTICATOR *auth;
+ for (auth = mailauthenticators; auth; auth = auth->next)
+ if (auth->server && !compare_cstring (auth->name,mechanism))
+ return (!(auth->flags & AU_DISABLE) &&
+ ((auth->flags & AU_SECURE) ||
+ !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))) ?
+ (*auth->server) (resp,argc,argv) : NIL;
+ return NIL; /* no authenticator found */
+}
+
+/* Lookup authenticator index
+ * Accepts: authenticator index
+ * Returns: authenticator, or 0 if not found
+ */
+
+AUTHENTICATOR *mail_lookup_auth (unsigned long i)
+{
+ AUTHENTICATOR *auth = mailauthenticators;
+ while (auth && --i) auth = auth->next;
+ return auth;
+}
+
+
+/* Lookup authenticator name
+ * Accepts: authenticator name
+ * required authenticator flags
+ * Returns: index in authenticator chain, or 0 if not found
+ */
+
+unsigned int mail_lookup_auth_name (char *mechanism,long flags)
+{
+ int i;
+ AUTHENTICATOR *auth;
+ for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
+ if (auth->client && !(flags & ~auth->flags) &&
+ !(auth->flags & AU_DISABLE) && !compare_cstring (auth->name,mechanism))
+ return i;
+ return 0;
+}
+
+/* Standard TCP/IP network driver */
+
+static NETDRIVER tcpdriver = {
+ tcp_open, /* open connection */
+ tcp_aopen, /* open preauthenticated connection */
+ tcp_getline, /* get a line */
+ tcp_getbuffer, /* get a buffer */
+ tcp_soutr, /* output pushed data */
+ tcp_sout, /* output string */
+ tcp_close, /* close connection */
+ tcp_host, /* return host name */
+ tcp_remotehost, /* return remote host name */
+ tcp_port, /* return port number */
+ tcp_localhost /* return local host name */
+};
+
+
+/* Network open
+ * Accepts: NETMBX specifier to open
+ * default network driver
+ * default port
+ * SSL driver
+ * SSL service name
+ * SSL driver port
+ * Returns: Network stream if success, else NIL
+ */
+
+NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
+ NETDRIVER *ssld,char *ssls,unsigned long sslp)
+{
+ NETSTREAM *stream = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long flags = mb->novalidate ? NET_NOVALIDATECERT : 0;
+ if (strlen (mb->host) >= NETMAXHOST) {
+ sprintf (tmp,"Invalid host name: %.80s",mb->host);
+ MM_LOG (tmp,ERROR);
+ }
+ /* use designated driver if given */
+ else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port,
+ flags);
+ else if (mb->sslflag && ssld) /* use ssl if sslflag lit */
+ stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,flags);
+ /* if trysslfirst and can open ssl... */
+ else if ((mb->trysslflag || trysslfirst) && ssld &&
+ (stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,
+ flags | NET_SILENT | NET_TRYSSL))) {
+ if (net_sout (stream,"",0)) mb->sslflag = T;
+ else {
+ net_close (stream); /* flush fake SSL stream */
+ stream = NIL;
+ }
+ }
+ /* default to TCP driver */
+ else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port,
+ flags);
+ return stream;
+}
+
+/* Network open worker routine
+ * Accepts: network driver
+ * host name
+ * service name to look up port
+ * port number if service name not found
+ * port number to override service name
+ * flags (passed on top of port)
+ * Returns: Network stream if success, else NIL
+ */
+
+NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
+ unsigned long port,unsigned long portoverride,
+ unsigned long flags)
+{
+ NETSTREAM *stream = NIL;
+ void *tstream;
+ if (service && (*service == '*')) {
+ flags |= NET_NOOPENTIMEOUT; /* mark that no timeout is desired */
+ ++service; /* no longer need the no timeout indicator */
+ }
+ if (portoverride) { /* explicit port number? */
+ service = NIL; /* yes, override service name */
+ port = portoverride; /* use that instead of default port */
+ }
+ if (tstream = (*dv->open) (host,service,port | flags)) {
+ stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
+ stream->stream = tstream;
+ stream->dtb = dv;
+ }
+ return stream;
+}
+
+
+/* Network authenticated open
+ * Accepts: network driver
+ * NETMBX specifier
+ * service specifier
+ * return user name buffer
+ * Returns: Network stream if success else NIL
+ */
+
+NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user)
+{
+ NETSTREAM *stream = NIL;
+ void *tstream;
+ if (!dv) dv = &tcpdriver; /* default to TCP driver */
+ if (tstream = (*dv->aopen) (mb,service,user)) {
+ stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
+ stream->stream = tstream;
+ stream->dtb = dv;
+ }
+ return stream;
+}
+
+/* Network receive line
+ * Accepts: Network stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *net_getline (NETSTREAM *stream)
+{
+ return (*stream->dtb->getline) (stream->stream);
+}
+
+
+/* Network receive buffer
+ * Accepts: Network stream (must be void * for use as readfn_t)
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long net_getbuffer (void *st,unsigned long size,char *buffer)
+{
+ NETSTREAM *stream = (NETSTREAM *) st;
+ return (*stream->dtb->getbuffer) (stream->stream,size,buffer);
+}
+
+
+/* Network send null-terminated string
+ * Accepts: Network stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long net_soutr (NETSTREAM *stream,char *string)
+{
+ return (*stream->dtb->soutr) (stream->stream,string);
+}
+
+
+/* Network send string
+ * Accepts: Network stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long net_sout (NETSTREAM *stream,char *string,unsigned long size)
+{
+ return (*stream->dtb->sout) (stream->stream,string,size);
+}
+
+/* Network close
+ * Accepts: Network stream
+ */
+
+void net_close (NETSTREAM *stream)
+{
+ if (stream->stream) (*stream->dtb->close) (stream->stream);
+ fs_give ((void **) &stream);
+}
+
+
+/* Network get host name
+ * Accepts: Network stream
+ * Returns: host name for this stream
+ */
+
+char *net_host (NETSTREAM *stream)
+{
+ return (*stream->dtb->host) (stream->stream);
+}
+
+
+/* Network get remote host name
+ * Accepts: Network stream
+ * Returns: host name for this stream
+ */
+
+char *net_remotehost (NETSTREAM *stream)
+{
+ return (*stream->dtb->remotehost) (stream->stream);
+}
+
+/* Network return port for this stream
+ * Accepts: Network stream
+ * Returns: port number for this stream
+ */
+
+unsigned long net_port (NETSTREAM *stream)
+{
+ return (*stream->dtb->port) (stream->stream);
+}
+
+
+/* Network get local host name
+ * Accepts: Network stream
+ * Returns: local host name
+ */
+
+char *net_localhost (NETSTREAM *stream)
+{
+ return (*stream->dtb->localhost) (stream->stream);
+}
diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h
new file mode 100644
index 00000000..174993e1
--- /dev/null
+++ b/imap/src/c-client/mail.h
@@ -0,0 +1,1838 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mailbox Access routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 22 November 1989
+ * Last Edited: 16 December 2008
+ */
+
+/* The Version */
+
+#define CCLIENTVERSION "2007e"
+
+/* Build parameters */
+
+#define CACHEINCREMENT 250 /* cache growth increments */
+#define MAILTMPLEN 1024 /* size of a temporary buffer */
+#define SENDBUFLEN 16385 /* size of temporary sending buffer, also
+ * used for SMTP commands and NETMBX generation
+ * buffer so shouldn't be made smaller than
+ * MAILTMPLEN. Note that there's a guard byte,
+ * so this is actually len+1. */
+#define MAXAUTHENTICATORS 8 /* maximum number of SASL authenticators */
+ /* maximum number of messages */
+#define MAXMESSAGES (unsigned long) 1000000
+#define MAXLOGINTRIALS 3 /* maximum number of client login attempts */
+#define MAXWILDCARDS 10 /* maximum wildcards allowed in LIST/LSUB */
+
+
+/* These can't be changed without changing code */
+
+#define NUSERFLAGS 30 /* maximum number of user flags */
+#define MAXUSERFLAG 50 /* maximum length of a user flag */
+#define BASEYEAR 1970 /* the year time began on Unix DON'T CHANGE */
+ /* default for unqualified addresses */
+#define BADHOST ".MISSING-HOST-NAME."
+ /* default for syntax errors in addresses */
+#define ERRHOST ".SYNTAX-ERROR."
+
+
+/* Coddle certain compilers' 6-character symbol limitation */
+
+#ifdef __COMPILER_KCC__
+#include "shortsym.h"
+#endif
+
+
+/* Function status code */
+
+#define NIL 0 /* convenient name */
+#define T 1 /* opposite of NIL */
+#define LONGT (long) 1 /* long T to pacify some compilers */
+#define VOIDT (void *) "" /* void T ditto */
+
+/* Global and Driver Parameters */
+
+ /* 0xx: driver and authenticator flags */
+#define ENABLE_DRIVER (long) 1
+#define DISABLE_DRIVER (long) 2
+#define ENABLE_AUTHENTICATOR (long) 3
+#define DISABLE_AUTHENTICATOR (long) 4
+#define ENABLE_DEBUG (long) 5
+#define DISABLE_DEBUG (long) 6
+#define HIDE_AUTHENTICATOR (long) 7
+#define UNHIDE_AUTHENTICATOR (long) 8
+ /* 1xx: c-client globals */
+#define GET_DRIVERS (long) 101
+#define SET_DRIVERS (long) 102
+#define GET_GETS (long) 103
+#define SET_GETS (long) 104
+#define GET_CACHE (long) 105
+#define SET_CACHE (long) 106
+#define GET_SMTPVERBOSE (long) 107
+#define SET_SMTPVERBOSE (long) 108
+#define GET_RFC822OUTPUT (long) 109
+#define SET_RFC822OUTPUT (long) 110
+#define GET_READPROGRESS (long) 111
+#define SET_READPROGRESS (long) 112
+#define GET_THREADERS (long) 113
+#define SET_THREADERS (long) 114
+#define GET_NAMESPACE (long) 115
+#define SET_NAMESPACE (long) 116
+#define GET_MAILPROXYCOPY (long) 117
+#define SET_MAILPROXYCOPY (long) 118
+#define GET_SERVICENAME (long) 119
+#define SET_SERVICENAME (long) 120
+#define GET_DRIVER (long) 121
+#define SET_DRIVER (long) 122
+#define GET_EXPUNGEATPING (long) 123
+#define SET_EXPUNGEATPING (long) 124
+#define GET_PARSEPHRASE (long) 125
+#define SET_PARSEPHRASE (long) 126
+#define GET_SSLDRIVER (long) 127
+#define SET_SSLDRIVER (long) 128
+#define GET_TRYSSLFIRST (long) 129
+#define SET_TRYSSLFIRST (long) 130
+#define GET_BLOCKNOTIFY (long) 131
+#define SET_BLOCKNOTIFY (long) 132
+#define GET_SORTRESULTS (long) 133
+#define SET_SORTRESULTS (long) 134
+#define GET_THREADRESULTS (long) 135
+#define SET_THREADRESULTS (long) 136
+#define GET_PARSELINE (long) 137
+#define SET_PARSELINE (long) 138
+#define GET_NEWSRCQUERY (long) 139
+#define SET_NEWSRCQUERY (long) 140
+#define GET_FREEENVELOPESPAREP (long) 141
+#define SET_FREEENVELOPESPAREP (long) 142
+#define GET_FREEELTSPAREP (long) 143
+#define SET_FREEELTSPAREP (long) 144
+#define GET_SSLSTART (long) 145
+#define SET_SSLSTART (long) 146
+#define GET_DEBUGSENSITIVE (long) 147
+#define SET_DEBUGSENSITIVE (long) 148
+#define GET_TCPDEBUG (long) 149
+#define SET_TCPDEBUG (long) 150
+#define GET_FREESTREAMSPAREP (long) 151
+#define SET_FREESTREAMSPAREP (long) 152
+#define GET_FREEBODYSPAREP (long) 153
+#define SET_FREEBODYSPAREP (long) 154
+#define GET_COPYUID (long) 155
+#define SET_COPYUID (long) 156
+#define GET_APPENDUID (long) 157
+#define SET_APPENDUID (long) 158
+#define GET_RFC822OUTPUTFULL (long) 159
+#define SET_RFC822OUTPUTFULL (long) 160
+#define GET_BLOCKENVINIT (long) 161
+#define SET_BLOCKENVINIT (long) 162
+
+ /* 2xx: environment */
+#define GET_USERNAME (long) 201
+#define SET_USERNAME (long) 202
+#define GET_HOMEDIR (long) 203
+#define SET_HOMEDIR (long) 204
+#define GET_LOCALHOST (long) 205
+#define SET_LOCALHOST (long) 206
+#define GET_SYSINBOX (long) 207
+#define SET_SYSINBOX (long) 208
+#define GET_USERPROMPT (long) 209
+#define SET_USERPROMPT (long) 210
+#define GET_DISABLEPLAINTEXT (long) 211
+#define SET_DISABLEPLAINTEXT (long) 212
+#define GET_CHROOTSERVER (long) 213
+#define SET_CHROOTSERVER (long) 214
+#define GET_ADVERTISETHEWORLD (long) 215
+#define SET_ADVERTISETHEWORLD (long) 216
+#define GET_DISABLEAUTOSHAREDNS (long) 217
+#define SET_DISABLEAUTOSHAREDNS (long) 218
+#define GET_MAILSUBDIR 219
+#define SET_MAILSUBDIR 220
+#define GET_DISABLE822TZTEXT 221
+#define SET_DISABLE822TZTEXT 222
+#define GET_LIMITEDADVERTISE (long) 223
+#define SET_LIMITEDADVERTISE (long) 224
+#define GET_LOGOUTHOOK (long) 225
+#define SET_LOGOUTHOOK (long) 226
+#define GET_LOGOUTDATA (long) 227
+#define SET_LOGOUTDATA (long) 228
+#define GET_EXTERNALAUTHID (long) 229
+#define SET_EXTERNALAUTHID (long) 230
+#define GET_SSLCAPATH (long) 231
+#define SET_SSLCAPATH (long) 232
+
+ /* 3xx: TCP/IP */
+#define GET_OPENTIMEOUT (long) 300
+#define SET_OPENTIMEOUT (long) 301
+#define GET_READTIMEOUT (long) 302
+#define SET_READTIMEOUT (long) 303
+#define GET_WRITETIMEOUT (long) 304
+#define SET_WRITETIMEOUT (long) 305
+#define GET_CLOSETIMEOUT (long) 306
+#define SET_CLOSETIMEOUT (long) 307
+#define GET_TIMEOUT (long) 308
+#define SET_TIMEOUT (long) 309
+#define GET_RSHTIMEOUT (long) 310
+#define SET_RSHTIMEOUT (long) 311
+#define GET_ALLOWREVERSEDNS (long) 312
+#define SET_ALLOWREVERSEDNS (long) 313
+#define GET_RSHCOMMAND (long) 314
+#define SET_RSHCOMMAND (long) 315
+#define GET_RSHPATH (long) 316
+#define SET_RSHPATH (long) 317
+#define GET_SSHTIMEOUT (long) 318
+#define SET_SSHTIMEOUT (long) 319
+#define GET_SSHCOMMAND (long) 320
+#define SET_SSHCOMMAND (long) 321
+#define GET_SSHPATH (long) 322
+#define SET_SSHPATH (long) 323
+#define GET_SSLCERTIFICATEQUERY (long) 324
+#define SET_SSLCERTIFICATEQUERY (long) 325
+#define GET_SSLFAILURE (long) 326
+#define SET_SSLFAILURE (long) 327
+#define GET_NEWSRCCANONHOST (long) 328
+#define SET_NEWSRCCANONHOST (long) 329
+#define GET_KINIT (long) 330
+#define SET_KINIT (long) 331
+#define GET_SSLCLIENTCERT (long) 332
+#define SET_SSLCLIENTCERT (long) 333
+#define GET_SSLCLIENTKEY (long) 334
+#define SET_SSLCLIENTKEY (long) 335
+#define GET_KERBEROS_CP_SVR_NAME (long) 336
+#define SET_KERBEROS_CP_SVR_NAME (long) 337
+
+ /* 4xx: network drivers */
+#define GET_MAXLOGINTRIALS (long) 400
+#define SET_MAXLOGINTRIALS (long) 401
+#define GET_LOOKAHEAD (long) 402
+#define SET_LOOKAHEAD (long) 403
+#define GET_IMAPPORT (long) 404
+#define SET_IMAPPORT (long) 405
+#define GET_PREFETCH (long) 406
+#define SET_PREFETCH (long) 407
+#define GET_CLOSEONERROR (long) 408
+#define SET_CLOSEONERROR (long) 409
+#define GET_POP3PORT (long) 410
+#define SET_POP3PORT (long) 411
+#define GET_UIDLOOKAHEAD (long) 412
+#define SET_UIDLOOKAHEAD (long) 413
+#define GET_NNTPPORT (long) 414
+#define SET_NNTPPORT (long) 415
+#define GET_IMAPENVELOPE (long) 416
+#define SET_IMAPENVELOPE (long) 417
+#define GET_IMAPREFERRAL (long) 418
+#define SET_IMAPREFERRAL (long) 419
+#define GET_SSLIMAPPORT (long) 420
+#define SET_SSLIMAPPORT (long) 421
+#define GET_SSLPOPPORT (long) 422
+#define SET_SSLPOPPORT (long) 423
+#define GET_SSLNNTPPORT (long) 424
+#define SET_SSLNNTPPORT (long) 425
+#define GET_SSLSMTPPORT (long) 426
+#define SET_SSLSMTPPORT (long) 427
+#define GET_SMTPPORT (long) 428
+#define SET_SMTPPORT (long) 429
+#define GET_IMAPEXTRAHEADERS (long) 430
+#define SET_IMAPEXTRAHEADERS (long) 431
+#define GET_ACL (long) 432
+#define SET_ACL (long) 433
+#define GET_LISTRIGHTS (long) 434
+#define SET_LISTRIGHTS (long) 435
+#define GET_MYRIGHTS (long) 436
+#define SET_MYRIGHTS (long) 437
+#define GET_QUOTA (long) 438
+#define SET_QUOTA (long) 439
+#define GET_QUOTAROOT (long) 440
+#define SET_QUOTAROOT (long) 441
+#define GET_IMAPTRYSSL (long) 442
+#define SET_IMAPTRYSSL (long) 443
+#define GET_FETCHLOOKAHEAD (long) 444
+#define SET_FETCHLOOKAHEAD (long) 445
+#define GET_NNTPRANGE (long) 446
+#define SET_NNTPRANGE (long) 447
+#define GET_NNTPHIDEPATH (long) 448
+#define SET_NNTPHIDEPATH (long) 449
+#define GET_SENDCOMMAND (long) 450
+#define SET_SENDCOMMAND (long) 451
+#define GET_IDLETIMEOUT (long) 452
+#define SET_IDLETIMEOUT (long) 453
+#define GET_FETCHLOOKAHEADLIMIT (long) 454
+#define SET_FETCHLOOKAHEADLIMIT (long) 455
+
+ /* 5xx: local file drivers */
+#define GET_MBXPROTECTION (long) 500
+#define SET_MBXPROTECTION (long) 501
+#define GET_DIRPROTECTION (long) 502
+#define SET_DIRPROTECTION (long) 503
+#define GET_LOCKPROTECTION (long) 504
+#define SET_LOCKPROTECTION (long) 505
+#define GET_FROMWIDGET (long) 506
+#define SET_FROMWIDGET (long) 507
+#define GET_NEWSACTIVE (long) 508
+#define SET_NEWSACTIVE (long) 509
+#define GET_NEWSSPOOL (long) 510
+#define SET_NEWSSPOOL (long) 511
+#define GET_NEWSRC (long) 512
+#define SET_NEWSRC (long) 513
+#define GET_EXTENSION (long) 514
+#define SET_EXTENSION (long) 515
+#define GET_DISABLEFCNTLLOCK (long) 516
+#define SET_DISABLEFCNTLLOCK (long) 517
+#define GET_LOCKEACCESERROR (long) 518
+#define SET_LOCKEACCESERROR (long) 519
+#define GET_LISTMAXLEVEL (long) 520
+#define SET_LISTMAXLEVEL (long) 521
+#define GET_ANONYMOUSHOME (long) 522
+#define SET_ANONYMOUSHOME (long) 523
+#define GET_FTPHOME (long) 524
+#define SET_FTPHOME (long) 525
+#define GET_PUBLICHOME (long) 526
+#define SET_PUBLICHOME (long) 527
+#define GET_SHAREDHOME (long) 528
+#define SET_SHAREDHOME (long) 529
+#define GET_MHPROFILE (long) 530
+#define SET_MHPROFILE (long) 531
+#define GET_MHPATH (long) 532
+#define SET_MHPATH (long) 533
+#define GET_ONETIMEEXPUNGEATPING (long) 534
+#define SET_ONETIMEEXPUNGEATPING (long) 535
+#define GET_USERHASNOLIFE (long) 536
+#define SET_USERHASNOLIFE (long) 537
+#define GET_FTPPROTECTION (long) 538
+#define SET_FTPPROTECTION (long) 539
+#define GET_PUBLICPROTECTION (long) 540
+#define SET_PUBLICPROTECTION (long) 541
+#define GET_SHAREDPROTECTION (long) 542
+#define SET_SHAREDPROTECTION (long) 543
+#define GET_LOCKTIMEOUT (long) 544
+#define SET_LOCKTIMEOUT (long) 545
+#define GET_NOTIMEZONES (long) 546
+#define SET_NOTIMEZONES (long) 547
+#define GET_HIDEDOTFILES (long) 548
+#define SET_HIDEDOTFILES (long) 549
+#define GET_FTPDIRPROTECTION (long) 550
+#define SET_FTPDIRPROTECTION (long) 551
+#define GET_PUBLICDIRPROTECTION (long) 552
+#define SET_PUBLICDIRPROTECTION (long) 553
+#define GET_SHAREDDIRPROTECTION (long) 554
+#define SET_SHAREDDIRPROTECTION (long) 555
+#define GET_TRUSTDNS (long) 556
+#define SET_TRUSTDNS (long) 557
+#define GET_SASLUSESPTRNAME (long) 558
+#define SET_SASLUSESPTRNAME (long) 559
+#define GET_NETFSSTATBUG (long) 560
+#define SET_NETFSSTATBUG (long) 561
+#define GET_SNARFMAILBOXNAME (long) 562
+#define SET_SNARFMAILBOXNAME (long) 563
+#define GET_SNARFINTERVAL (long) 564
+#define SET_SNARFINTERVAL (long) 565
+#define GET_SNARFPRESERVE (long) 566
+#define SET_SNARFPRESERVE (long) 567
+#define GET_INBOXPATH (long) 568
+#define SET_INBOXPATH (long) 569
+#define GET_DIRFMTTEST (long) 570
+#define SET_DIRFMTTEST (long) 571
+#define GET_SCANCONTENTS (long) 572
+#define SET_SCANCONTENTS (long) 573
+#define GET_MHALLOWINBOX (long) 574
+#define SET_MHALLOWINBOX (long) 575
+
+/* Driver flags */
+
+#define DR_DISABLE (long) 0x1 /* driver is disabled */
+#define DR_LOCAL (long) 0x2 /* local file driver */
+#define DR_MAIL (long) 0x4 /* supports mail */
+#define DR_NEWS (long) 0x8 /* supports news */
+#define DR_READONLY (long) 0x10 /* driver only allows readonly access */
+#define DR_NOFAST (long) 0x20 /* "fast" data is slow (whole msg fetch) */
+#define DR_NAMESPACE (long) 0x40/* driver has a special namespace */
+#define DR_LOWMEM (long) 0x80 /* low amounts of memory available */
+#define DR_LOCKING (long) 0x100 /* driver does locking */
+#define DR_CRLF (long) 0x200 /* driver internal form uses CRLF newlines */
+#define DR_NOSTICKY (long) 0x400/* driver does not support sticky UIDs */
+#define DR_RECYCLE (long) 0x800 /* driver does stream recycling */
+#define DR_XPOINT (long) 0x1000 /* needs to be checkpointed */
+ /* driver has no real internal date */
+#define DR_NOINTDATE (long) 0x2000
+ /* driver does not announce new mail */
+#define DR_NONEWMAIL (long) 0x4000
+ /* driver does not announce new mail when RO */
+#define DR_NONEWMAILRONLY (long) 0x8000
+ /* driver can be halfopen */
+#define DR_HALFOPEN (long) 0x10000
+#define DR_DIRFMT (long) 0x20000/* driver is a directory-format */
+#define DR_MODSEQ (long) 0x40000/* driver supports modseqs */
+
+
+/* Cache management function codes */
+
+#define CH_INIT (long) 10 /* initialize cache */
+#define CH_SIZE (long) 11 /* (re-)size the cache */
+#define CH_MAKEELT (long) 30 /* return elt, make if needed */
+#define CH_ELT (long) 31 /* return elt if exists */
+#define CH_SORTCACHE (long) 35 /* return sortcache entry, make if needed */
+#define CH_FREE (long) 40 /* free space used by elt */
+ /* free space used by sortcache */
+#define CH_FREESORTCACHE (long) 43
+#define CH_EXPUNGE (long) 45 /* delete elt pointer from list */
+
+
+/* Mailbox open options
+ * For compatibility with the past, OP_DEBUG must always be 1.
+ */
+
+#define OP_DEBUG (long) 0x1 /* debug protocol negotiations */
+#define OP_READONLY (long) 0x2 /* read-only open */
+#define OP_ANONYMOUS (long) 0x4 /* anonymous open of newsgroup */
+#define OP_SHORTCACHE (long) 0x8/* short (elt-only) caching */
+#define OP_SILENT (long) 0x10 /* don't pass up events (internal use) */
+#define OP_PROTOTYPE (long) 0x20/* return driver prototype */
+#define OP_HALFOPEN (long) 0x40 /* half-open (IMAP connect but no select) */
+#define OP_EXPUNGE (long) 0x80 /* silently expunge recycle stream */
+#define OP_SECURE (long) 0x100 /* don't do non-secure authentication */
+#define OP_TRYSSL (long) 0x200 /* try SSL first */
+ /* use multiple newsrc files */
+#define OP_MULNEWSRC (long) 0x400
+#define OP_NOKOD (long) 0x800 /* suppress kiss-of-death */
+#define OP_SNIFF (long) 0x1000 /* metadata only open */
+ /* reserved for application use */
+#define OP_RESERVED (unsigned long) 0xff000000
+
+
+/* Net open options */
+
+ /* no error messages */
+#define NET_SILENT ((unsigned long) 0x80000000)
+ /* no validation of SSL certificates */
+#define NET_NOVALIDATECERT ((unsigned long) 0x40000000)
+ /* no open timeout */
+#define NET_NOOPENTIMEOUT ((unsigned long) 0x20000000)
+ /* TLS not SSL */
+#define NET_TLSCLIENT ((unsigned long) 0x10000000)
+ /* try SSL mode */
+#define NET_TRYSSL ((unsigned long) 0x8000000)
+
+/* Close options */
+
+#define CL_EXPUNGE (long) 1 /* expunge silently */
+
+
+/* Fetch options */
+
+#define FT_UID (long) 0x1 /* argument is a UID */
+#define FT_PEEK (long) 0x2 /* peek at data */
+#define FT_NOT (long) 0x4 /* NOT flag for header lines fetch */
+#define FT_INTERNAL (long) 0x8 /* text can be internal strings */
+ /* IMAP prefetch text when fetching header */
+#define FT_PREFETCHTEXT (long) 0x20
+#define FT_NOHDRS (long) 0x40 /* suppress fetching extra headers (note that
+ this breaks news handling) */
+#define FT_NEEDENV (long) 0x80 /* (internal use) include envelope */
+#define FT_NEEDBODY (long) 0x100/* (internal use) include body structure */
+ /* no fetch lookahead */
+#define FT_NOLOOKAHEAD (long) 0x200
+ /* (internal use) lookahead in hdr searching */
+#define FT_SEARCHLOOKAHEAD (long) 0x400
+ /* stringstruct return hack */
+#define FT_RETURNSTRINGSTRUCT (long) 0x800
+
+
+/* Flagging options */
+
+#define ST_UID (long) 0x1 /* argument is a UID sequence */
+#define ST_SILENT (long) 0x2 /* don't return results */
+#define ST_SET (long) 0x4 /* set vs. clear */
+
+
+/* Expunge options */
+
+#define EX_UID (long) 0x1 /* argument is a UID sequence */
+
+
+/* Copy options */
+
+#define CP_UID (long) 0x1 /* argument is a UID sequence */
+#define CP_MOVE (long) 0x2 /* delete from source after copying */
+ /* set debug in any created stream */
+#define CP_DEBUG (long) 0x20000000
+
+/* Search/sort/thread options */
+
+#define SE_UID (long) 0x1 /* return UID */
+#define SE_FREE (long) 0x2 /* free search program after finished */
+#define SE_NOPREFETCH (long) 0x4/* no search prefetching */
+#define SO_FREE (long) 0x8 /* free sort program after finished */
+#define SE_NOSERVER (long) 0x10 /* don't do server-based search/sort/thread */
+#define SE_RETAIN (long) 0x20 /* retain previous search results */
+#define SO_OVERVIEW (long) 0x40 /* use overviews in searching (NNTP only) */
+#define SE_NEEDBODY (long) 0x80 /* include body structure in prefetch */
+#define SE_NOHDRS (long) 0x100 /* suppress prefetching extra headers (note
+ that this breaks news handling) */
+#define SE_NOLOCAL (long) 0x200 /* no local retry (IMAP only) */
+
+#define SO_NOSERVER SE_NOSERVER /* compatibility name */
+#define SE_SILLYOK (long) 0x400 /* allow silly searches */
+
+
+/* Status options */
+
+#define SA_MESSAGES (long) 0x1 /* number of messages */
+#define SA_RECENT (long) 0x2 /* number of recent messages */
+#define SA_UNSEEN (long) 0x4 /* number of unseen messages */
+#define SA_UIDNEXT (long) 0x8 /* next UID to be assigned */
+ /* UID validity value */
+#define SA_UIDVALIDITY (long) 0x10
+ /* set OP_DEBUG on any created stream */
+#define SA_DEBUG (long) 0x10000000
+ /* use multiple newsrcs */
+#define SA_MULNEWSRC (long) 0x20000000
+
+/* Mailgets flags */
+
+#define MG_UID (long) 0x1 /* message number is a UID */
+#define MG_COPY (long) 0x2 /* must return copy of argument */
+
+/* SASL authenticator categories */
+
+#define AU_SECURE (long) 0x1 /* /secure allowed */
+#define AU_AUTHUSER (long) 0x2 /* /authuser=xxx allowed */
+ /* authenticator hidden */
+#define AU_HIDE (long) 0x10000000
+ /* authenticator disabled */
+#define AU_DISABLE (long) 0x20000000
+
+
+/* Garbage collection flags */
+
+#define GC_ELT (long) 0x1 /* message cache elements */
+#define GC_ENV (long) 0x2 /* envelopes and bodies */
+#define GC_TEXTS (long) 0x4 /* cached texts */
+
+
+/* mm_log()/mm_notify() condition codes */
+
+#define WARN (long) 1 /* mm_log warning type */
+#define ERROR (long) 2 /* mm_log error type */
+#define PARSE (long) 3 /* mm_log parse error type */
+#define BYE (long) 4 /* mm_notify stream dying */
+#define TCPDEBUG (long) 5 /* mm_log TCP debug babble */
+
+
+/* Bits from mail_parse_flags(). Don't change these, since the header format
+ * used by tenex, mtx, and mbx corresponds to these bits.
+ */
+
+#define fSEEN 0x1
+#define fDELETED 0x2
+#define fFLAGGED 0x4
+#define fANSWERED 0x8
+#define fOLD 0x10
+#define fDRAFT 0x20
+
+#define fEXPUNGED 0x8000 /* internal flag */
+
+/* Bits for mm_list() and mm_lsub() */
+
+/* Note that (LATT_NOINFERIORS LATT_HASCHILDREN LATT_HASNOCHILDREN) and
+ * (LATT_NOSELECT LATT_MARKED LATT_UNMARKED) each have eight possible states,
+ * but only four of these are valid. The other four are silly states which
+ * while invalid can unfortunately be expressed in the IMAP protocol.
+ */
+
+ /* terminal node in hierarchy */
+#define LATT_NOINFERIORS (long) 0x1
+ /* name can not be selected */
+#define LATT_NOSELECT (long) 0x2
+ /* changed since last accessed */
+#define LATT_MARKED (long) 0x4
+ /* accessed since last changed */
+#define LATT_UNMARKED (long) 0x8
+ /* name has referral to remote mailbox */
+#define LATT_REFERRAL (long) 0x10
+ /* has selectable inferiors */
+#define LATT_HASCHILDREN (long) 0x20
+ /* has no selectable inferiors */
+#define LATT_HASNOCHILDREN (long) 0x40
+
+
+/* Sort functions */
+
+#define SORTDATE 0 /* date */
+#define SORTARRIVAL 1 /* arrival date */
+#define SORTFROM 2 /* from */
+#define SORTSUBJECT 3 /* subject */
+#define SORTTO 4 /* to */
+#define SORTCC 5 /* cc */
+#define SORTSIZE 6 /* size */
+
+
+/* imapreferral_t codes */
+
+#define REFAUTHFAILED (long) 0 /* authentication referral -- not logged in */
+#define REFAUTH (long) 1 /* authentication referral -- logged in */
+#define REFSELECT (long) 2 /* select referral */
+#define REFCREATE (long) 3
+#define REFDELETE (long) 4
+#define REFRENAME (long) 5
+#define REFSUBSCRIBE (long) 6
+#define REFUNSUBSCRIBE (long) 7
+#define REFSTATUS (long) 8
+#define REFCOPY (long) 9
+#define REFAPPEND (long) 10
+
+
+/* sendcommand_t codes */
+
+ /* expunge response deferred */
+#define SC_EXPUNGEDEFERRED (long) 1
+
+/* Block notification codes */
+
+#define BLOCK_NONE 0 /* not blocked */
+#define BLOCK_SENSITIVE 1 /* sensitive code, disallow alarms */
+#define BLOCK_NONSENSITIVE 2 /* non-sensitive code, allow alarms */
+#define BLOCK_DNSLOOKUP 10 /* blocked on DNS lookup */
+#define BLOCK_TCPOPEN 11 /* blocked on TCP open */
+#define BLOCK_TCPREAD 12 /* blocked on TCP read */
+#define BLOCK_TCPWRITE 13 /* blocked on TCP write */
+#define BLOCK_TCPCLOSE 14 /* blocked on TCP close */
+#define BLOCK_FILELOCK 20 /* blocked on file locking */
+
+
+/* In-memory sized-text */
+
+#define SIZEDTEXT struct mail_sizedtext
+
+SIZEDTEXT {
+ unsigned char *data; /* text */
+ unsigned long size; /* size of text in octets */
+};
+
+
+/* String list */
+
+#define STRINGLIST struct string_list
+
+STRINGLIST {
+ SIZEDTEXT text; /* string text */
+ STRINGLIST *next;
+};
+
+
+/* Parse results from mail_valid_net_parse */
+
+#define NETMAXHOST 256
+#define NETMAXUSER 65
+#define NETMAXMBX (MAILTMPLEN/4)
+#define NETMAXSRV 21
+typedef struct net_mailbox {
+ char host[NETMAXHOST]; /* host name (may be canonicalized) */
+ char orighost[NETMAXHOST]; /* host name before canonicalization */
+ char user[NETMAXUSER]; /* user name */
+ char authuser[NETMAXUSER]; /* authentication user name */
+ char mailbox[NETMAXMBX]; /* mailbox name */
+ char service[NETMAXSRV]; /* service name */
+ unsigned long port; /* TCP port number */
+ unsigned int anoflag : 1; /* anonymous */
+ unsigned int dbgflag : 1; /* debug flag */
+ unsigned int secflag : 1; /* secure flag */
+ unsigned int sslflag : 1; /* SSL driver flag */
+ unsigned int trysslflag : 1; /* try SSL driver first flag */
+ unsigned int novalidate : 1; /* don't validate certificates */
+ unsigned int tlsflag : 1; /* TLS flag */
+ unsigned int notlsflag : 1; /* do not do TLS flag */
+ unsigned int readonlyflag : 1;/* want readonly */
+ unsigned int norsh : 1; /* don't use rsh/ssh */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int tlssslv23 : 1; /* force SSLv23 client method over TLS */
+} NETMBX;
+
+/* Item in an address list */
+
+#define ADDRESS struct mail_address
+
+ADDRESS {
+ char *personal; /* personal name phrase */
+ char *adl; /* at-domain-list source route */
+ char *mailbox; /* mailbox name */
+ char *host; /* domain name of mailbox's host */
+ char *error; /* error in address from SMTP module */
+ struct {
+ char *type; /* address type (default "rfc822") */
+ char *addr; /* address as xtext */
+ } orcpt;
+ ADDRESS *next; /* pointer to next address in list */
+};
+
+
+/* Message envelope */
+
+typedef struct mail_envelope {
+ unsigned int ngpathexists : 1; /* newsgroups may be bogus */
+ unsigned int incomplete : 1; /* envelope may be incomplete */
+ unsigned int imapenvonly : 1; /* envelope only has IMAP envelope */
+ char *remail; /* remail header if any */
+ ADDRESS *return_path; /* error return address */
+ unsigned char *date; /* message composition date string */
+ ADDRESS *from; /* originator address list */
+ ADDRESS *sender; /* sender address list */
+ ADDRESS *reply_to; /* reply address list */
+ char *subject; /* message subject string */
+ ADDRESS *to; /* primary recipient list */
+ ADDRESS *cc; /* secondary recipient list */
+ ADDRESS *bcc; /* blind secondary recipient list */
+ char *in_reply_to; /* replied message ID */
+ char *message_id; /* message ID */
+ char *newsgroups; /* USENET newsgroups */
+ char *followup_to; /* USENET reply newsgroups */
+ char *references; /* USENET references */
+ void *sparep; /* spare pointer reserved for main program */
+} ENVELOPE;
+
+/* Primary body types */
+/* If you change any of these you must also change body_types in rfc822.c */
+
+#define TYPETEXT 0 /* unformatted text */
+#define TYPEMULTIPART 1 /* multiple part */
+#define TYPEMESSAGE 2 /* encapsulated message */
+#define TYPEAPPLICATION 3 /* application data */
+#define TYPEAUDIO 4 /* audio */
+#define TYPEIMAGE 5 /* static image */
+#define TYPEVIDEO 6 /* video */
+#define TYPEMODEL 7 /* model */
+#define TYPEOTHER 8 /* unknown */
+#define TYPEMAX 15 /* maximum type code */
+
+
+/* Body encodings */
+/* If you change any of these you must also change body_encodings in rfc822.c
+ */
+
+#define ENC7BIT 0 /* 7 bit SMTP semantic data */
+#define ENC8BIT 1 /* 8 bit SMTP semantic data */
+#define ENCBINARY 2 /* 8 bit binary data */
+#define ENCBASE64 3 /* base-64 encoded data */
+#define ENCQUOTEDPRINTABLE 4 /* human-readable 8-as-7 bit data */
+#define ENCOTHER 5 /* unknown */
+#define ENCMAX 10 /* maximum encoding code */
+
+
+/* Body contents */
+
+#define BODY struct mail_bodystruct
+#define MESSAGE struct mail_body_message
+#define PARAMETER struct mail_body_parameter
+#define PART struct mail_body_part
+#define PARTTEXT struct mail_body_text
+
+/* Message body text */
+
+PARTTEXT {
+ unsigned long offset; /* offset from body origin */
+ SIZEDTEXT text; /* text */
+};
+
+
+/* Message body structure */
+
+BODY {
+ unsigned short type; /* body primary type */
+ unsigned short encoding; /* body transfer encoding */
+ char *subtype; /* subtype string */
+ PARAMETER *parameter; /* parameter list */
+ char *id; /* body identifier */
+ char *description; /* body description */
+ struct { /* body disposition */
+ char *type; /* disposition type */
+ PARAMETER *parameter; /* disposition parameters */
+ } disposition;
+ STRINGLIST *language; /* body language */
+ char *location; /* body content URI */
+ PARTTEXT mime; /* MIME header */
+ PARTTEXT contents; /* body part contents */
+ union { /* different ways of accessing contents */
+ PART *part; /* body part list */
+ MESSAGE *msg; /* body encapsulated message */
+ } nested;
+ struct {
+ unsigned long lines; /* size of text in lines */
+ unsigned long bytes; /* size of text in octets */
+ } size;
+ char *md5; /* MD5 checksum */
+ void *sparep; /* spare pointer reserved for main program */
+};
+
+
+/* Parameter list */
+
+PARAMETER {
+ char *attribute; /* parameter attribute name */
+ char *value; /* parameter value */
+ PARAMETER *next; /* next parameter in list */
+};
+
+
+/* Multipart content list */
+
+PART {
+ BODY body; /* body information for this part */
+ PART *next; /* next body part */
+};
+
+
+/* RFC-822 Message */
+
+MESSAGE {
+ ENVELOPE *env; /* message envelope */
+ BODY *body; /* message body */
+ PARTTEXT full; /* full message */
+ STRINGLIST *lines; /* lines used to filter header */
+ PARTTEXT header; /* header text */
+ PARTTEXT text; /* body text */
+};
+
+/* Entry in the message cache array */
+
+typedef struct message_cache {
+ unsigned long msgno; /* message number */
+ unsigned int lockcount : 8; /* non-zero if multiple references */
+ unsigned long rfc822_size; /* # of bytes of message as raw RFC822 */
+ struct { /* c-client internal use only */
+ unsigned long uid; /* message unique ID */
+ unsigned long mod; /* modseq */
+ PARTTEXT special; /* special text pointers */
+ MESSAGE msg; /* internal message pointers */
+ union { /* driver internal use */
+ unsigned long data;
+ void *ptr;
+ } spare;
+ unsigned int sequence : 1; /* saved sequence bit */
+ unsigned int dirty : 1; /* driver internal use */
+ unsigned int filter : 1; /* driver internal use */
+ unsigned int ghost : 1; /* driver internal use */
+ } private;
+ /* internal date */
+ unsigned int day : 5; /* day of month (1-31) */
+ unsigned int month : 4; /* month of year (1-12) */
+ unsigned int year : 7; /* year since BASEYEAR (expires in 127 yrs) */
+ unsigned int hours: 5; /* hours (0-23) */
+ unsigned int minutes: 6; /* minutes (0-59) */
+ unsigned int seconds: 6; /* seconds (0-59) */
+ unsigned int zoccident : 1; /* non-zero if west of UTC */
+ unsigned int zhours : 4; /* hours from UTC (0-12) */
+ unsigned int zminutes: 6; /* minutes (0-59) */
+ /* system flags */
+ unsigned int seen : 1; /* system Seen flag */
+ unsigned int deleted : 1; /* system Deleted flag */
+ unsigned int flagged : 1; /* system Flagged flag */
+ unsigned int answered : 1; /* system Answered flag */
+ unsigned int draft : 1; /* system Draft flag */
+ unsigned int recent : 1; /* system Recent flag */
+ /* message status */
+ unsigned int valid : 1; /* elt has valid flags */
+ unsigned int searched : 1; /* message was searched */
+ unsigned int sequence : 1; /* message is in sequence */
+ /* reserved for use by main program */
+ unsigned int spare : 1; /* first spare bit */
+ unsigned int spare2 : 1; /* second spare bit */
+ unsigned int spare3 : 1; /* third spare bit */
+ unsigned int spare4 : 1; /* fourth spare bit */
+ unsigned int spare5 : 1; /* fifth spare bit */
+ unsigned int spare6 : 1; /* sixth spare bit */
+ unsigned int spare7 : 1; /* seventh spare bit */
+ unsigned int spare8 : 1; /* eighth spare bit */
+ void *sparep; /* spare pointer */
+ unsigned long user_flags; /* user-assignable flags */
+} MESSAGECACHE;
+
+/* String structure */
+
+#define STRINGDRIVER struct string_driver
+
+typedef struct mailstring {
+ void *data; /* driver-dependent data */
+ unsigned long data1; /* driver-dependent data */
+ unsigned long size; /* total length of string */
+ char *chunk; /* base address of chunk */
+ unsigned long chunksize; /* size of chunk */
+ unsigned long offset; /* offset of this chunk in base */
+ char *curpos; /* current position in chunk */
+ unsigned long cursize; /* number of bytes remaining in chunk */
+ STRINGDRIVER *dtb; /* driver that handles this type of string */
+} STRING;
+
+
+/* Dispatch table for string driver */
+
+STRINGDRIVER {
+ /* initialize string driver */
+ void (*init) (STRING *s,void *data,unsigned long size);
+ /* get next character in string */
+ char (*next) (STRING *s);
+ /* set position in string */
+ void (*setpos) (STRING *s,unsigned long i);
+};
+
+
+/* Stringstruct access routines */
+
+#define INIT(s,d,data,size) ((*((s)->dtb = &d)->init) (s,data,size))
+#define SIZE(s) ((s)->size - GETPOS (s))
+#define CHR(s) (*(s)->curpos)
+#define SNX(s) (--(s)->cursize ? *(s)->curpos++ : (*(s)->dtb->next) (s))
+#define GETPOS(s) ((s)->offset + ((s)->curpos - (s)->chunk))
+#define SETPOS(s,i) (*(s)->dtb->setpos) (s,i)
+
+/* Search program */
+
+#define SEARCHPGM struct search_program
+#define SEARCHHEADER struct search_header
+#define SEARCHSET struct search_set
+#define SEARCHOR struct search_or
+#define SEARCHPGMLIST struct search_pgm_list
+
+
+SEARCHHEADER { /* header search */
+ SIZEDTEXT line; /* header line */
+ SIZEDTEXT text; /* text in header */
+ SEARCHHEADER *next; /* next in list */
+};
+
+
+SEARCHSET { /* message set */
+ unsigned long first; /* sequence number */
+ unsigned long last; /* last value, if a range */
+ SEARCHSET *next; /* next in list */
+};
+
+
+SEARCHOR {
+ SEARCHPGM *first; /* first program */
+ SEARCHPGM *second; /* second program */
+ SEARCHOR *next; /* next in list */
+};
+
+
+SEARCHPGMLIST {
+ SEARCHPGM *pgm; /* search program */
+ SEARCHPGMLIST *next; /* next in list */
+};
+
+SEARCHPGM { /* search program */
+ SEARCHSET *msgno; /* message numbers */
+ SEARCHSET *uid; /* unique identifiers */
+ SEARCHOR *or; /* or'ed in programs */
+ SEARCHPGMLIST *not; /* and'ed not program */
+ SEARCHHEADER *header; /* list of headers */
+ STRINGLIST *bcc; /* bcc recipients */
+ STRINGLIST *body; /* text in message body */
+ STRINGLIST *cc; /* cc recipients */
+ STRINGLIST *from; /* originator */
+ STRINGLIST *keyword; /* keywords */
+ STRINGLIST *unkeyword; /* unkeywords */
+ STRINGLIST *subject; /* text in subject */
+ STRINGLIST *text; /* text in headers and body */
+ STRINGLIST *to; /* to recipients */
+ unsigned long larger; /* larger than this size */
+ unsigned long smaller; /* smaller than this size */
+ unsigned long older; /* older than this interval */
+ unsigned long younger; /* younger than this interval */
+ unsigned short sentbefore; /* sent before this date */
+ unsigned short senton; /* sent on this date */
+ unsigned short sentsince; /* sent since this date */
+ unsigned short before; /* before this date */
+ unsigned short on; /* on this date */
+ unsigned short since; /* since this date */
+ unsigned int answered : 1; /* answered messages */
+ unsigned int unanswered : 1; /* unanswered messages */
+ unsigned int deleted : 1; /* deleted messages */
+ unsigned int undeleted : 1; /* undeleted messages */
+ unsigned int draft : 1; /* message draft */
+ unsigned int undraft : 1; /* message undraft */
+ unsigned int flagged : 1; /* flagged messages */
+ unsigned int unflagged : 1; /* unflagged messages */
+ unsigned int recent : 1; /* recent messages */
+ unsigned int old : 1; /* old messages */
+ unsigned int seen : 1; /* seen messages */
+ unsigned int unseen : 1; /* unseen messages */
+ /* These must be simulated in IMAP */
+ STRINGLIST *return_path; /* error return address */
+ STRINGLIST *sender; /* sender address list */
+ STRINGLIST *reply_to; /* reply address list */
+ STRINGLIST *in_reply_to; /* replied message ID */
+ STRINGLIST *message_id; /* message ID */
+ STRINGLIST *newsgroups; /* USENET newsgroups */
+ STRINGLIST *followup_to; /* USENET reply newsgroups */
+ STRINGLIST *references; /* USENET references */
+};
+
+
+/* Mailbox status */
+
+typedef struct mbx_status {
+ long flags; /* validity flags */
+ unsigned long messages; /* number of messages */
+ unsigned long recent; /* number of recent messages */
+ unsigned long unseen; /* number of unseen messages */
+ unsigned long uidnext; /* next UID to be assigned */
+ unsigned long uidvalidity; /* UID validity value */
+} MAILSTATUS;
+
+/* Sort program */
+
+typedef void (*postsort_t) (void *sc);
+
+#define SORTPGM struct sort_program
+
+SORTPGM {
+ unsigned int reverse : 1; /* sort function is to be reversed */
+ unsigned int abort : 1; /* abort sorting */
+ short function; /* sort function */
+ unsigned long nmsgs; /* number of messages being sorted */
+ struct {
+ unsigned long cached; /* number of messages cached so far */
+ unsigned long sorted; /* number of messages sorted so far */
+ unsigned long postsorted; /* number of postsorted messages so far */
+ } progress;
+ postsort_t postsort; /* post sorter */
+ SORTPGM *next; /* next function */
+};
+
+
+/* Sort cache */
+
+#define SORTCACHE struct sort_cache
+
+SORTCACHE {
+ unsigned int sorted : 1; /* message has been sorted */
+ unsigned int postsorted : 1; /* message has been postsorted */
+ unsigned int refwd : 1; /* subject is a re or fwd */
+ unsigned int dirty : 1; /* has data not written to backup */
+ SORTPGM *pgm; /* sort program */
+ unsigned long num; /* message number (sequence or UID) */
+ unsigned long date; /* sent date */
+ unsigned long arrival; /* arrival date */
+ unsigned long size; /* message size */
+ char *from; /* from string */
+ char *to; /* to string */
+ char *cc; /* cc string */
+ char *subject; /* extracted subject string */
+ char *message_id; /* message-id string */
+ char *unique; /* unique string, normally message-id */
+ STRINGLIST *references; /* references string */
+};
+
+/* ACL list */
+
+#define ACLLIST struct acl_list
+
+ACLLIST {
+ char *identifier; /* authentication identifier */
+ char *rights; /* access rights */
+ ACLLIST *next;
+};
+
+/* Quota resource list */
+
+#define QUOTALIST struct quota_list
+
+QUOTALIST {
+ char *name; /* resource name */
+ unsigned long usage; /* resource usage */
+ unsigned long limit; /* resource limit */
+ QUOTALIST *next; /* next resource */
+};
+
+/* Mail Access I/O stream */
+
+
+/* Structure for mail driver dispatch */
+
+#define DRIVER struct driver
+
+
+/* Mail I/O stream */
+
+typedef struct mail_stream {
+ DRIVER *dtb; /* dispatch table for this driver */
+ void *local; /* pointer to driver local data */
+ char *mailbox; /* mailbox name (canonicalized) */
+ char *original_mailbox; /* mailbox name (non-canonicalized) */
+ unsigned short use; /* stream use count */
+ unsigned short sequence; /* stream sequence */
+ unsigned int inbox : 1; /* stream open on an INBOX */
+ unsigned int lock : 1; /* stream lock flag */
+ unsigned int debug : 1; /* stream debug flag */
+ unsigned int silent : 1; /* don't pass events to main program */
+ unsigned int rdonly : 1; /* stream read-only flag */
+ unsigned int anonymous : 1; /* stream anonymous access flag */
+ unsigned int scache : 1; /* stream short cache flag */
+ unsigned int halfopen : 1; /* stream half-open flag */
+ unsigned int secure : 1; /* stream secure flag */
+ unsigned int tryssl : 1; /* stream tryssl flag */
+ unsigned int mulnewsrc : 1; /* stream use multiple newsrc files */
+ unsigned int perm_seen : 1; /* permanent Seen flag */
+ unsigned int perm_deleted : 1;/* permanent Deleted flag */
+ unsigned int perm_flagged : 1;/* permanent Flagged flag */
+ unsigned int perm_answered :1;/* permanent Answered flag */
+ unsigned int perm_draft : 1; /* permanent Draft flag */
+ unsigned int kwd_create : 1; /* can create new keywords */
+ unsigned int uid_nosticky : 1;/* UIDs are not preserved */
+ unsigned int unhealthy : 1; /* unhealthy protocol negotiations */
+ unsigned int nokod : 1; /* suppress kiss-of-death */
+ unsigned int sniff : 1; /* metadata only */
+ unsigned long perm_user_flags;/* mask of permanent user flags */
+ unsigned long gensym; /* generated tag */
+ unsigned long nmsgs; /* # of associated msgs */
+ unsigned long recent; /* # of recent msgs */
+ unsigned long uid_validity; /* UID validity sequence */
+ unsigned long uid_last; /* last assigned UID */
+ char *user_flags[NUSERFLAGS]; /* pointers to user flags in bit order */
+ unsigned long cachesize; /* size of message cache */
+ MESSAGECACHE **cache; /* message cache array */
+ SORTCACHE **sc; /* sort cache array */
+ unsigned long msgno; /* message number of `current' message */
+ ENVELOPE *env; /* scratch buffer for envelope */
+ BODY *body; /* scratch buffer for body */
+ SIZEDTEXT text; /* scratch buffer for text */
+ struct {
+ char *name; /* mailbox name to snarf from */
+ unsigned long time; /* last snarf time */
+ long options; /* snarf open options */
+ } snarf;
+ struct { /* internal use only */
+ struct { /* search temporaries */
+ STRINGLIST *string; /* string(s) to search */
+ long result; /* search result */
+ char *text; /* cache of fetched text */
+ } search;
+ STRING string; /* stringstruct return hack */
+ } private;
+ /* reserved for use by main program */
+ void *sparep; /* spare pointer */
+ unsigned int spare : 1; /* first spare bit */
+ unsigned int spare2 : 1; /* second spare bit */
+ unsigned int spare3 : 1; /* third spare bit */
+ unsigned int spare4 : 1; /* fourth spare bit */
+ unsigned int spare5 : 1; /* fifth spare bit */
+ unsigned int spare6 : 1; /* sixth spare bit */
+ unsigned int spare7 : 1; /* seventh spare bit */
+ unsigned int spare8 : 1; /* eighth spare bit */
+} MAILSTREAM;
+
+/* Mail I/O stream handle */
+
+typedef struct mail_stream_handle {
+ MAILSTREAM *stream; /* pointer to mail stream */
+ unsigned short sequence; /* sequence of what we expect stream to be */
+} MAILHANDLE;
+
+
+/* Message overview */
+
+typedef struct mail_overview {
+ char *subject; /* message subject string */
+ ADDRESS *from; /* originator address list */
+ char *date; /* message composition date string */
+ char *message_id; /* message ID */
+ char *references; /* USENET references */
+ struct { /* may be 0 or NUL if unknown/undefined */
+ unsigned long octets; /* message octets (probably LF-newline form) */
+ unsigned long lines; /* message lines */
+ char *xref; /* cross references */
+ } optional;
+} OVERVIEW;
+
+/* Network access I/O stream */
+
+
+/* Structure for network driver dispatch */
+
+#define NETDRIVER struct net_driver
+
+
+/* Network transport I/O stream */
+
+typedef struct net_stream {
+ void *stream; /* driver's I/O stream */
+ NETDRIVER *dtb; /* network driver */
+} NETSTREAM;
+
+
+/* Network transport driver dispatch */
+
+NETDRIVER {
+ void *(*open) (char *host,char *service,unsigned long port);
+ void *(*aopen) (NETMBX *mb,char *service,char *usrbuf);
+ char *(*getline) (void *stream);
+ long (*getbuffer) (void *stream,unsigned long size,char *buffer);
+ long (*soutr) (void *stream,char *string);
+ long (*sout) (void *stream,char *string,unsigned long size);
+ void (*close) (void *stream);
+ char *(*host) (void *stream);
+ char *(*remotehost) (void *stream);
+ unsigned long (*port) (void *stream);
+ char *(*localhost) (void *stream);
+};
+
+
+/* Mailgets data identifier */
+
+typedef struct getsdata {
+ MAILSTREAM *stream;
+ unsigned long msgno;
+ char *what;
+ STRINGLIST *stl;
+ unsigned long first;
+ unsigned long last;
+ long flags;
+} GETS_DATA;
+
+
+#define INIT_GETS(md,s,m,w,f,l) \
+ md.stream = s, md.msgno = m, md.what = w, md.first = f, md.last = l, \
+ md.stl = NIL, md.flags = NIL;
+
+/* Mail delivery I/O stream */
+
+typedef struct send_stream {
+ NETSTREAM *netstream; /* network I/O stream */
+ char *host; /* SMTP service host */
+ char *reply; /* last reply string */
+ long replycode; /* last reply code */
+ unsigned int debug : 1; /* stream debug flag */
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+ union { /* protocol specific */
+ struct { /* SMTP specific */
+ unsigned int ok : 1; /* supports ESMTP */
+ struct { /* service extensions */
+ unsigned int send : 1; /* supports SEND */
+ unsigned int soml : 1; /* supports SOML */
+ unsigned int saml : 1; /* supports SAML */
+ unsigned int expn : 1; /* supports EXPN */
+ unsigned int help : 1; /* supports HELP */
+ unsigned int turn : 1; /* supports TURN */
+ unsigned int etrn : 1; /* supports ETRN */
+ unsigned int starttls:1;/* supports STARTTLS */
+ unsigned int relay : 1; /* supports relaying */
+ unsigned int pipe : 1; /* supports pipelining */
+ unsigned int ensc : 1; /* supports enhanced status codes */
+ unsigned int bmime : 1; /* supports BINARYMIME */
+ unsigned int chunk : 1; /* supports CHUNKING */
+ } service;
+ struct { /* 8-bit MIME transport */
+ unsigned int ok : 1; /* supports 8-bit MIME */
+ unsigned int want : 1; /* want 8-bit MIME */
+ } eightbit;
+ struct { /* delivery status notification */
+ unsigned int ok : 1; /* supports DSN */
+ unsigned int want : 1; /* want DSN */
+ struct { /* notification options */
+ /* notify on failure */
+ unsigned int failure : 1;
+ /* notify on delay */
+ unsigned int delay : 1;
+ /* notify on success */
+ unsigned int success : 1;
+ } notify;
+ unsigned int full : 1; /* return full headers */
+ char *envid; /* envelope identifier as xtext */
+ } dsn;
+ struct { /* size declaration */
+ unsigned int ok : 1; /* supports SIZE */
+ unsigned long limit; /* maximum size supported */
+ } size;
+ struct { /* deliverby declaration */
+ unsigned int ok : 1; /* supports DELIVERBY */
+ unsigned long minby; /* minimum by-time */
+ } deliverby;
+ struct { /* authenticated turn */
+ unsigned int ok : 1; /* supports ATRN */
+ char *domains; /* domains */
+ } atrn;
+ /* supported SASL authenticators */
+ unsigned int auth : MAXAUTHENTICATORS;
+ } esmtp;
+ struct { /* NNTP specific */
+ unsigned int post : 1; /* supports POST */
+ struct { /* NNTP extensions */
+ unsigned int ok : 1; /* supports extensions */
+ /* supports LISTGROUP */
+ unsigned int listgroup : 1;
+ unsigned int over : 1; /* supports OVER */
+ unsigned int hdr : 1; /* supports HDR */
+ unsigned int pat : 1; /* supports PAT */
+ /* supports STARTTLS */
+ unsigned int starttls : 1;
+ /* server has MULTIDOMAIN */
+ unsigned int multidomain : 1;
+ /* supports AUTHINFO USER */
+ unsigned int authuser : 1;
+ /* supported authenticators */
+ unsigned int sasl : MAXAUTHENTICATORS;
+ } ext;
+ } nntp;
+ } protocol;
+} SENDSTREAM;
+
+/* Jacket into external interfaces */
+
+typedef long (*readfn_t) (void *stream,unsigned long size,char *buffer);
+typedef char *(*mailgets_t) (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+typedef char *(*readprogress_t) (GETS_DATA *md,unsigned long octets);
+typedef void *(*mailcache_t) (MAILSTREAM *stream,unsigned long msgno,long op);
+typedef long (*mailproxycopy_t) (MAILSTREAM *stream,char *sequence,
+ char *mailbox,long options);
+typedef long (*tcptimeout_t) (long overall,long last, char *host);
+typedef void *(*authchallenge_t) (void *stream,unsigned long *len);
+typedef long (*authrespond_t) (void *stream,char *s,unsigned long size);
+typedef long (*authcheck_t) (void);
+typedef long (*authclient_t) (authchallenge_t challenger,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *s,unsigned long *trial,char *user);
+typedef char *(*authresponse_t) (void *challenge,unsigned long clen,
+ unsigned long *rlen);
+typedef char *(*authserver_t) (authresponse_t responder,int argc,char *argv[]);
+typedef void (*smtpverbose_t) (char *buffer);
+typedef void (*imapenvelope_t) (MAILSTREAM *stream,unsigned long msgno,
+ ENVELOPE *env);
+typedef char *(*imapreferral_t) (MAILSTREAM *stream,char *url,long code);
+typedef void (*overview_t) (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+typedef unsigned long *(*sorter_t) (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,SORTPGM *pgm,long flags);
+typedef void (*parseline_t) (ENVELOPE *env,char *hdr,char *data,char *host);
+typedef ADDRESS *(*parsephrase_t) (char *phrase,char *end,char *host);
+typedef void *(*blocknotify_t) (int reason,void *data);
+typedef long (*kinit_t) (char *host,char *reason);
+typedef void (*sendcommand_t) (MAILSTREAM *stream,char *cmd,long flags);
+typedef char *(*newsrcquery_t) (MAILSTREAM *stream,char *mulname,char *name);
+typedef void (*getacl_t) (MAILSTREAM *stream,char *mailbox,ACLLIST *acl);
+typedef void (*listrights_t) (MAILSTREAM *stream,char *mailbox,char *id,
+ char *alwaysrights,STRINGLIST *possiblerights);
+typedef void (*myrights_t) (MAILSTREAM *stream,char *mailbox,char *rights);
+typedef void (*quota_t) (MAILSTREAM *stream,char *qroot,QUOTALIST *qlist);
+typedef void (*quotaroot_t) (MAILSTREAM *stream,char *mbx,STRINGLIST *qroot);
+typedef void (*sortresults_t) (MAILSTREAM *stream,unsigned long *list,
+ unsigned long size);
+typedef char *(*userprompt_t) (void);
+typedef long (*append_t) (MAILSTREAM *stream,void *data,char **flags,
+ char **date,STRING **message);
+typedef void (*copyuid_t) (MAILSTREAM *stream,char *mailbox,
+ unsigned long uidvalidity,SEARCHSET *sourceset,
+ SEARCHSET *destset);
+typedef void (*appenduid_t) (char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *set);
+typedef long (*dirfmttest_t) (char *name);
+typedef long (*scancontents_t) (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+
+typedef void (*freeeltsparep_t) (void **sparep);
+typedef void (*freeenvelopesparep_t) (void **sparep);
+typedef void (*freebodysparep_t) (void **sparep);
+typedef void (*freestreamsparep_t) (void **sparep);
+typedef void *(*sslstart_t) (void *stream,char *host,unsigned long flags);
+typedef long (*sslcertificatequery_t) (char *reason,char *host,char *cert);
+typedef void (*sslfailure_t) (char *host,char *reason,unsigned long flags);
+typedef void (*logouthook_t) (void *data);
+typedef char *(*sslclientcert_t) (void);
+typedef char *(*sslclientkey_t) (void);
+
+/* Globals */
+
+extern char *body_types[]; /* defined body type strings */
+extern char *body_encodings[]; /* defined body encoding strings */
+extern const char *days[]; /* day name strings */
+extern const char *months[]; /* month name strings */
+
+/* Threading */
+
+/* Thread node */
+
+#define THREADNODE struct thread_node
+
+THREADNODE {
+ unsigned long num; /* message number */
+ SORTCACHE *sc; /* (internal use) sortcache entry */
+ THREADNODE *branch; /* branch at this point in tree */
+ THREADNODE *next; /* next node */
+};
+
+typedef void (*threadresults_t) (MAILSTREAM *stream,THREADNODE *tree);
+
+
+/* Thread dispatch */
+
+#define THREADER struct threader_list
+
+THREADER {
+ char *name; /* name of threader */
+ THREADNODE *(*dispatch) (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ long flags,sorter_t sorter);
+ THREADER *next;
+};
+
+
+/* Container for references threading */
+
+typedef void ** container_t;
+
+/* Namespaces */
+
+#define NAMESPACE struct mail_namespace
+
+NAMESPACE {
+ char *name; /* name of this namespace */
+ int delimiter; /* hierarchy delimiter */
+ PARAMETER *param; /* namespace parameters */
+ NAMESPACE *next; /* next namespace */
+};
+
+
+/* Authentication */
+
+#define AUTHENTICATOR struct mail_authenticator
+
+AUTHENTICATOR {
+ long flags; /* authenticator flags */
+ char *name; /* name of this authenticator */
+ authcheck_t valid; /* authenticator valid on this system */
+ authclient_t client; /* client function that supports it */
+ authserver_t server; /* server function that supports it */
+ AUTHENTICATOR *next; /* next authenticator */
+};
+
+/* Mail driver dispatch */
+
+DRIVER {
+ char *name; /* driver name */
+ unsigned long flags; /* driver flags */
+ DRIVER *next; /* next driver */
+ /* mailbox is valid for us */
+ DRIVER *(*valid) (char *mailbox);
+ /* manipulate driver parameters */
+ void *(*parameters) (long function,void *value);
+ /* scan mailboxes */
+ void (*scan) (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+ /* list mailboxes */
+ void (*list) (MAILSTREAM *stream,char *ref,char *pat);
+ /* list subscribed mailboxes */
+ void (*lsub) (MAILSTREAM *stream,char *ref,char *pat);
+ /* subscribe to mailbox */
+ long (*subscribe) (MAILSTREAM *stream,char *mailbox);
+ /* unsubscribe from mailbox */
+ long (*unsubscribe) (MAILSTREAM *stream,char *mailbox);
+ /* create mailbox */
+ long (*create) (MAILSTREAM *stream,char *mailbox);
+ /* delete mailbox */
+ long (*mbxdel) (MAILSTREAM *stream,char *mailbox);
+ /* rename mailbox */
+ long (*mbxren) (MAILSTREAM *stream,char *old,char *newname);
+ /* status of mailbox */
+ long (*status) (MAILSTREAM *stream,char *mbx,long flags);
+
+ /* open mailbox */
+ MAILSTREAM *(*open) (MAILSTREAM *stream);
+ /* close mailbox */
+ void (*close) (MAILSTREAM *stream,long options);
+ /* fetch message "fast" attributes */
+ void (*fast) (MAILSTREAM *stream,char *sequence,long flags);
+ /* fetch message flags */
+ void (*msgflags) (MAILSTREAM *stream,char *sequence,long flags);
+ /* fetch message overview */
+ long (*overview) (MAILSTREAM *stream,overview_t ofn);
+ /* fetch message envelopes */
+ ENVELOPE *(*structure) (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+ /* return RFC-822 header */
+ char *(*header) (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+ /* return RFC-822 text */
+ long (*text) (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+ /* load cache */
+ long (*msgdata) (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags);
+ /* return UID for message */
+ unsigned long (*uid) (MAILSTREAM *stream,unsigned long msgno);
+ /* return message number from UID */
+ unsigned long (*msgno) (MAILSTREAM *stream,unsigned long uid);
+ /* modify flags */
+ void (*flag) (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+ /* per-message modify flags */
+ void (*flagmsg) (MAILSTREAM *stream,MESSAGECACHE *elt);
+ /* search for message based on criteria */
+ long (*search) (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+ /* sort messages */
+ unsigned long *(*sort) (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+ /* thread messages */
+ THREADNODE *(*thread) (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flag);
+ /* ping mailbox to see if still alive */
+ long (*ping) (MAILSTREAM *stream);
+ /* check for new messages */
+ void (*check) (MAILSTREAM *stream);
+ /* expunge deleted messages */
+ long (*expunge) (MAILSTREAM *stream,char *sequence,long options);
+ /* copy messages to another mailbox */
+ long (*copy) (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+ /* append string message to mailbox */
+ long (*append) (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+ /* garbage collect stream */
+ void (*gc) (MAILSTREAM *stream,long gcflags);
+};
+
+
+#include "linkage.h"
+
+/* Compatibility support names for old interfaces */
+
+#define GET_TRYALTFIRST GET_TRYSSLFIRST
+#define SET_TRYALTFIRST SET_TRYSSLFIRST
+#define GET_IMAPTRYALT GET_IMAPTRYSSL
+#define SET_IMAPTRYALT SET_IMAPTRYSSL
+#define OP_TRYALT OP_TRYSSL
+#define altflag sslflag
+
+#define mail_close(stream) \
+ mail_close_full (stream,NIL)
+#define mail_fetchfast(stream,sequence) \
+ mail_fetch_fast (stream,sequence,NIL)
+#define mail_fetchfast_full mail_fetch_fast
+#define mail_fetchflags(stream,sequence) \
+ mail_fetch_flags (stream,sequence,NIL)
+#define mail_fetchflags_full mail_fetch_flags
+#define mail_fetchenvelope(stream,msgno) \
+ mail_fetch_structure (stream,msgno,NIL,NIL)
+#define mail_fetchstructure(stream,msgno,body) \
+ mail_fetch_structure (stream,msgno,body,NIL)
+#define mail_fetchstructure_full mail_fetch_structure
+#define mail_fetchheader(stream,msgno) \
+ mail_fetch_header (stream,msgno,NIL,NIL,NIL,FT_PEEK)
+#define mail_fetchheader_full(stream,msgno,lines,len,flags) \
+ mail_fetch_header (stream,msgno,NIL,lines,len,FT_PEEK | (flags))
+#define mail_fetchtext(stream,msgno) \
+ mail_fetch_text (stream,msgno,NIL,NIL,NIL)
+#define mail_fetchtext_full(stream,msgno,length,flags) \
+ mail_fetch_text (stream,msgno,NIL,length,flags)
+#define mail_fetchbody(stream,msgno,section,length) \
+ mail_fetch_body (stream,msgno,section,length,NIL)
+#define mail_fetchbody_full mail_fetch_body
+#define mail_setflag(stream,sequence,flag) \
+ mail_flag (stream,sequence,flag,ST_SET)
+#define mail_setflag_full(stream,sequence,flag,flags) \
+ mail_flag (stream,sequence,flag,ST_SET | (flags))
+#define mail_clearflag(stream,sequence,flag) \
+ mail_flag (stream,sequence,flag,NIL)
+#define mail_clearflag_full mail_flag
+#define mail_search(stream,criteria) \
+ mail_search_full (stream,NIL,mail_criteria (criteria),SE_FREE);
+#define mail_expunge(stream) \
+ mail_expunge_full (stream,NIL,NIL)
+#define mail_copy(stream,sequence,mailbox) \
+ mail_copy_full (stream,sequence,mailbox,NIL)
+#define mail_move(stream,sequence,mailbox) \
+ mail_copy_full (stream,sequence,mailbox,CP_MOVE)
+#define mail_append(stream,mailbox,message) \
+ mail_append_full (stream,mailbox,NIL,NIL,message)
+
+/* Interfaces for SVR4 locking brain-damage workaround */
+
+/* Driver dispatching */
+
+#define SAFE_DELETE(dtb,stream,mailbox) (*dtb->mbxdel) (stream,mailbox)
+#define SAFE_RENAME(dtb,stream,old,newname) (*dtb->mbxren) (stream,old,newname)
+#define SAFE_STATUS(dtb,stream,mbx,flags) (*dtb->status) (stream,mbx,flags)
+#define SAFE_COPY(dtb,stream,sequence,mailbox,options) \
+ (*dtb->copy) (stream,sequence,mailbox,options)
+#define SAFE_APPEND(dtb,stream,mailbox,af,data) \
+ (*dtb->append) (stream,mailbox,af,data)
+#define SAFE_SCAN_CONTENTS(dtb,name,contents,csiz,fsiz) \
+ scan_contents (dtb,name,contents,csiz,fsiz)
+
+
+/* Driver callbacks */
+
+#define MM_EXISTS mm_exists
+#define MM_EXPUNGED mm_expunged
+#define MM_FLAGS mm_flags
+#define MM_NOTIFY mm_notify
+#define MM_STATUS mm_status
+#define MM_LOG mm_log
+#define MM_CRITICAL mm_critical
+#define MM_NOCRITICAL mm_nocritical
+#define MM_DISKERROR mm_diskerror
+#define MM_FATAL mm_fatal
+#define MM_APPEND(af) (*af)
+
+/* Function prototypes */
+
+void mm_searched (MAILSTREAM *stream,unsigned long number);
+void mm_exists (MAILSTREAM *stream,unsigned long number);
+void mm_expunged (MAILSTREAM *stream,unsigned long number);
+void mm_flags (MAILSTREAM *stream,unsigned long number);
+void mm_notify (MAILSTREAM *stream,char *string,long errflg);
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+void mm_log (char *string,long errflg);
+void mm_dlog (char *string);
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial);
+void mm_critical (MAILSTREAM *stream);
+void mm_nocritical (MAILSTREAM *stream);
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
+void mm_fatal (char *string);
+void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op);
+
+extern STRINGDRIVER mail_string;
+void mail_versioncheck (char *version);
+void mail_link (DRIVER *driver);
+void *mail_parameters (MAILSTREAM *stream,long function,void *value);
+DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose);
+DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox);
+long mail_valid_net_parse (char *name,NETMBX *mb);
+long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service);
+void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mail_list (MAILSTREAM *stream,char *ref,char *pat);
+void mail_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mail_subscribe (MAILSTREAM *stream,char *mailbox);
+long mail_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mail_create (MAILSTREAM *stream,char *mailbox);
+long mail_delete (MAILSTREAM *stream,char *mailbox);
+long mail_rename (MAILSTREAM *stream,char *old,char *newname);
+char *mail_utf7_valid (char *mailbox);
+long mail_status (MAILSTREAM *stream,char *mbx,long flags);
+long mail_status_default (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options);
+MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
+ long options);
+MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options);
+MAILHANDLE *mail_makehandle (MAILSTREAM *stream);
+void mail_free_handle (MAILHANDLE **handle);
+MAILSTREAM *mail_stream (MAILHANDLE *handle);
+
+void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags);
+void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn);
+void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence,
+ overview_t ofn);
+void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn);
+ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags);
+char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags);
+char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *lines,unsigned long *len,long flags);
+char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags);
+long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags);
+char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len);
+char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
+ unsigned long *len,long flags);
+long mail_read (void *stream,unsigned long size,char *buffer);
+unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno);
+unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid);
+void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno);
+void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+long mail_ping (MAILSTREAM *stream);
+void mail_check (MAILSTREAM *stream);
+long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options);
+long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message);
+long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
+ void *data);
+void mail_gc (MAILSTREAM *stream,long gcflags);
+void mail_gc_msg (MESSAGE *msg,long gcflags);
+void mail_gc_body (BODY *body);
+
+BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,
+ unsigned char *section);
+char *mail_date (char *string,MESSAGECACHE *elt);
+char *mail_cdate (char *string,MESSAGECACHE *elt);
+long mail_parse_date (MESSAGECACHE *elt,unsigned char *string);
+void mail_exists (MAILSTREAM *stream,unsigned long nmsgs);
+void mail_recent (MAILSTREAM *stream,unsigned long recent);
+void mail_expunged (MAILSTREAM *stream,unsigned long msgno);
+void mail_lock (MAILSTREAM *stream);
+void mail_unlock (MAILSTREAM *stream);
+void mail_debug (MAILSTREAM *stream);
+void mail_nodebug (MAILSTREAM *stream);
+void mail_dlog (char *string,long flag);
+long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags);
+unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
+ long flags);
+long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
+ SEARCHPGM *pgm);
+long mail_search_header_text (char *s,STRINGLIST *st);
+long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st);
+long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *st,long flags);
+long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
+ char *prefix,unsigned long section,long flags);
+long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st);
+long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st);
+long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st,
+ long flag);
+long mail_search_addr (ADDRESS *adr,STRINGLIST *st);
+char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+SEARCHPGM *mail_criteria (char *criteria);
+int mail_criteria_date (unsigned short *date,char **r);
+int mail_criteria_string (STRINGLIST **s,char **r);
+unsigned short mail_shortdate (unsigned int year,unsigned int month,
+ unsigned int day);
+SEARCHSET *mail_parse_set (char *s,char **ret);
+SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno);
+unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
+ long flags);
+unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm);
+unsigned int mail_strip_subject (char *t,char **ret);
+char *mail_strip_subject_wsp (char *s);
+char *mail_strip_subject_blob (char *s);
+int mail_sort_compare (const void *a1,const void *a2);
+unsigned long mail_longdate (MESSAGECACHE *elt);
+THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter);
+THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter);
+THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter);
+void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+char *mail_thread_parse_msgid (char *s,char **ss);
+STRINGLIST *mail_thread_parse_references (char *s,long flag);
+long mail_thread_check_child (container_t mother,container_t daughter);
+container_t mail_thread_prune_dummy (container_t msg,container_t ane);
+container_t mail_thread_prune_dummy_work (container_t msg,container_t ane);
+THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags);
+THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc);
+int mail_thread_compare_date (const void *a1,const void *a2);
+long mail_sequence (MAILSTREAM *stream,unsigned char *sequence);
+long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence);
+long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf);
+long mail_usable_network_stream (MAILSTREAM *stream,char *name);
+
+MESSAGECACHE *mail_new_cache_elt (unsigned long msgno);
+ENVELOPE *mail_newenvelope (void);
+ADDRESS *mail_newaddr (void);
+BODY *mail_newbody (void);
+BODY *mail_initbody (BODY *body);
+PARAMETER *mail_newbody_parameter (void);
+PART *mail_newbody_part (void);
+MESSAGE *mail_newmsg (void);
+STRINGLIST *mail_newstringlist (void);
+SEARCHPGM *mail_newsearchpgm (void);
+SEARCHHEADER *mail_newsearchheader (char *line,char *text);
+SEARCHSET *mail_newsearchset (void);
+SEARCHOR *mail_newsearchor (void);
+SEARCHPGMLIST *mail_newsearchpgmlist (void);
+SORTPGM *mail_newsortpgm (void);
+THREADNODE *mail_newthreadnode (SORTCACHE *sc);
+ACLLIST *mail_newacllist (void);
+QUOTALIST *mail_newquotalist (void);
+void mail_free_body (BODY **body);
+void mail_free_body_data (BODY *body);
+void mail_free_body_parameter (PARAMETER **parameter);
+void mail_free_body_part (PART **part);
+void mail_free_cache (MAILSTREAM *stream);
+void mail_free_elt (MESSAGECACHE **elt);
+void mail_free_envelope (ENVELOPE **env);
+void mail_free_address (ADDRESS **address);
+void mail_free_stringlist (STRINGLIST **string);
+void mail_free_searchpgm (SEARCHPGM **pgm);
+void mail_free_searchheader (SEARCHHEADER **hdr);
+void mail_free_searchset (SEARCHSET **set);
+void mail_free_searchor (SEARCHOR **orl);
+void mail_free_searchpgmlist (SEARCHPGMLIST **pgl);
+void mail_free_namespace (NAMESPACE **n);
+void mail_free_sortpgm (SORTPGM **pgm);
+void mail_free_threadnode (THREADNODE **thr);
+void mail_free_acllist (ACLLIST **al);
+void mail_free_quotalist (QUOTALIST **ql);
+void auth_link (AUTHENTICATOR *auth);
+char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[]);
+AUTHENTICATOR *mail_lookup_auth (unsigned long i);
+unsigned int mail_lookup_auth_name (char *mechanism,long flags);
+
+NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
+ NETDRIVER *ssld,char *ssls,unsigned long sslp);
+NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
+ unsigned long port,unsigned long portoverride,
+ unsigned long flags);
+NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *usrbuf);
+char *net_getline (NETSTREAM *stream);
+ /* stream must be void* for use as readfn_t */
+long net_getbuffer (void *stream,unsigned long size,char *buffer);
+long net_soutr (NETSTREAM *stream,char *string);
+long net_sout (NETSTREAM *stream,char *string,unsigned long size);
+void net_close (NETSTREAM *stream);
+char *net_host (NETSTREAM *stream);
+char *net_remotehost (NETSTREAM *stream);
+unsigned long net_port (NETSTREAM *stream);
+char *net_localhost (NETSTREAM *stream);
+
+long sm_subscribe (char *mailbox);
+long sm_unsubscribe (char *mailbox);
+char *sm_read (void **sdb);
+
+void ssl_onceonlyinit (void);
+char *ssl_start_tls (char *s);
+void ssl_server_init (char *server);
+
+
+/* Server I/O functions */
+
+int PBIN (void);
+char *PSIN (char *s,int n);
+long PSINR (char *s,unsigned long n);
+int PBOUT (int c);
+long INWAIT (long seconds);
+int PSOUT (char *s);
+int PSOUTR (SIZEDTEXT *s);
+int PFLUSH (void);
diff --git a/imap/src/c-client/misc.c b/imap/src/c-client/misc.c
new file mode 100644
index 00000000..4b789af8
--- /dev/null
+++ b/imap/src/c-client/misc.c
@@ -0,0 +1,475 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Miscellaneous utility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 July 1988
+ * Last Edited: 6 December 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include "c-client.h"
+
+/* Convert ASCII string to all uppercase
+ * Accepts: string pointer
+ * Returns: string pointer
+ *
+ * Don't use islower/toupper since this function must be ASCII only.
+ */
+
+unsigned char *ucase (unsigned char *s)
+{
+ unsigned char *t;
+ /* if lowercase covert to upper */
+ for (t = s; *t; t++) if ((*t >= 'a') && (*t <= 'z')) *t -= ('a' - 'A');
+ return s; /* return string */
+}
+
+
+/* Convert string to all lowercase
+ * Accepts: string pointer
+ * Returns: string pointer
+ *
+ * Don't use isupper/tolower since this function must be ASCII only.
+ */
+
+unsigned char *lcase (unsigned char *s)
+{
+ unsigned char *t;
+ /* if uppercase covert to lower */
+ for (t = s; *t; t++) if ((*t >= 'A') && (*t <= 'Z')) *t += ('a' - 'A');
+ return s; /* return string */
+}
+
+/* Copy string to free storage
+ * Accepts: source string
+ * Returns: free storage copy of string
+ */
+
+char *cpystr (const char *string)
+{
+ return string ? strcpy ((char *) fs_get (1 + strlen (string)),string) : NIL;
+}
+
+
+/* Copy text/size to free storage as sized text
+ * Accepts: destination sized text
+ * pointer to source text
+ * size of source text
+ * Returns: text as a char *
+ */
+
+char *cpytxt (SIZEDTEXT *dst,char *text,unsigned long size)
+{
+ /* flush old space */
+ if (dst->data) fs_give ((void **) &dst->data);
+ /* copy data in sized text */
+ memcpy (dst->data = (unsigned char *)
+ fs_get ((size_t) (dst->size = size) + 1),text,(size_t) size);
+ dst->data[size] = '\0'; /* tie off text */
+ return (char *) dst->data; /* convenience return */
+}
+
+/* Copy sized text to free storage as sized text
+ * Accepts: destination sized text
+ * source sized text
+ * Returns: text as a char *
+ */
+
+char *textcpy (SIZEDTEXT *dst,SIZEDTEXT *src)
+{
+ /* flush old space */
+ if (dst->data) fs_give ((void **) &dst->data);
+ /* copy data in sized text */
+ memcpy (dst->data = (unsigned char *)
+ fs_get ((size_t) (dst->size = src->size) + 1),
+ src->data,(size_t) src->size);
+ dst->data[dst->size] = '\0'; /* tie off text */
+ return (char *) dst->data; /* convenience return */
+}
+
+
+/* Copy stringstruct to free storage as sized text
+ * Accepts: destination sized text
+ * source stringstruct
+ * Returns: text as a char *
+ */
+
+char *textcpystring (SIZEDTEXT *text,STRING *bs)
+{
+ unsigned long i = 0;
+ /* clear old space */
+ if (text->data) fs_give ((void **) &text->data);
+ /* make free storage space in sized text */
+ text->data = (unsigned char *) fs_get ((size_t) (text->size = SIZE (bs)) +1);
+ while (i < text->size) text->data[i++] = SNX (bs);
+ text->data[i] = '\0'; /* tie off text */
+ return (char *) text->data; /* convenience return */
+}
+
+
+/* Copy stringstruct from offset to free storage as sized text
+ * Accepts: destination sized text
+ * source stringstruct
+ * offset into stringstruct
+ * size of source text
+ * Returns: text as a char *
+ */
+
+char *textcpyoffstring (SIZEDTEXT *text,STRING *bs,unsigned long offset,
+ unsigned long size)
+{
+ unsigned long i = 0;
+ /* clear old space */
+ if (text->data) fs_give ((void **) &text->data);
+ SETPOS (bs,offset); /* offset the string */
+ /* make free storage space in sized text */
+ text->data = (unsigned char *) fs_get ((size_t) (text->size = size) + 1);
+ while (i < size) text->data[i++] = SNX (bs);
+ text->data[i] = '\0'; /* tie off text */
+ return (char *) text->data; /* convenience return */
+}
+
+/* Returns index of rightmost bit in word
+ * Accepts: pointer to a 32 bit value
+ * Returns: -1 if word is 0, else index of rightmost bit
+ *
+ * Bit is cleared in the word
+ */
+
+unsigned long find_rightmost_bit (unsigned long *valptr)
+{
+ unsigned long value = *valptr;
+ unsigned long bit = 0;
+ if (!(value & 0xffffffff)) return 0xffffffff;
+ /* binary search for rightmost bit */
+ if (!(value & 0xffff)) value >>= 16, bit += 16;
+ if (!(value & 0xff)) value >>= 8, bit += 8;
+ if (!(value & 0xf)) value >>= 4, bit += 4;
+ if (!(value & 0x3)) value >>= 2, bit += 2;
+ if (!(value & 0x1)) value >>= 1, bit += 1;
+ *valptr ^= (1 << bit); /* clear specified bit */
+ return bit;
+}
+
+
+/* Return minimum of two integers
+ * Accepts: integer 1
+ * integer 2
+ * Returns: minimum
+ */
+
+long min (long i,long j)
+{
+ return ((i < j) ? i : j);
+}
+
+
+/* Return maximum of two integers
+ * Accepts: integer 1
+ * integer 2
+ * Returns: maximum
+ */
+
+long max (long i,long j)
+{
+ return ((i > j) ? i : j);
+}
+
+/* Search, case-insensitive for ASCII characters
+ * Accepts: base string
+ * length of base string
+ * pattern string
+ * length of pattern string
+ * Returns: T if pattern exists inside base, else NIL
+ */
+
+long search (unsigned char *base,long basec,unsigned char *pat,long patc)
+{
+ long i,j,k;
+ int c;
+ unsigned char mask[256];
+ static unsigned char alphatab[256] = {
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+ 223,223,223,223,223,223,223,223,223,223,223,255,255,255,255,255,
+ 255,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+ 223,223,223,223,223,223,223,223,223,223,223,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+ };
+ /* validate arguments */
+ if (base && (basec > 0) && pat && (basec >= patc)) {
+ if (patc <= 0) return T; /* empty pattern always succeeds */
+ memset (mask,0,256); /* initialize search validity mask */
+ for (i = 0; i < patc; i++) if (!mask[c = pat[i]]) {
+ /* mark single character if non-alphabetic */
+ if (alphatab[c] & 0x20) mask[c] = T;
+ /* else mark both cases */
+ else mask[c & 0xdf] = mask[c | 0x20] = T;
+ }
+ /* Boyer-Moore type search */
+ for (i = --patc; i < basec; i += (mask[c] ? 1 : (j + 1)))
+ for (j = patc,c = base[k = i]; !((c ^ pat[j]) & alphatab[c]);
+ j--,c = base[--k])
+ if (!j) return T; /* found a match! */
+ }
+ return NIL; /* pattern not found */
+}
+
+/* Boyer-Moore string search
+ * Accepts: base string
+ * length of base string
+ * pattern string
+ * length of pattern string
+ * Returns: T if pattern exists inside base, else NIL
+ */
+
+long ssearch (unsigned char *base,long basec,unsigned char *pat,long patc)
+{
+ long i,j,k;
+ int c;
+ unsigned char mask[256];
+ /* validate arguments */
+ if (base && (basec > 0) && pat && (basec >= patc)) {
+ if (patc <= 0) return T; /* empty pattern always succeeds */
+ memset (mask,0,256); /* initialize search validity mask */
+ for (i = 0; i < patc; i++) mask[pat[i]] = T;
+ /* Boyer-Moore type search */
+ for (i = --patc, c = pat[i]; i < basec; i += (mask[c] ? 1 : (j + 1)))
+ for (j = patc,c = base[k = i]; (c == pat[j]); j--,c = base[--k])
+ if (!j) return T; /* found a match! */
+ }
+ return NIL; /* pattern not found */
+}
+
+/* Create a hash table
+ * Accepts: size of new table (note: should be a prime)
+ * Returns: hash table
+ */
+
+HASHTAB *hash_create (size_t size)
+{
+ size_t i = sizeof (size_t) + size * sizeof (HASHENT *);
+ HASHTAB *ret = (HASHTAB *) memset (fs_get (i),0,i);
+ ret->size = size;
+ return ret;
+}
+
+
+/* Destroy hash table
+ * Accepts: hash table
+ */
+
+void hash_destroy (HASHTAB **hashtab)
+{
+ if (*hashtab) {
+ hash_reset (*hashtab); /* reset hash table */
+ fs_give ((void **) hashtab);
+ }
+}
+
+
+/* Reset hash table
+ * Accepts: hash table
+ */
+
+void hash_reset (HASHTAB *hashtab)
+{
+ size_t i;
+ HASHENT *ent,*nxt;
+ /* free each hash entry */
+ for (i = 0; i < hashtab->size; i++) if (ent = hashtab->table[i])
+ for (hashtab->table[i] = NIL; ent; ent = nxt) {
+ nxt = ent->next; /* get successor */
+ fs_give ((void **) &ent); /* flush this entry */
+ }
+}
+
+/* Calculate index into hash table
+ * Accepts: hash table
+ * entry name
+ * Returns: index
+ */
+
+unsigned long hash_index (HASHTAB *hashtab,char *key)
+{
+ unsigned long i,ret;
+ /* polynomial of letters of the word */
+ for (ret = 0; i = (unsigned int) *key++; ret += i) ret *= HASHMULT;
+ return ret % (unsigned long) hashtab->size;
+}
+
+
+/* Look up name in hash table
+ * Accepts: hash table
+ * key
+ * Returns: associated data
+ */
+
+void **hash_lookup (HASHTAB *hashtab,char *key)
+{
+ HASHENT *ret;
+ for (ret = hashtab->table[hash_index (hashtab,key)]; ret; ret = ret->next)
+ if (!strcmp (key,ret->name)) return ret->data;
+ return NIL;
+}
+
+/* Add entry to hash table
+ * Accepts: hash table
+ * key
+ * associated data
+ * number of extra data slots
+ * Returns: hash entry
+ * Caller is responsible for ensuring that entry isn't already in table
+ */
+
+HASHENT *hash_add (HASHTAB *hashtab,char *key,void *data,long extra)
+{
+ unsigned long i = hash_index (hashtab,key);
+ size_t j = sizeof (HASHENT) + (extra * sizeof (void *));
+ HASHENT *ret = (HASHENT *) memset (fs_get (j),0,j);
+ ret->next = hashtab->table[i];/* insert as new head in this index */
+ ret->name = key; /* set up hash key */
+ ret->data[0] = data; /* and first data */
+ return hashtab->table[i] = ret;
+}
+
+
+/* Look up name in hash table
+ * Accepts: hash table
+ * key
+ * associated data
+ * number of extra data slots
+ * Returns: associated data
+ */
+
+void **hash_lookup_and_add (HASHTAB *hashtab,char *key,void *data,long extra)
+{
+ HASHENT *ret;
+ unsigned long i = hash_index (hashtab,key);
+ size_t j = sizeof (HASHENT) + (extra * sizeof (void *));
+ for (ret = hashtab->table[i]; ret; ret = ret->next)
+ if (!strcmp (key,ret->name)) return ret->data;
+ ret = (HASHENT *) memset (fs_get (j),0,j);
+ ret->next = hashtab->table[i];/* insert as new head in this index */
+ ret->name = key; /* set up hash key */
+ ret->data[0] = data; /* and first data */
+ return (hashtab->table[i] = ret)->data;
+}
+
+/* Convert two hex characters into byte
+ * Accepts: char for high nybble
+ * char for low nybble
+ * Returns: byte
+ *
+ * Arguments must be isxdigit validated
+ */
+
+unsigned char hex2byte (unsigned char c1,unsigned char c2)
+{
+ /* merge the two nybbles */
+ return ((c1 -= (isdigit (c1) ? '0' : ((c1 <= 'Z') ? 'A' : 'a') - 10)) << 4) +
+ (c2 - (isdigit (c2) ? '0' : ((c2 <= 'Z') ? 'A' : 'a') - 10));
+}
+
+
+/* Compare two unsigned longs
+ * Accepts: first value
+ * second value
+ * Returns: -1 if l1 < l2, 0 if l1 == l2, 1 if l1 > l2
+ */
+
+int compare_ulong (unsigned long l1,unsigned long l2)
+{
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+
+/* Compare two unsigned chars, case-independent
+ * Accepts: first value
+ * second value
+ * Returns: -1 if c1 < c2, 0 if c1 == c2, 1 if c1 > c2
+ *
+ * Don't use isupper/tolower since this function must be ASCII only.
+ */
+
+int compare_uchar (unsigned char c1,unsigned char c2)
+{
+ return compare_ulong (((c1 >= 'A') && (c1 <= 'Z')) ? c1 + ('a' - 'A') : c1,
+ ((c2 >= 'A') && (c2 <= 'Z')) ? c2 + ('a' - 'A') : c2);
+}
+
+/* Compare two case-independent ASCII strings
+ * Accepts: first string
+ * second string
+ * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
+ */
+
+int compare_cstring (unsigned char *s1,unsigned char *s2)
+{
+ int i;
+ if (!s1) return s2 ? -1 : 0; /* empty string cases */
+ else if (!s2) return 1;
+ for (; *s1 && *s2; s1++,s2++) if (i = (compare_uchar (*s1,*s2))) return i;
+ if (*s1) return 1; /* first string is longer */
+ return *s2 ? -1 : 0; /* second string longer : strings identical */
+}
+
+
+/* Compare case-independent string with sized text
+ * Accepts: first string
+ * sized text
+ * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
+ */
+
+int compare_csizedtext (unsigned char *s1,SIZEDTEXT *s2)
+{
+ int i;
+ unsigned char *s;
+ unsigned long j;
+ if (!s1) return s2 ? -1 : 0; /* null string cases */
+ else if (!s2) return 1;
+ for (s = (char *) s2->data,j = s2->size; *s1 && j; ++s1,++s,--j)
+ if (i = (compare_uchar (*s1,*s))) return i;
+ if (*s1) return 1; /* first string is longer */
+ return j ? -1 : 0; /* second string longer : strings identical */
+}
diff --git a/imap/src/c-client/misc.h b/imap/src/c-client/misc.h
new file mode 100644
index 00000000..1ac79485
--- /dev/null
+++ b/imap/src/c-client/misc.h
@@ -0,0 +1,110 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Miscellaneous utility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 July 1988
+ * Last Edited: 30 August 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+/* Hash table operations */
+
+#define HASHMULT 29 /* hash polynomial multiplier */
+
+#define HASHENT struct hash_entry
+
+HASHENT {
+ HASHENT *next; /* next entry with same hash code */
+ char *name; /* name of this hash entry */
+ void *data[1]; /* data of this hash entry */
+};
+
+
+#define HASHTAB struct hash_table
+
+HASHTAB {
+ size_t size; /* size of this table */
+ HASHENT *table[1]; /* table */
+};
+
+
+/* KLUDGE ALERT!!!
+ *
+ * Yes, write() is overridden here instead of in osdep. This
+ * is because misc.h is one of the last files that most things #include, so
+ * this should avoid problems with some system #include file.
+ */
+
+#define write safe_write
+
+
+/* Some C compilers have these as macros */
+
+#undef min
+#undef max
+
+
+/* And some C libraries have these as int functions */
+
+#define min Min
+#define max Max
+
+
+/* Compatibility definitions */
+
+#define pmatch(s,pat) \
+ pmatch_full (s,pat,NIL)
+
+/* Function prototypes */
+
+unsigned char *ucase (unsigned char *string);
+unsigned char *lcase (unsigned char *string);
+char *cpystr (const char *string);
+char *cpytxt (SIZEDTEXT *dst,char *text,unsigned long size);
+char *textcpy (SIZEDTEXT *dst,SIZEDTEXT *src);
+char *textcpystring (SIZEDTEXT *text,STRING *bs);
+char *textcpyoffstring (SIZEDTEXT *text,STRING *bs,unsigned long offset,
+ unsigned long size);
+unsigned long find_rightmost_bit (unsigned long *valptr);
+long min (long i,long j);
+long max (long i,long j);
+long search (unsigned char *base,long basec,unsigned char *pat,long patc);
+long ssearch (unsigned char *base,long basec,unsigned char *pat,long patc);
+HASHTAB *hash_create (size_t size);
+void hash_destroy (HASHTAB **hashtab);
+void hash_reset (HASHTAB *hashtab);
+unsigned long hash_index (HASHTAB *hashtab,char *key);
+void **hash_lookup (HASHTAB *hashtab,char *key);
+HASHENT *hash_add (HASHTAB *hashtab,char *key,void *data,long extra);
+void **hash_lookup_and_add (HASHTAB *hashtab,char *key,void *data,long extra);
+unsigned char hex2byte (unsigned char c1,unsigned char c2);
+int compare_ulong (unsigned long l1,unsigned long l2);
+int compare_uchar (unsigned char c1,unsigned char c2);
+int compare_cstring (unsigned char *s1,unsigned char *s2);
+int compare_csizedtext (unsigned char *s1,SIZEDTEXT *s2);
diff --git a/imap/src/c-client/netmsg.c b/imap/src/c-client/netmsg.c
new file mode 100644
index 00000000..187e4ebb
--- /dev/null
+++ b/imap/src/c-client/netmsg.c
@@ -0,0 +1,104 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network message (SMTP/NNTP/POP2/POP3) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 June 1995
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "c-client.h"
+#include "netmsg.h"
+#include "flstring.h"
+
+/* Network message read
+ * Accepts: file
+ * number of bytes to read
+ * buffer address
+ * Returns: T if success, NIL otherwise
+ */
+
+long netmsg_read (void *stream,unsigned long count,char *buffer)
+{
+ return (fread (buffer,(size_t) 1,(size_t) count,(FILE *) stream) == count) ?
+ T : NIL;
+}
+
+/* Slurp dot-terminated text from NET
+ * Accepts: NET stream
+ * place to return size
+ * place to return header size
+ * Returns: file descriptor
+ */
+
+FILE *netmsg_slurp (NETSTREAM *stream,unsigned long *size,unsigned long *hsiz)
+{
+ unsigned long i;
+ char *s,*t,tmp[MAILTMPLEN];
+ FILE *f = tmpfile ();
+ if (!f) {
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (f = fopen (tmp,"wb+")) unlink (tmp);
+ else {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+ *size = 0; /* initially emtpy */
+ if (hsiz) *hsiz = 0;
+ while (s = net_getline (stream)) {
+ if (*s == '.') { /* possible end of text? */
+ if (s[1]) t = s + 1; /* pointer to true start of line */
+ else {
+ fs_give ((void **) &s); /* free the line */
+ break; /* end of data */
+ }
+ }
+ else t = s; /* want the entire line */
+ if (f) { /* copy it to the file */
+ i = strlen (t); /* size of line */
+ if ((fwrite (t,(size_t) 1,(size_t) i,f) == i) &&
+ (fwrite ("\015\012",(size_t) 1,(size_t) 2,f) == 2)) {
+ *size += i + 2; /* tally up size of data */
+ /* note header position */
+ if (!i && hsiz && !*hsiz) *hsiz = *size;
+ }
+ else {
+ sprintf (tmp,"Error writing scratch file at byte %lu",*size);
+ MM_LOG (tmp,ERROR);
+ fclose (f); /* forget it */
+ f = NIL; /* failure now */
+ }
+ }
+ fs_give ((void **) &s); /* free the line */
+ }
+ /* if making a file, rewind to start of file */
+ if (f) fseek (f,(unsigned long) 0,SEEK_SET);
+ /* header consumes entire message */
+ if (hsiz && !*hsiz) *hsiz = *size;
+ return f; /* return the file descriptor */
+}
diff --git a/imap/src/c-client/netmsg.h b/imap/src/c-client/netmsg.h
new file mode 100644
index 00000000..bacff576
--- /dev/null
+++ b/imap/src/c-client/netmsg.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network message (SMTP/NNTP/POP2/POP3) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+ /* stream must be void* for use as readfn_t */
+long netmsg_read (void *stream,unsigned long count,char *buffer);
+FILE *netmsg_slurp (NETSTREAM *stream,unsigned long *size,unsigned long *hsiz);
diff --git a/imap/src/c-client/newsrc.c b/imap/src/c-client/newsrc.c
new file mode 100644
index 00000000..41f1fd24
--- /dev/null
+++ b/imap/src/c-client/newsrc.c
@@ -0,0 +1,510 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newsrc manipulation routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 September 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+#include "newsrc.h"
+
+#ifndef OLDFILESUFFIX
+#define OLDFILESUFFIX ".old"
+#endif
+
+/* Error message
+ * Accepts: message format
+ * additional message string
+ * message level
+ * Returns: NIL, always
+ */
+
+long newsrc_error (char *fmt,char *text,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,fmt,text);
+ MM_LOG (tmp,errflg);
+ return NIL;
+}
+
+
+/* Write error message
+ * Accepts: newsrc name
+ * file designator
+ * file designator
+ * Returns: NIL, always
+ */
+
+long newsrc_write_error (char *name,FILE *f1,FILE *f2)
+{
+ if (f1) fclose (f1); /* close file designators */
+ if (f2) fclose (f2);
+ return newsrc_error ("Error writing to %.80s",name,ERROR);
+}
+
+
+/* Create newsrc file in local form
+ * Accepts: MAIL stream
+ * notification flag
+ * Returns: file designator of newsrc
+ */
+
+FILE *newsrc_create (MAILSTREAM *stream,int notify)
+{
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ FILE *f = fopen (newsrc,"wb");
+ if (!f) newsrc_error ("Unable to create news state %.80s",newsrc,ERROR);
+ else if (notify) newsrc_error ("Creating news state %.80s",newsrc,WARN);
+ return f;
+}
+
+/* Write new state in newsrc
+ * Accepts: file designator of newsrc
+ * group
+ * new subscription status character
+ * newline convention
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_newstate (FILE *f,char *group,char state,char *nl)
+{
+ long ret = (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
+ ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF)) ? LONGT : NIL;
+ if (fclose (f) == EOF) ret = NIL;
+ return ret;
+}
+
+
+/* Write messages in newsrc
+ * Accepts: file designator of newsrc
+ * MAIL stream
+ * message number/newsgroup message map
+ * newline convention
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl)
+{
+ unsigned long i,j,k;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ int c = ' ';
+ if (stream->nmsgs) { /* have any messages? */
+ for (i = 1,j = k = (mail_elt (stream,i)->private.uid > 1) ? 1 : 0;
+ i <= stream->nmsgs; ++i) {
+ /* deleted message? */
+ if ((elt = mail_elt (stream,i))->deleted) {
+ k = elt->private.uid; /* this is the top of the current range */
+ if (!j) j = k; /* if no range in progress, start one */
+ }
+ else if (j) { /* unread message, ending a range */
+ /* calculate end of range */
+ if (k = elt->private.uid - 1) {
+ /* dump range */
+ sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
+ if (fputs (tmp,f) == EOF) return NIL;
+ c = ','; /* need a comma after the first time */
+ }
+ j = 0; /* no more range in progress */
+ }
+ }
+ if (j) { /* dump trailing range */
+ sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
+ if (fputs (tmp,f) == EOF) return NIL;
+ }
+ }
+ /* write trailing newline, return */
+ return (fputs (nl,f) == EOF) ? NIL : LONGT;
+}
+
+/* List subscribed newsgroups
+ * Accepts: MAIL stream
+ * prefix to append name
+ * pattern to search
+ */
+
+void newsrc_lsub (MAILSTREAM *stream,char *pattern)
+{
+ char *s,*t,*lcl,name[MAILTMPLEN];
+ int c = ' ';
+ int showuppers = pattern[strlen (pattern) - 1] == '%';
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) { /* got file? */
+ /* remote name? */
+ if (*(lcl = strcpy (name,pattern)) == '{') lcl = strchr (lcl,'}') + 1;
+ if (*lcl == '#') lcl += 6; /* namespace format name? */
+ while (c != EOF) { /* yes, read newsrc */
+ for (s = lcl; (s < (name + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
+ *s++ = c);
+ if (c == ':') { /* found a subscribed newsgroup? */
+ *s = '\0'; /* yes, tie off name */
+ /* report if match */
+ if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
+ else while (showuppers && (t = strrchr (lcl,'.'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (name,pattern,'.'))
+ mm_lsub (stream,'.',name,LATT_NOSELECT);
+ }
+ }
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ }
+ fclose (f);
+ }
+}
+
+/* Update subscription status of newsrc
+ * Accepts: MAIL stream
+ * group
+ * new subscription status character
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_update (MAILSTREAM *stream,char *group,char state)
+{
+ char tmp[MAILTMPLEN];
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ long ret = NIL;
+ FILE *f = fopen (newsrc,"r+b");
+ if (f) { /* update existing file */
+ int c = 0;
+ char *s,nl[3];
+ long pos = 0;
+ nl[0] = nl[1] = nl[2]='\0'; /* no newline known yet */
+ do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
+ *s++ = c) pos = ftell (f);
+ *s = '\0'; /* tie off name */
+ /* found the newsgroup? */
+ if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
+ if (c == state) { /* already at that state? */
+ if (c == ':') newsrc_error("Already subscribed to %.80s",group,WARN);
+ ret = LONGT; /* noop the update */
+ }
+ /* write the character */
+ else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
+ if (fclose (f) == EOF) ret = NIL;
+ f = NIL; /* done with file */
+ break;
+ }
+ /* gobble remainder of this line */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ /* need to know about newlines and found it? */
+ if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+ /* sniff and see if an LF */
+ if ((c = getc (f)) == '\012') nl[1] = c;
+ else ungetc (c,f); /* nope, push it back */
+ }
+ } while (c != EOF);
+
+ if (f) { /* still haven't written it yet? */
+ if (nl[0]) { /* know its newline convention? */
+ fseek (f,0L,2); /* yes, seek to end of file */
+ ret = newsrc_newstate (f,group,state,nl);
+ }
+ else { /* can't find a newline convention */
+ fclose (f); /* punt the file */
+ /* can't win if read something */
+ if (pos) newsrc_error ("Unknown newline convention in %.80s",
+ newsrc,ERROR);
+ /* file must have been empty, rewrite it */
+ else ret = newsrc_newstate(newsrc_create(stream,NIL),group,state,"\n");
+ }
+ }
+ }
+ /* create new file */
+ else ret = newsrc_newstate (newsrc_create (stream,T),group,state,"\n");
+ return ret; /* return with update status */
+}
+
+/* Update newsgroup status in stream
+ * Accepts: newsgroup name
+ * MAIL stream
+ * Returns: number of recent messages
+ */
+
+long newsrc_read (char *group,MAILSTREAM *stream)
+{
+ int c = 0;
+ char *s,tmp[MAILTMPLEN];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ unsigned long m = 1,recent = 0,unseen = 0;
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ if ((c==':') || (c=='!')) { /* found newsgroup? */
+ if (strcmp (tmp,group)) /* group name match? */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ else { /* yes, skip leading whitespace */
+ while ((c = getc (f)) == ' ');
+ /* only if unprocessed messages */
+ if (stream->nmsgs) while (f && (m <= stream->nmsgs)) {
+ /* collect a number */
+ if (isdigit (c)) { /* better have a number */
+ for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
+ if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
+ j = j*10 +(c-'0');/* collect second value if range */
+ if (!unseen && (mail_elt (stream,m)->private.uid < i)) unseen = m;
+ /* skip messages before first value */
+ while ((m <= stream->nmsgs) &&
+ ((elt = mail_elt (stream,m))->private.uid < i) && m++)
+ elt->valid = T;
+ /* do all messages in range */
+ while ((m <= stream->nmsgs) && (elt = mail_elt (stream,m)) &&
+ (j ? ((elt->private.uid >= i) && (elt->private.uid <= j)) :
+ (elt->private.uid == i)) && m++)
+ elt->valid = elt->deleted = T;
+ }
+
+ switch (c) { /* what is the delimiter? */
+ case ',': /* more to come */
+ c = getc (f); /* get first character of number */
+ break;
+ default: /* bogus character */
+ sprintf (tmp,"Bogus character 0x%x in news state",(unsigned int)c);
+ MM_LOG (tmp,ERROR);
+ case EOF: case '\015': case '\012':
+ fclose (f); /* all done - close the file */
+ f = NIL;
+ break;
+ }
+ }
+ else { /* empty newsgroup */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ fclose (f); /* all done - close the file */
+ f = NIL;
+ }
+ }
+ }
+ } while (f && (c != EOF)); /* until file closed or EOF */
+ if (f) { /* still have file open? */
+ sprintf (tmp,"No state for newsgroup %.80s found, reading as new",group);
+ MM_LOG (tmp,WARN);
+ fclose (f); /* close the file */
+ }
+ if (m <= stream->nmsgs) { /* any messages beyond newsrc range? */
+ if (!unseen) unseen = m; /* then this must be the first unseen one */
+ do {
+ elt = mail_elt (stream,m++);
+ elt->valid = elt->recent = T;
+ ++recent; /* count another recent message */
+ }
+ while (m <= stream->nmsgs);
+ }
+ if (unseen) { /* report first unseen message */
+ sprintf (tmp,"[UNSEEN] %lu is first unseen message in %.80s",unseen,group);
+ MM_NOTIFY (stream,tmp,(long) NIL);
+ }
+ return recent;
+}
+
+/* Update newsgroup entry in newsrc
+ * Accepts: newsgroup name
+ * MAIL stream
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_write (char *group,MAILSTREAM *stream)
+{
+ long ret = NIL;
+ int c = 0,d = EOF;
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
+ FILE *f,*bf;
+ nl[0] = nl[1] = nl[2] = '\0'; /* no newline known yet */
+ if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
+ if (!(bf = fopen ((strcat (strcpy (backup,newsrc),OLDFILESUFFIX)),"wb"))) {
+ fclose (f); /* punt input file */
+ return newsrc_error("Can't create backup news state %.80s",backup,ERROR);
+ }
+ /* copy to backup file */
+ while ((c = getc (f)) != EOF) {
+ /* need to know about newlines and found it? */
+ if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+ /* sniff and see if an LF */
+ if ((c = getc (f)) == '\012') nl[1] = c;
+ ungetc (c,f); /* push it back */
+ }
+ /* write to backup file */
+ if ((d = putc (c,bf)) == EOF) {
+ fclose (f); /* punt input file */
+ return newsrc_error ("Error writing backup news state %.80s",
+ newsrc,ERROR);
+ }
+ }
+ fclose (f); /* close existing file */
+ if (fclose (bf) == EOF) /* and backup file */
+ return newsrc_error ("Error closing backup news state %.80s",
+ newsrc,ERROR);
+ if (d == EOF) { /* open for write if empty file */
+ if (f = newsrc_create (stream,NIL)) bf = NIL;
+ else return NIL;
+ }
+ else if (!nl[0]) /* make sure newlines valid */
+ return newsrc_error ("Unknown newline convention in %.80s",newsrc,ERROR);
+ /* now read backup file */
+ else if (!(bf = fopen (backup,"rb")))
+ return newsrc_error ("Error reading backup news state %.80s",
+ backup,ERROR);
+ /* open newsrc for writing */
+ else if (!(f = fopen (newsrc,"wb"))) {
+ fclose (bf); /* punt backup */
+ return newsrc_error ("Can't rewrite news state %.80s",newsrc,ERROR);
+ }
+ }
+ else { /* create new newsrc file */
+ if (f = newsrc_create (stream,T)) bf = NIL;
+ else return NIL; /* can't create newsrc */
+ }
+
+ while (bf) { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ /* saw correct end of group delimiter? */
+ if (tmp[0] && ((c == ':') || (c == '!'))) {
+ /* yes, write newsgroup name and delimiter */
+ if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
+ return newsrc_write_error (newsrc,bf,f);
+ if (!strcmp (tmp,group)) {/* found correct group? */
+ /* yes, write new status */
+ if (!newsrc_newmessages (f,stream,nl[0] ? nl : "\n"))
+ return newsrc_write_error (newsrc,bf,f);
+ /* skip past old data */
+ while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'));
+ /* skip past newline */
+ while ((c == '\015') || (c == '\012')) c = getc (bf);
+ while (c != EOF) { /* copy remainder of file */
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ c = getc (bf); /* get next character */
+ }
+ /* done with file */
+ if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,NIL);
+ f = NIL;
+ }
+ /* copy remainder of line */
+ else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ if (c == '\015') { /* write CR if seen */
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ /* sniff to see if LF */
+ if (((c = getc (bf)) != EOF) && (c != '\012')) ungetc (c,bf);
+ }
+ /* write LF if seen */
+ if ((c == '\012') && (putc (c,f) == EOF))
+ return newsrc_write_error (newsrc,bf,f);
+ }
+ if (c == EOF) { /* hit end of file? */
+ fclose (bf); /* yup, close the file */
+ bf = NIL;
+ }
+ }
+ if (f) { /* still have newsrc file open? */
+ ret = ((fputs (group,f) != EOF) && ((putc (':',f)) != EOF) &&
+ newsrc_newmessages (f,stream,nl[0] ? nl : "\n")) ? LONGT : NIL;
+ if (fclose (f) == EOF) ret = newsrc_write_error (newsrc,NIL,NIL);
+ }
+ else ret = LONGT;
+ return ret;
+}
+
+/* Get newsgroup state as text stream
+ * Accepts: MAIL stream
+ * newsgroup name
+ * Returns: string containing newsgroup state, or NIL if not found
+ */
+
+char *newsrc_state (MAILSTREAM *stream,char *group)
+{
+ int c = 0;
+ char *s,tmp[MAILTMPLEN];
+ long pos;
+ size_t size;
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ if ((c==':') || (c=='!')) { /* found newsgroup? */
+ if (strcmp (tmp,group)) /* group name match? */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ else { /* yes, skip leading whitespace */
+ do pos = ftell (f);
+ while ((c = getc (f)) == ' ');
+ /* count characters in state */
+ for (size = 0; (c != '\015') && (c != '\012') && (c != EOF); size++)
+ c = getc (f);
+ /* now copy it */
+ s = (char *) fs_get (size + 1);
+ fseek (f,pos,SEEK_SET);
+ fread (s,(size_t) 1,size,f);
+ s[size] = '\0'; /* tie off string */
+ fclose (f); /* all done - close the file */
+ return s;
+ }
+ }
+ } while (f && (c != EOF)); /* until file closed or EOF */
+ sprintf (tmp,"No state for newsgroup %.80s found",group);
+ MM_LOG (tmp,WARN);
+ if (f) fclose (f); /* close the file */
+ return NIL; /* not found return */
+}
+
+/* Check UID in newsgroup state
+ * Accepts: newsgroup state string
+ * uid
+ * returned recent count
+ * returned unseen count
+ */
+
+void newsrc_check_uid (unsigned char *state,unsigned long uid,
+ unsigned long *recent,unsigned long *unseen)
+{
+ unsigned long i,j;
+ while (*state) { /* until run out of state string */
+ /* collect a number */
+ for (i = 0; isdigit (*state); i = i*10 + (*state++ - '0'));
+ if (*state != '-') j = i; /* coerce single mesage into range */
+ else { /* have a range */
+ for (j = 0; isdigit (*++state); j = j*10 + (*state - '0'));
+ if (!j) j = i; /* guard against -0 */
+ if (j < i) return; /* bogon if end less than start */
+ }
+ if (*state == ',') state++; /* skip past comma */
+ else if (*state) return; /* otherwise it's a bogon */
+ if (uid <= j) { /* covered by upper bound? */
+ if (uid < i) ++*unseen; /* unseen if not covered by lower bound */
+ return; /* don't need to look further */
+ }
+ }
+ ++*unseen; /* not found in any range, message is unseen */
+ ++*recent; /* and recent */
+}
diff --git a/imap/src/c-client/newsrc.h b/imap/src/c-client/newsrc.h
new file mode 100644
index 00000000..578f656d
--- /dev/null
+++ b/imap/src/c-client/newsrc.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newsrc manipulation routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 September 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+long newsrc_error (char *fmt,char *text,long errflg);
+long newsrc_write_error (char *name,FILE *f1,FILE *f2);
+FILE *newsrc_create (MAILSTREAM *stream,int notify);
+long newsrc_newstate (FILE *f,char *group,char state,char *nl);
+long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl);
+void newsrc_lsub (MAILSTREAM *stream,char *pattern);
+long newsrc_update (MAILSTREAM *stream,char *group,char state);
+long newsrc_read (char *group,MAILSTREAM *stream);
+long newsrc_write (char *group,MAILSTREAM *stream);
+char *newsrc_state (MAILSTREAM *stream,char *group);
+void newsrc_check_uid (unsigned char *state,unsigned long uid,
+ unsigned long *recent,unsigned long *unseen);
diff --git a/imap/src/c-client/nl.h b/imap/src/c-client/nl.h
new file mode 100644
index 00000000..520e9f7e
--- /dev/null
+++ b/imap/src/c-client/nl.h
@@ -0,0 +1,34 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl);
+unsigned long strcrlflen (STRING *s);
diff --git a/imap/src/c-client/nntp.c b/imap/src/c-client/nntp.c
new file mode 100644
index 00000000..fe90edba
--- /dev/null
+++ b/imap/src/c-client/nntp.c
@@ -0,0 +1,2224 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network News Transfer Protocol (NNTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 February 1992
+ * Last Edited: 6 September 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+#include "newsrc.h"
+#include "netmsg.h"
+#include "flstring.h"
+
+/* Constants */
+
+#define NNTPSSLPORT (long) 563 /* assigned SSL TCP contact port */
+#define NNTPGREET (long) 200 /* NNTP successful greeting */
+ /* NNTP successful greeting w/o posting priv */
+#define NNTPGREETNOPOST (long) 201
+#define NNTPEXTOK (long) 202 /* NNTP extensions OK */
+#define NNTPGOK (long) 211 /* NNTP group selection OK */
+#define NNTPGLIST (long) 215 /* NNTP group list being returned */
+#define NNTPARTICLE (long) 220 /* NNTP article file */
+#define NNTPHEAD (long) 221 /* NNTP header text */
+#define NNTPBODY (long) 222 /* NNTP body text */
+#define NNTPOVER (long) 224 /* NNTP overview text */
+#define NNTPOK (long) 240 /* NNTP OK code */
+#define NNTPAUTHED (long) 281 /* NNTP successful authentication */
+ /* NNTP successful authentication with data */
+#define NNTPAUTHEDDATA (long) 282
+#define NNTPREADY (long) 340 /* NNTP ready for data */
+#define NNTPWANTAUTH2 (long) 380/* NNTP authentication needed (old) */
+#define NNTPWANTPASS (long) 381 /* NNTP password needed */
+#define NNTPTLSSTART (long) 382 /* NNTP continue with TLS negotiation */
+#define NNTPCHALLENGE (long) 383/* NNTP challenge, want response */
+#define NNTPSOFTFATAL (long) 400/* NNTP soft fatal code */
+#define NNTPWANTAUTH (long) 480 /* NNTP authentication needed */
+#define NNTPBADCMD (long) 500 /* NNTP unrecognized command */
+#define IDLETIMEOUT (long) 3 /* defined in NNTPEXT WG base draft */
+
+
+/* NNTP I/O stream local data */
+
+typedef struct nntp_local {
+ SENDSTREAM *nntpstream; /* NNTP stream for I/O */
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ unsigned int tlsflag : 1; /* TLS session */
+ unsigned int tlssslv23 : 1; /* TLS using SSLv23 client method */
+ unsigned int notlsflag : 1; /* TLS not used in session */
+ unsigned int sslflag : 1; /* SSL session */
+ unsigned int novalidate : 1; /* certificate not validated */
+ unsigned int xover : 1; /* supports XOVER */
+ unsigned int xhdr : 1; /* supports XHDR */
+ char *name; /* remote newsgroup name */
+ char *user; /* mailbox user */
+ char *newsrc; /* newsrc file */
+ char *over_fmt; /* overview format */
+ unsigned long msgno; /* current text message number */
+ FILE *txt; /* current text */
+ unsigned long txtsize; /* current text size */
+} NNTPLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NNTPLOCAL *) stream->local)
+
+
+/* Convenient access to protocol-specific data */
+
+#define NNTP stream->protocol.nntp
+
+
+/* Convenient access to extensions */
+
+#define EXTENSION LOCAL->nntpstream->protocol.nntp.ext
+
+/* Function prototypes */
+
+DRIVER *nntp_valid (char *name);
+DRIVER *nntp_isvalid (char *name,char *mbx);
+void *nntp_parameters (long function,void *value);
+void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void nntp_list (MAILSTREAM *stream,char *ref,char *pat);
+void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat);
+long nntp_subscribe (MAILSTREAM *stream,char *mailbox);
+long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long nntp_create (MAILSTREAM *stream,char *mailbox);
+long nntp_delete (MAILSTREAM *stream,char *mailbox);
+long nntp_rename (MAILSTREAM *stream,char *old,char *newname);
+long nntp_status (MAILSTREAM *stream,char *mbx,long flags);
+long nntp_getmap (MAILSTREAM *stream,char *name,
+ unsigned long first,unsigned long last,
+ unsigned long rnmsgs,unsigned long nmsgs,char *tmp);
+MAILSTREAM *nntp_mopen (MAILSTREAM *stream);
+void nntp_mclose (MAILSTREAM *stream,long options);
+void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
+void nntp_flags (MAILSTREAM *stream,char *sequence,long flags);
+long nntp_overview (MAILSTREAM *stream,overview_t ofn);
+long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt);
+long nntp_over (MAILSTREAM *stream,char *sequence);
+char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags);
+long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
+ unsigned long *hsiz);
+void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
+ OVERVIEW *ov);
+unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
+ unsigned long start,unsigned long last,
+ long flags);
+THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long nntp_ping (MAILSTREAM *stream);
+void nntp_check (MAILSTREAM *stream);
+long nntp_expunge (MAILSTREAM *stream,char *sequence,long options);
+long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long nntp_extensions (SENDSTREAM *stream,long flags);
+long nntp_send (SENDSTREAM *stream,char *command,char *args);
+long nntp_send_work (SENDSTREAM *stream,char *command,char *args);
+long nntp_send_auth (SENDSTREAM *stream,long flags);
+long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags);
+void *nntp_challenge (void *s,unsigned long *len);
+long nntp_response (void *s,char *response,unsigned long size);
+long nntp_reply (SENDSTREAM *stream);
+long nntp_fake (SENDSTREAM *stream,char *text);
+long nntp_soutr (void *stream,char *s);
+
+/* Driver dispatch used by MAIL */
+
+DRIVER nntpdriver = {
+ "nntp", /* driver name */
+ /* driver flags */
+#ifdef INADEQUATE_MEMORY
+ DR_LOWMEM |
+#endif
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_CRLF|DR_RECYCLE|DR_XPOINT |
+ DR_NOINTDATE|DR_NONEWMAIL|DR_HALFOPEN,
+ (DRIVER *) NIL, /* next driver */
+ nntp_valid, /* mailbox is valid for us */
+ nntp_parameters, /* manipulate parameters */
+ nntp_scan, /* scan mailboxes */
+ nntp_list, /* find mailboxes */
+ nntp_lsub, /* find subscribed mailboxes */
+ nntp_subscribe, /* subscribe to mailbox */
+ nntp_unsubscribe, /* unsubscribe from mailbox */
+ nntp_create, /* create mailbox */
+ nntp_delete, /* delete mailbox */
+ nntp_rename, /* rename mailbox */
+ nntp_status, /* status of mailbox */
+ nntp_mopen, /* open mailbox */
+ nntp_mclose, /* close mailbox */
+ nntp_fetchfast, /* fetch message "fast" attributes */
+ nntp_flags, /* fetch message flags */
+ nntp_overview, /* fetch overview */
+ NIL, /* fetch message structure */
+ nntp_header, /* fetch message header */
+ nntp_text, /* fetch message text */
+ NIL, /* fetch message */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ nntp_flagmsg, /* per-message modify flags */
+ nntp_search, /* search for message based on criteria */
+ nntp_sort, /* sort messages */
+ nntp_thread, /* thread messages */
+ nntp_ping, /* ping mailbox to see if still alive */
+ nntp_check, /* check for new messages */
+ nntp_expunge, /* expunge deleted messages */
+ nntp_copy, /* copy messages to another mailbox */
+ nntp_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM nntpproto = {&nntpdriver};
+
+
+ /* driver parameters */
+static unsigned long nntp_maxlogintrials = MAXLOGINTRIALS;
+static long nntp_port = 0;
+static long nntp_sslport = 0;
+static unsigned long nntp_range = 0;
+static long nntp_hidepath = 0;
+
+/* NNTP validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *nntp_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return nntp_isvalid (name,tmp);
+}
+
+
+/* NNTP validate mailbox work routine
+ * Accepts: mailbox name
+ * buffer for returned mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *nntp_isvalid (char *name,char *mbx)
+{
+ NETMBX mb;
+ if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,nntpdriver.name)||
+ mb.anoflag) return NIL;
+ if (mb.mailbox[0] != '#') strcpy (mbx,mb.mailbox);
+ /* namespace format name */
+ else if ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
+ (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
+ (mb.mailbox[5] == '.')) strcpy (mbx,mb.mailbox+6);
+ else return NIL; /* bogus name */
+ return &nntpdriver;
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *nntp_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ nntp_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) nntp_maxlogintrials;
+ break;
+ case SET_NNTPPORT:
+ nntp_port = (long) value;
+ break;
+ case GET_NNTPPORT:
+ value = (void *) nntp_port;
+ break;
+ case SET_SSLNNTPPORT:
+ nntp_sslport = (long) value;
+ break;
+ case GET_SSLNNTPPORT:
+ value = (void *) nntp_sslport;
+ break;
+ case SET_NNTPRANGE:
+ nntp_range = (unsigned long) value;
+ break;
+ case GET_NNTPRANGE:
+ value = (void *) nntp_range;
+ break;
+ case SET_NNTPHIDEPATH:
+ nntp_hidepath = (long) value;
+ break;
+ case GET_NNTPHIDEPATH:
+ value = (void *) nntp_hidepath;
+ break;
+ case GET_NEWSRC:
+ if (value)
+ value = (void *) ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->newsrc;
+ break;
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ case ENABLE_DEBUG:
+ if (value)
+ ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = T;
+ break;
+ case DISABLE_DEBUG:
+ if (value)
+ ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = NIL;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* NNTP mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (nntp_canonicalize (ref,pat,tmp,NIL))
+ mm_log ("Scan not valid for NNTP mailboxes",ERROR);
+}
+
+
+/* NNTP list newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void nntp_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ MAILSTREAM *st = stream;
+ char *s,*t,*lcl,pattern[MAILTMPLEN],name[MAILTMPLEN],wildmat[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ if (!*pat) {
+ if (nntp_canonicalize (ref,"*",pattern,NIL)) {
+ /* tie off name at root */
+ if ((s = strchr (pattern,'}')) && (s = strchr (s+1,'.'))) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,NIL);
+ }
+ }
+ /* ask server for open newsgroups */
+ else if (nntp_canonicalize (ref,pat,pattern,wildmat) &&
+ ((stream && LOCAL && LOCAL->nntpstream) ||
+ (stream = mail_open (NIL,pattern,OP_HALFOPEN|OP_SILENT))) &&
+ ((nntp_send (LOCAL->nntpstream,"LIST ACTIVE",
+ wildmat[0] ? wildmat : NIL) == NNTPGLIST) ||
+ (nntp_send (LOCAL->nntpstream,"LIST",NIL) == NNTPGLIST))) {
+ /* namespace format name? */
+ if (*(lcl = strchr (strcpy (name,pattern),'}') + 1) == '#') lcl += 6;
+ /* process data until we see final dot */
+ while (s = net_getline (LOCAL->nntpstream->netstream)) {
+ if ((*s == '.') && !s[1]){/* end of text */
+ fs_give ((void **) &s);
+ break;
+ }
+ if (t = strchr (s,' ')) { /* tie off after newsgroup name */
+ *t = '\0';
+ strcpy (lcl,s); /* make full form of name */
+ /* report if match */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else while (showuppers && (t = strrchr (lcl,'.'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ }
+ fs_give ((void **) &s); /* clean up */
+ }
+ if (stream != st) mail_close (stream);
+ }
+}
+
+/* NNTP list subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ /* return data from newsrc */
+ if (nntp_canonicalize (ref,pat,mbx,NIL)) newsrc_lsub (stream,mbx);
+ if (*pat == '{') { /* if remote pattern, must be NNTP */
+ if (!nntp_valid (pat)) return;
+ ref = NIL; /* good NNTP pattern, punt reference */
+ }
+ /* if remote reference, must be valid NNTP */
+ if (ref && (*ref == '{') && !nntp_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (nntp_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+/* NNTP canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * returned wildmat pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat)
+{
+ char *s;
+ DRIVER *ret;
+ if (ref && *ref) { /* have a reference */
+ if (!nntp_valid (ref)) return NIL;
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (strchr (pattern,'}') + 1,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((ret = wildmat ? /* if valid and wildmat */
+ nntp_isvalid (pattern,wildmat) : nntp_valid (pattern)) && wildmat) {
+ /* don't return wildmat if specials present */
+ if (strpbrk (wildmat,",?![\\]")) wildmat[0] = '\0';
+ /* replace all % with * */
+ for (s = wildmat; s = strchr (s,'%'); *s = '*');
+ }
+ return ret ? LONGT : NIL;
+}
+
+/* NNTP subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char mbx[MAILTMPLEN];
+ return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,':') : NIL;
+}
+
+
+/* NNTP unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char mbx[MAILTMPLEN];
+ return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,'!') : NIL;
+}
+
+/* NNTP create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+
+/* NNTP delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+
+/* NNTP rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+/* NNTP status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ NETMBX mb;
+ unsigned long i,j,k,rnmsgs;
+ long ret = NIL;
+ char *s,*name,*state,tmp[MAILTMPLEN];
+ char *old = (stream && !stream->halfopen) ? LOCAL->name : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!(mail_valid_net_parse (mbx,&mb) && !strcmp (mb.service,"nntp") &&
+ *mb.mailbox &&
+ ((mb.mailbox[0] != '#') ||
+ ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
+ (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
+ (mb.mailbox[5] == '.'))))) {
+ sprintf (tmp,"Invalid NNTP name %s",mbx);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* note mailbox name */
+ name = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
+ /* stream to reuse? */
+ if (!(stream && LOCAL->nntpstream &&
+ mail_usable_network_stream (stream,mbx)) &&
+ !(tstream = stream =
+ mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT|
+ ((flags & SA_MULNEWSRC) ? OP_MULNEWSRC : NIL))))
+ return NIL; /* can't reuse or make a new one */
+
+ if (nntp_send (LOCAL->nntpstream,"GROUP",name) == NNTPGOK) {
+ status.flags = flags; /* status validity flags */
+ k = strtoul (LOCAL->nntpstream->reply + 4,&s,10);
+ i = strtoul (s,&s,10); /* first assigned UID */
+ /* next UID to be assigned */
+ status.uidnext = (j = strtoul (s,NIL,10)) + 1;
+ /* maximum number of messages */
+ rnmsgs = status.messages = (i | j) ? status.uidnext - i : 0;
+ if (k > status.messages) { /* check for absurdity */
+ sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
+ k,status.messages);
+ mm_log (tmp,WARN);
+ }
+ /* restrict article range if needed */
+ if (nntp_range && (status.messages > nntp_range)) {
+ i = status.uidnext - (status.messages = nntp_range);
+ if (k > nntp_range) k = nntp_range;
+ }
+ /* initially zero */
+ status.recent = status.unseen = 0;
+ if (!status.messages); /* empty case */
+ /* use server guesstimate in simple case */
+ else if (!(flags & (SA_RECENT | SA_UNSEEN))) status.messages = k;
+
+ /* have newsrc state? */
+ else if (state = newsrc_state (stream,name)) {
+ /* yes, get the UID/sequence map */
+ if (nntp_getmap (stream,name,i,status.uidnext - 1,rnmsgs,
+ status.messages,tmp)) {
+ /* calculate true count */
+ for (status.messages = 0;
+ (s = net_getline (LOCAL->nntpstream->netstream)) &&
+ strcmp (s,"."); ) {
+ /* only count if in range */
+ if (((k = atol (s)) >= i) && (k < status.uidnext)) {
+ newsrc_check_uid (state,k,&status.recent,&status.unseen);
+ status.messages++;
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+ /* assume c-client/NNTP map is entire range */
+ else while (i < status.uidnext)
+ newsrc_check_uid (state,i++,&status.recent,&status.unseen);
+ fs_give ((void **) &state);
+ }
+ /* no .newsrc state, all messages new */
+ else status.recent = status.unseen = status.messages;
+ /* UID validity is a constant */
+ status.uidvalidity = stream->uid_validity;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = T; /* succes */
+ }
+ /* flush temporary stream */
+ if (tstream) mail_close (tstream);
+ /* else reopen old newsgroup */
+ else if (old && nntp_send (LOCAL->nntpstream,"GROUP",old) != NNTPGOK) {
+ mm_log (LOCAL->nntpstream->reply,ERROR);
+ stream->halfopen = T; /* go halfopen */
+ }
+ return ret; /* success */
+}
+
+/* NNTP get map
+ * Accepts: stream
+ * newsgroup name
+ * first UID in map range
+ * last UID in map range
+ * reported total number of messages in newsgroup
+ * calculated number of messages in range
+ * temporary buffer
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_getmap (MAILSTREAM *stream,char *name,
+ unsigned long first,unsigned long last,
+ unsigned long rnmsgs,unsigned long nmsgs,char *tmp)
+{
+ short trylistgroup = NIL;
+ if (rnmsgs > (nmsgs * 8)) /* small subrange? */
+ trylistgroup = T; /* yes, can try LISTGROUP if [X]HDR fails */
+ else switch ((int) nntp_send (LOCAL->nntpstream,"LISTGROUP",name)) {
+ case NNTPGOK: /* got data */
+ return LONGT;
+ default: /* else give up if server claims LISTGROUP */
+ if (EXTENSION.listgroup) return NIL;
+ }
+ /* build range */
+ sprintf (tmp,"%lu-%lu",first,last);
+ if (EXTENSION.hdr) /* have HDR extension? */
+ return (nntp_send (LOCAL->nntpstream,"HDR Date",tmp) == NNTPHEAD) ?
+ LONGT : NIL;
+ if (LOCAL->xhdr) /* try the experimental extension then */
+ switch ((int) nntp_send (LOCAL->nntpstream,"XHDR Date",tmp)) {
+ case NNTPHEAD: /* got an overview? */
+ return LONGT;
+ case NNTPBADCMD: /* unknown command? */
+ LOCAL->xhdr = NIL; /* disable future XHDR attempts */
+ }
+ if (trylistgroup && /* no [X]HDR, maybe do LISTGROUP after all */
+ (nntp_send (LOCAL->nntpstream,"LISTGROUP",name) == NNTPGOK))
+ return LONGT;
+ return NIL;
+}
+
+/* NNTP open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
+{
+ unsigned long i,j,k,nmsgs,rnmsgs;
+ char *s,*mbx,tmp[MAILTMPLEN];
+ FILE *f;
+ NETMBX mb;
+ char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
+ newsrcquery_t nq = (newsrcquery_t) mail_parameters (NIL,GET_NEWSRCQUERY,NIL);
+ SENDSTREAM *nstream = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &nntpproto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ /* note mailbox anme */
+ mbx = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
+ if (LOCAL) { /* recycle stream */
+ nstream = LOCAL->nntpstream;/* remember NNTP protocol stream */
+ sprintf (tmp,"Reusing connection to %s",net_host (nstream->netstream));
+ if (!stream->silent) mm_log (tmp,(long) NIL);
+ if (stream->rdonly) mb.readonlyflag = T;
+ if (LOCAL->tlsflag) mb.tlsflag = T;
+ if (LOCAL->tlssslv23) mb.tlssslv23 = T;
+ if (LOCAL->notlsflag) mb.notlsflag = T;
+ if (LOCAL->sslflag) mb.sslflag = T;
+ if (LOCAL->novalidate) mb.novalidate = T;
+ if (LOCAL->nntpstream->loser) mb.loser = T;
+ if (stream->secure) mb.secflag = T;
+ LOCAL->nntpstream = NIL; /* keep nntp_mclose() from punting it */
+ nntp_mclose (stream,NIL); /* do close action */
+ stream->dtb = &nntpdriver; /* reattach this driver */
+ }
+ /* copy flags */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.readonlyflag) stream->rdonly = T;
+ if (mb.secflag) stream->secure = T;
+ mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
+ if (!nstream) { /* open NNTP now if not already open */
+ char *hostlist[2];
+ hostlist[0] = strcpy (tmp,mb.host);
+ if (mb.port || nntp_port)
+ sprintf (tmp + strlen (tmp),":%lu",mb.port ? mb.port : nntp_port);
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ if (mb.loser) strcat (tmp,"/loser");
+ if (mb.secflag) strcat (tmp,"/secure");
+ if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=\"%s\"",mb.user);
+ hostlist[1] = NIL;
+ if (!(nstream = nntp_open (hostlist,NOP_READONLY |
+ (stream->debug ? NOP_DEBUG : NIL)))) return NIL;
+ }
+
+ /* always zero messages if halfopen */
+ if (stream->halfopen) i = j = k = rnmsgs = nmsgs = 0;
+ /* otherwise open the newsgroup */
+ else if (nntp_send (nstream,"GROUP",mbx) == NNTPGOK) {
+ k = strtoul (nstream->reply + 4,&s,10);
+ i = strtoul (s,&s,10);
+ stream->uid_last = j = strtoul (s,&s,10);
+ rnmsgs = nmsgs = (i | j) ? 1 + j - i : 0;
+ if (k > nmsgs) { /* check for absurdity */
+ sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
+ k,nmsgs);
+ mm_log (tmp,WARN);
+ }
+ /* restrict article range if needed */
+ if (nntp_range && (nmsgs > nntp_range)) i = 1 + j - (nmsgs = nntp_range);
+ }
+ else { /* no such newsgroup */
+ mm_log (nstream->reply,ERROR);
+ nntp_close (nstream); /* punt stream */
+ return NIL;
+ }
+ /* instantiate local data */
+ stream->local = memset (fs_get (sizeof (NNTPLOCAL)),0,sizeof (NNTPLOCAL));
+ LOCAL->nntpstream = nstream;
+ /* save state for future recycling */
+ if (mb.tlsflag) LOCAL->tlsflag = T;
+ if (mb.tlssslv23) LOCAL->tlssslv23 = T;
+ if (mb.notlsflag) LOCAL->notlsflag = T;
+ if (mb.sslflag) LOCAL->sslflag = T;
+ if (mb.novalidate) LOCAL->novalidate = T;
+ if (mb.loser) LOCAL->nntpstream->loser = T;
+ /* assume present until proven otherwise */
+ LOCAL->xhdr = LOCAL->xover = T;
+ LOCAL->name = cpystr (mbx); /* copy newsgroup name */
+ if (stream->mulnewsrc) { /* want to use multiple .newsrc files? */
+ strcpy (tmp,newsrc);
+ s = tmp + strlen (tmp); /* end of string */
+ *s++ = '-'; /* hyphen delimiter and host */
+ lcase (strcpy (s,(long) mail_parameters (NIL,GET_NEWSRCCANONHOST,NIL) ?
+ net_host (nstream->netstream) : mb.host));
+ LOCAL->newsrc = cpystr (nq ? (*nq) (stream,tmp,newsrc) : tmp);
+ }
+ else LOCAL->newsrc = cpystr (newsrc);
+ if (mb.user[0]) LOCAL->user = cpystr (mb.user);
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ sprintf (tmp,"{%s:%lu/nntp",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (nstream->netstream) : mb.host,
+ net_port (nstream->netstream));
+ if (LOCAL->tlsflag) strcat (tmp,"/tls");
+ if (LOCAL->tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (LOCAL->notlsflag) strcat (tmp,"/notls");
+ if (LOCAL->sslflag) strcat (tmp,"/ssl");
+ if (LOCAL->novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->nntpstream->loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ if (stream->rdonly) strcat (tmp,"/readonly");
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",LOCAL->user);
+ if (stream->halfopen) strcat (tmp,"}<no_mailbox>");
+ else sprintf (tmp + strlen (tmp),"}#news.%s",mbx);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+
+ if (EXTENSION.over && /* get overview format if have OVER */
+ (nntp_send (LOCAL->nntpstream,"LIST","OVERVIEW.FMT") == NNTPGLIST) &&
+ (f = netmsg_slurp (LOCAL->nntpstream->netstream,&k,NIL))) {
+ fread (LOCAL->over_fmt = (char *) fs_get ((size_t) k + 3),
+ (size_t) 1,(size_t) k,f);
+ LOCAL->over_fmt[k] = '\0';
+ fclose (f); /* flush temp file */
+ }
+ if (nmsgs) { /* if any messages exist */
+ short silent = stream->silent;
+ stream->silent = T; /* don't notify main program yet */
+ mail_exists (stream,nmsgs); /* silently set the cache to the guesstimate */
+ /* get UID/sequence map, nuke holes */
+ if (nntp_getmap (stream,mbx,i,j,rnmsgs,nmsgs,tmp)) {
+ for (nmsgs = 0; /* calculate true count */
+ (s = net_getline (nstream->netstream)) && strcmp (s,"."); ) {
+ if ((k = atol (s)) > j){/* discard too high article numbers */
+ sprintf (tmp,"NNTP SERVER BUG (out of range article ID): %lu > %lu",
+ k,j);
+ mm_notify (stream,tmp,NIL);
+ stream->unhealthy = T;
+ }
+ else if (k >= i) { /* silently ignore too-low article numbers */
+ /* guard against server returning extra msgs */
+ if (nmsgs == stream->nmsgs) mail_exists (stream,nmsgs+1);
+ /* create elt for this message, set UID */
+ mail_elt (stream,++nmsgs)->private.uid = k;
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+ /* assume c-client/NNTP map is entire range */
+ else for (k = 1; k <= nmsgs; k++) mail_elt (stream,k)->private.uid = i++;
+ stream->unhealthy = NIL; /* set healthy */
+ stream->nmsgs = 0; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (mbx,stream));
+ }
+ else { /* empty newsgroup or halfopen */
+ if (!(stream->silent || stream->halfopen)) {
+ sprintf (tmp,"Newsgroup %s is empty",mbx);
+ mm_log (tmp,WARN);
+ }
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ }
+ return stream; /* return stream to caller */
+}
+
+/* NNTP close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void nntp_mclose (MAILSTREAM *stream,long options)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (LOCAL) { /* only if a file is open */
+ nntp_check (stream); /* dump final checkpoint */
+ if (LOCAL->over_fmt) fs_give ((void **) &LOCAL->over_fmt);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ if (LOCAL->user) fs_give ((void **) &LOCAL->user);
+ if (LOCAL->newsrc) fs_give ((void **) &LOCAL->newsrc);
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ /* close NNTP connection */
+ if (LOCAL->nntpstream) nntp_close (LOCAL->nntpstream);
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.spare.ptr)
+ fs_give ((void **) &elt->private.spare.ptr);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* NNTP fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * This is ugly and slow
+ */
+
+void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* get sequence */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++) {
+ if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) &&
+ !(elt->day && elt->rfc822_size)) {
+ ENVELOPE **env = NIL;
+ ENVELOPE *e = NIL;
+ if (!stream->scache) env = &elt->private.msg.env;
+ else if (stream->msgno == i) env = &stream->env;
+ else env = &e;
+ if (!*env || !elt->rfc822_size) {
+ STRING bs;
+ unsigned long hs;
+ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
+ /* need to make an envelope? */
+ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
+ stream->dtb->flags);
+ /* need message size too, ugh */
+ if (!elt->rfc822_size) {
+ (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
+ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date)
+ mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ mail_free_envelope (&e);
+ }
+ }
+}
+
+/* NNTP fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void nntp_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* NNTP fetch overview
+ * Accepts: MAIL stream, sequence bits set
+ * overview return function
+ * Returns: T if successful, NIL otherwise
+ */
+
+long nntp_overview (MAILSTREAM *stream,overview_t ofn)
+{
+ unsigned long i,j,k,uid;
+ char c,*s,*t,*v,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ OVERVIEW ov;
+ if (!LOCAL->nntpstream->netstream) return NIL;
+ /* scan sequence to load cache */
+ for (i = 1; i <= stream->nmsgs; i++)
+ /* have cached overview yet? */
+ if ((elt = mail_elt (stream,i))->sequence && !elt->private.spare.ptr) {
+ for (j = i + 1; /* no, find end of cache gap range */
+ (j <= stream->nmsgs) && (elt = mail_elt (stream,j))->sequence &&
+ !elt->private.spare.ptr; j++);
+ /* make NNTP range */
+ sprintf (tmp,(i == (j - 1)) ? "%lu" : "%lu-%lu",mail_uid (stream,i),
+ mail_uid (stream,j - 1));
+ i = j; /* advance beyond gap */
+ /* ask server for overview data to cache */
+ if (nntp_over (stream,tmp)) {
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) &&
+ strcmp (s,".")) {
+ /* death to embedded newlines */
+ for (t = v = s; c = *v++;)
+ if ((c != '\012') && (c != '\015')) *t++ = c;
+ *t++ = '\0'; /* tie off string in case it was shortened */
+ /* cache the overview if found its sequence */
+ if ((uid = atol (s)) && (k = mail_msgno (stream,uid)) &&
+ (t = strchr (s,'\t'))) {
+ if ((elt = mail_elt (stream,k))->private.spare.ptr)
+ fs_give ((void **) &elt->private.spare.ptr);
+ elt->private.spare.ptr = cpystr (t + 1);
+ }
+ else { /* shouldn't happen, snarl if it does */
+ sprintf (tmp,"Server returned data for unknown UID %lu",uid);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ }
+ /* flush the overview */
+ fs_give ((void **) &s);
+ }
+ stream->unhealthy = NIL;/* set healthy */
+ /* flush the terminating dot */
+ if (s) fs_give ((void **) &s);
+ }
+ else i = stream->nmsgs; /* OVER failed, punt cache load */
+ }
+
+ /* now scan sequence to return overviews */
+ if (ofn) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ uid = mail_uid (stream,i);/* UID for this message */
+ /* parse cached overview */
+ if (nntp_parse_overview (&ov,s = (char *) elt->private.spare.ptr,elt))
+ (*ofn) (stream,uid,&ov,i);
+ else { /* parse failed */
+ (*ofn) (stream,uid,NIL,i);
+ if (s && *s) { /* unusable cached entry? */
+ sprintf (tmp,"Unable to parse overview for UID %lu: %.500s",uid,s);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ /* erase it from the cache */
+ fs_give ((void **) &s);
+ }
+ stream->unhealthy = NIL;/* set healthy */
+ /* insert empty cached text as necessary */
+ if (!s) elt->private.spare.ptr = cpystr ("");
+ }
+ /* clean up overview data */
+ if (ov.from) mail_free_address (&ov.from);
+ if (ov.subject) fs_give ((void **) &ov.subject);
+ }
+ return T;
+}
+
+/* Send OVER to NNTP server
+ * Accepts: mail stream
+ * sequence to send
+ * Returns: T if success and overviews will follow, else NIL
+ */
+
+long nntp_over (MAILSTREAM *stream,char *sequence)
+{
+ unsigned char *s;
+ /* test for Netscape Collabra server */
+ if (EXTENSION.over && LOCAL->xover &&
+ nntp_send (LOCAL->nntpstream,"OVER","0") == NNTPOVER) {
+ /* "Netscape-Collabra/3.52 03615 NNTP" responds to the OVER command with
+ * a bogus "Subject:From:Date:Bytes:Lines" response followed by overviews
+ * which lack the Message-ID and References:. This violates the draft
+ * NNTP specification (draft-ietf-nntpext-base-18.txt as of this writing).
+ * XOVER works fine.
+ */
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
+ if (!isdigit (*s)) { /* is it that fetid piece of reptile dung? */
+ EXTENSION.over = NIL; /* sure smells like it */
+ mm_log ("Working around Netscape Collabra bug",WARN);
+ }
+ fs_give ((void **) &s); /* flush the overview */
+ }
+ if (s) fs_give ((void **) &s);
+ /* don't do this test again */
+ if (EXTENSION.over) LOCAL->xover = NIL;
+ }
+ if (EXTENSION.over) /* have OVER extension? */
+ return (nntp_send (LOCAL->nntpstream,"OVER",sequence) == NNTPOVER) ?
+ LONGT : NIL;
+ if (LOCAL->xover) /* try the experiment extension then */
+ switch ((int) nntp_send (LOCAL->nntpstream,"XOVER",sequence)) {
+ case NNTPOVER: /* got an overview? */
+ return LONGT;
+ case NNTPBADCMD: /* unknown command? */
+ LOCAL->xover = NIL; /* disable future XOVER attempts */
+ }
+ return NIL;
+}
+
+/* Parse OVERVIEW struct from cached NNTP OVER response
+ * Accepts: struct to load
+ * cached OVER response
+ * internaldate
+ * Returns: T if success, NIL if fail
+ */
+
+long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt)
+{
+ char *t;
+ /* nothing in overview yet */
+ memset ((void *) ov,0,sizeof (OVERVIEW));
+ /* no cached data */
+ if (!(text && *text)) return NIL;
+ ov->subject = cpystr (text); /* make hackable copy of overview */
+ /* find end of Subject */
+ if (t = strchr (ov->subject,'\t')) {
+ *t++ = '\0'; /* tie off Subject, point to From */
+ /* find end of From */
+ if (ov->date = strchr (t,'\t')) {
+ *ov->date++ = '\0'; /* tie off From, point to Date */
+ /* load internaldate too */
+ if (!elt->day) mail_parse_date (elt,ov->date);
+ /* parse From */
+ rfc822_parse_adrlist (&ov->from,t,BADHOST);
+ /* find end of Date */
+ if (ov->message_id = strchr (ov->date,'\t')) {
+ /* tie off Date, point to Message-ID */
+ *ov->message_id++ = '\0';
+ /* find end of Message-ID */
+ if (ov->references = strchr (ov->message_id,'\t')) {
+ /* tie off Message-ID, point to References */
+ *ov->references++ = '\0';
+ /* fine end of References */
+ if (t = strchr (ov->references,'\t')) {
+ *t++ = '\0'; /* tie off References, point to octet size */
+ /* parse size of message in octets */
+ ov->optional.octets = atol (t);
+ /* find end of size */
+ if (t = strchr (t,'\t')) {
+ /* parse size of message in lines */
+ ov->optional.lines = atol (++t);
+ /* find Xref */
+ if (ov->optional.xref = strchr (t,'\t'))
+ *ov->optional.xref++ = '\0';
+ }
+ }
+ }
+ }
+ }
+ }
+ return ov->references ? T : NIL;
+}
+
+/* NNTP fetch header as text
+ * Accepts: mail stream
+ * message number
+ * pointer to return size
+ * flags
+ * Returns: header text
+ */
+
+char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ FILE *f;
+ *size = 0;
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
+ /* have header text? */
+ if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
+ sprintf (tmp,"%lu",mail_uid (stream,msgno));
+ /* get header text */
+ switch (nntp_send (LOCAL->nntpstream,"HEAD",tmp)) {
+ case NNTPHEAD:
+ if (f = netmsg_slurp (LOCAL->nntpstream->netstream,size,NIL)) {
+ fread (elt->private.msg.header.text.data =
+ (unsigned char *) fs_get ((size_t) *size + 3),
+ (size_t) 1,(size_t) *size,f);
+ fclose (f); /* flush temp file */
+ /* tie off header with extra CRLF and NUL */
+ elt->private.msg.header.text.data[*size] = '\015';
+ elt->private.msg.header.text.data[++*size] = '\012';
+ elt->private.msg.header.text.data[++*size] = '\0';
+ elt->private.msg.header.text.size = *size;
+ elt->valid = T; /* make elt valid now */
+ break;
+ }
+ /* fall into default case */
+ default: /* failed, mark as deleted and empty */
+ elt->valid = elt->deleted = T;
+ case NNTPSOFTFATAL: /* don't mark deleted if stream dead */
+ *size = elt->private.msg.header.text.size = 0;
+ break;
+ }
+ }
+ /* just return size of text */
+ else *size = elt->private.msg.header.text.size;
+ return elt->private.msg.header.text.data ?
+ (char *) elt->private.msg.header.text.data : "";
+}
+
+/* NNTP fetch body
+ * Accepts: mail stream
+ * message number
+ * pointer to stringstruct to initialize
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ INIT (bs,mail_string,(void *) "",0);
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* different message, flush cache */
+ if (LOCAL->txt && (LOCAL->msgno != msgno)) {
+ fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ }
+ LOCAL->msgno = msgno; /* note cached message */
+ if (!LOCAL->txt) { /* have file for this message? */
+ sprintf (tmp,"%lu",elt->private.uid);
+ switch (nntp_send (LOCAL->nntpstream,"BODY",tmp)) {
+ case NNTPBODY:
+ if (LOCAL->txt = netmsg_slurp (LOCAL->nntpstream->netstream,
+ &LOCAL->txtsize,NIL)) break;
+ /* fall into default case */
+ default: /* failed, mark as deleted */
+ elt->deleted = T;
+ case NNTPSOFTFATAL: /* don't mark deleted if stream dead */
+ return NIL;
+ }
+ }
+ if (!(flags & FT_PEEK)) { /* mark seen if needed */
+ elt->seen = T;
+ mm_flags (stream,elt->msgno);
+ }
+ INIT (bs,file_string,(void *) LOCAL->txt,LOCAL->txtsize);
+ return T;
+}
+
+/* NNTP fetch article from message ID (for news: URL support)
+ * Accepts: mail stream
+ * message ID
+ * pointer to return total message size
+ * pointer to return file size
+ * Returns: FILE * to message if successful, else NIL
+ */
+
+FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
+ unsigned long *hsiz)
+{
+ return (nntp_send (LOCAL->nntpstream,"ARTICLE",msgid) == NNTPARTICLE) ?
+ netmsg_slurp (LOCAL->nntpstream->netstream,size,hsiz) : NIL;
+}
+
+
+/* NNTP per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+/* NNTP search messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ OVERVIEW ov;
+ char *msg;
+ /* make sure that charset is good */
+ if (msg = utf8_badcharset (charset)) {
+ MM_LOG (msg,ERROR); /* output error */
+ fs_give ((void **) &msg);
+ return NIL;
+ }
+ utf8_searchpgm (pgm,charset);
+ if (flags & SO_OVERVIEW) { /* only if specified to use overview */
+ /* identify messages that will be searched */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ mail_elt (stream,i)->sequence = nntp_search_msg (stream,i,pgm,NIL);
+ nntp_overview (stream,NIL); /* load the overview cache */
+ }
+ /* init in case no overview at cleanup */
+ memset ((void *) &ov,0,sizeof (OVERVIEW));
+ /* otherwise do default search */
+ for (i = 1; i <= stream->nmsgs; ++i) {
+ if (((flags & SO_OVERVIEW) && ((elt = mail_elt (stream,i))->sequence) &&
+ nntp_parse_overview (&ov,(char *) elt->private.spare.ptr,elt)) ?
+ nntp_search_msg (stream,i,pgm,&ov) :
+ mail_search_msg (stream,i,NIL,pgm)) {
+ if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
+ else { /* mark as searched, notify mail program */
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ }
+ /* clean up overview data */
+ if (ov.from) mail_free_address (&ov.from);
+ if (ov.subject) fs_give ((void **) &ov.subject);
+ }
+ return LONGT;
+}
+
+/* NNTP search message
+ * Accepts: MAIL stream
+ * message number
+ * search program
+ * overview to search (NIL means preliminary pass)
+ * Returns: T if found, NIL otherwise
+ */
+
+long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
+ OVERVIEW *ov)
+{
+ unsigned short d;
+ unsigned long now = (unsigned long) time (0);
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ SEARCHHEADER *hdr;
+ SEARCHOR *or;
+ SEARCHPGMLIST *not;
+ if (pgm->msgno || pgm->uid) { /* message set searches */
+ SEARCHSET *set;
+ /* message sequences */
+ if (set = pgm->msgno) { /* must be inside this sequence */
+ while (set) { /* run down until find matching range */
+ if (set->last ? ((msgno < set->first) || (msgno > set->last)) :
+ msgno != set->first) set = set->next;
+ else break;
+ }
+ if (!set) return NIL; /* not found within sequence */
+ }
+ if (set = pgm->uid) { /* must be inside this sequence */
+ unsigned long uid = mail_uid (stream,msgno);
+ while (set) { /* run down until find matching range */
+ if (set->last ? ((uid < set->first) || (uid > set->last)) :
+ uid != set->first) set = set->next;
+ else break;
+ }
+ if (!set) return NIL; /* not found within sequence */
+ }
+ }
+
+ /* Fast data searches */
+ /* message flags */
+ if ((pgm->answered && !elt->answered) ||
+ (pgm->unanswered && elt->answered) ||
+ (pgm->deleted && !elt->deleted) ||
+ (pgm->undeleted && elt->deleted) ||
+ (pgm->draft && !elt->draft) ||
+ (pgm->undraft && elt->draft) ||
+ (pgm->flagged && !elt->flagged) ||
+ (pgm->unflagged && elt->flagged) ||
+ (pgm->recent && !elt->recent) ||
+ (pgm->old && elt->recent) ||
+ (pgm->seen && !elt->seen) ||
+ (pgm->unseen && elt->seen)) return NIL;
+ /* keywords */
+ if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
+ (pgm->unkeyword && mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
+ return NIL;
+ if (ov) { /* only do this if real searching */
+ MESSAGECACHE delt;
+ /* size ranges */
+ if ((pgm->larger && (ov->optional.octets <= pgm->larger)) ||
+ (pgm->smaller && (ov->optional.octets >= pgm->smaller))) return NIL;
+ /* date ranges */
+ if ((pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->before || pgm->on || pgm->since) &&
+ (!mail_parse_date (&delt,ov->date) ||
+ !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
+ (pgm->sentbefore && (d >= pgm->sentbefore)) ||
+ (pgm->senton && (d != pgm->senton)) ||
+ (pgm->sentsince && (d < pgm->sentsince)) ||
+ (pgm->before && (d >= pgm->before)) ||
+ (pgm->on && (d != pgm->on)) ||
+ (pgm->since && (d < pgm->since)))) return NIL;
+ if (pgm->older || pgm->younger) {
+ unsigned long msgd = mail_longdate (elt);
+ if (pgm->older && msgd > (now - pgm->older)) return NIL;
+ if (pgm->younger && msgd < (now - pgm->younger)) return NIL;
+ }
+ if ((pgm->from && !mail_search_addr (ov->from,pgm->from)) ||
+ (pgm->subject && !mail_search_header_text (ov->subject,pgm->subject))||
+ (pgm->message_id &&
+ !mail_search_header_text (ov->message_id,pgm->message_id)) ||
+ (pgm->references &&
+ !mail_search_header_text (ov->references,pgm->references)))
+ return NIL;
+
+
+ /* envelope searches */
+ if (pgm->bcc || pgm->cc || pgm->to || pgm->return_path || pgm->sender ||
+ pgm->reply_to || pgm->in_reply_to || pgm->newsgroups ||
+ pgm->followup_to) {
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ if (!env) return NIL; /* no envelope obtained */
+ /* search headers */
+ if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
+ (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
+ (pgm->to && !mail_search_addr (env->to,pgm->to)))
+ return NIL;
+ /* These criteria are not supported by IMAP and have to be emulated */
+ if ((pgm->return_path &&
+ !mail_search_addr (env->return_path,pgm->return_path)) ||
+ (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
+ (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
+ (pgm->in_reply_to &&
+ !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
+ (pgm->newsgroups &&
+ !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
+ (pgm->followup_to &&
+ !mail_search_header_text (env->followup_to,pgm->followup_to)))
+ return NIL;
+ }
+
+ /* search header lines */
+ for (hdr = pgm->header; hdr; hdr = hdr->next) {
+ char *t,*e,*v;
+ SIZEDTEXT s;
+ STRINGLIST sth,stc;
+ sth.next = stc.next = NIL;/* only one at a time */
+ sth.text.data = hdr->line.data;
+ sth.text.size = hdr->line.size;
+ /* get the header text */
+ if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
+ FT_INTERNAL | FT_PEEK)) && strchr (t,':')) {
+ if (hdr->text.size) { /* anything matches empty search string */
+ /* non-empty, copy field data */
+ s.data = (unsigned char *) fs_get (s.size + 1);
+ /* for each line */
+ for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
+ default: /* non-continuation, skip leading field name */
+ while ((t < e) && (*t++ != ':'));
+ if ((t < e) && (*t == ':')) t++;
+ case '\t': case ' ': /* copy field data */
+ while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
+ *v++ = '\n'; /* tie off line */
+ while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
+ }
+ /* calculate true size */
+ s.size = v - (char *) s.data;
+ *v = '\0'; /* tie off results */
+ stc.text.data = hdr->text.data;
+ stc.text.size = hdr->text.size;
+ /* search header */
+ if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
+ else { /* search failed */
+ fs_give ((void **) &s.data);
+ return NIL;
+ }
+ }
+ }
+ else return NIL; /* no matching header text */
+ }
+ /* search strings */
+ if ((pgm->text &&
+ !mail_search_text (stream,msgno,NIL,pgm->text,LONGT))||
+ (pgm->body && !mail_search_text (stream,msgno,NIL,pgm->body,NIL)))
+ return NIL;
+ }
+ /* logical conditions */
+ for (or = pgm->or; or; or = or->next)
+ if (!(nntp_search_msg (stream,msgno,or->first,ov) ||
+ nntp_search_msg (stream,msgno,or->second,ov))) return NIL;
+ for (not = pgm->not; not; not = not->next)
+ if (nntp_search_msg (stream,msgno,not->pgm,ov)) return NIL;
+ return T;
+}
+
+/* NNTP sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i,start,last;
+ SORTCACHE **sc;
+ mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ unsigned long *ret = NIL;
+ sortresults_t sr = (sortresults_t) mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ pgm->nmsgs++;
+ /* have this in the sortcache already? */
+ if (!((SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE))->date) {
+ /* no, record as last message */
+ last = mail_uid (stream,i);
+ /* and as first too if needed */
+ if (!start) start = last;
+ }
+ }
+ if (pgm->nmsgs) { /* pass 2: load sort cache */
+ sc = nntp_sort_loadcache (stream,pgm,start,last,flags);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ }
+ /* empty sort results */
+ else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
+ sizeof (unsigned long));
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,ret,pgm->nmsgs);
+ return ret;
+}
+
+/* Mail load sortcache
+ * Accepts: mail stream, already searched
+ * sort program
+ * first UID to OVER
+ * last UID to OVER
+ * option flags
+ * Returns: vector of sortcache pointers matching search
+ */
+
+SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
+ unsigned long start,unsigned long last,
+ long flags)
+{
+ unsigned long i;
+ char c,*s,*t,*v,tmp[MAILTMPLEN];
+ SORTPGM *pg;
+ SORTCACHE **sc,*r;
+ MESSAGECACHE telt;
+ ADDRESS *adr = NIL;
+ mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ /* verify that the sortpgm is OK */
+ for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
+ case SORTARRIVAL: /* sort by arrival date */
+ case SORTSIZE: /* sort by message size */
+ case SORTDATE: /* sort by date */
+ case SORTFROM: /* sort by first from */
+ case SORTSUBJECT: /* sort by subject */
+ break;
+ case SORTTO: /* sort by first to */
+ mm_notify (stream,"[NNTPSORT] Can't do To-field sorting in NNTP",WARN);
+ break;
+ case SORTCC: /* sort by first cc */
+ mm_notify (stream,"[NNTPSORT] Can't do cc-field sorting in NNTP",WARN);
+ break;
+ default:
+ fatal ("Unknown sort function");
+ }
+
+ if (start) { /* messages need to be loaded in sortcache? */
+ /* yes, build range */
+ if (start != last) sprintf (tmp,"%lu-%lu",start,last);
+ else sprintf (tmp,"%lu",start);
+ /* get it from the NNTP server */
+ if (!nntp_over (stream,tmp)) return mail_sort_loadcache (stream,pgm);
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
+ /* death to embedded newlines */
+ for (t = v = s; c = *v++;) if ((c != '\012') && (c != '\015')) *t++ = c;
+ *t++ = '\0'; /* tie off resulting string */
+ /* parse OVER response */
+ if ((i = mail_msgno (stream,atol (s))) &&
+ (t = strchr (s,'\t')) && (v = strchr (++t,'\t'))) {
+ *v++ = '\0'; /* tie off subject */
+ /* put stripped subject in sortcache */
+ r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ r->refwd = mail_strip_subject (t,&r->subject);
+ if (t = strchr (v,'\t')) {
+ *t++ = '\0'; /* tie off from */
+ if (adr = rfc822_parse_address (&adr,adr,&v,BADHOST,0)) {
+ r->from = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ if (v = strchr (t,'\t')) {
+ *v++ = '\0'; /* tie off date */
+ if (mail_parse_date (&telt,t)) r->date = mail_longdate (&telt);
+ if ((v = strchr (v,'\t')) && (v = strchr (++v,'\t')))
+ r->size = atol (++v);
+ }
+ }
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+
+ /* calculate size of sortcache index */
+ i = pgm->nmsgs * sizeof (SORTCACHE *);
+ /* instantiate the index */
+ sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ /* see what needs to be loaded */
+ for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
+ if ((mail_elt (stream,i))->searched) {
+ sc[pgm->progress.cached++] =
+ r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ r->pgm = pgm; /* note sort program */
+ r->num = (flags & SE_UID) ? mail_uid (stream,i) : i;
+ if (!r->date) r->date = r->num;
+ if (!r->arrival) r->arrival = mail_uid (stream,i);
+ if (!r->size) r->size = 1;
+ if (!r->from) r->from = cpystr ("");
+ if (!r->to) r->to = cpystr ("");
+ if (!r->cc) r->cc = cpystr ("");
+ if (!r->subject) r->subject = cpystr ("");
+ }
+ return sc;
+}
+
+
+/* NNTP thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree
+ */
+
+THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ return mail_thread_msgs (stream,type,charset,spg,flags,nntp_sort);
+}
+
+/* NNTP ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long nntp_ping (MAILSTREAM *stream)
+{
+ return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != NNTPSOFTFATAL);
+}
+
+
+/* NNTP check mailbox
+ * Accepts: MAIL stream
+ */
+
+void nntp_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* NNTP expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long nntp_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* NNTP copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for NNTP",ERROR);
+ return NIL;
+}
+
+
+/* NNTP append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for NNTP",ERROR);
+ return NIL;
+}
+
+/* NNTP open connection
+ * Accepts: network driver
+ * service host list
+ * port number
+ * service name
+ * NNTP open options
+ * Returns: SEND stream on success, NIL on failure
+ */
+
+SENDSTREAM *nntp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options)
+{
+ SENDSTREAM *stream = NIL;
+ NETSTREAM *netstream = NIL;
+ NETMBX mb;
+ char tmp[MAILTMPLEN];
+ long extok = LONGT;
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ if (!(hostlist && *hostlist)) mm_log ("Missing NNTP service host",ERROR);
+ else do { /* try to open connection */
+ sprintf (tmp,"{%.200s/%.20s}",*hostlist,service ? service : "nntp");
+ if (!mail_valid_net_parse (tmp,&mb) || mb.anoflag) {
+ sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
+ mm_log (tmp,ERROR);
+ }
+ else { /* light tryssl flag if requested */
+ mb.trysslflag = (options & NOP_TRYSSL) ? T : NIL;
+ /* default port */
+ if (mb.port) port = mb.port;
+ else if (!port) port = nntp_port ? nntp_port : NNTPTCPPORT;
+ if (netstream = /* try to open ordinary connection */
+ net_open (&mb,dv,port,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*nntps",nntp_sslport ? nntp_sslport : NNTPSSLPORT)) {
+ stream = (SENDSTREAM *) fs_get (sizeof (SENDSTREAM));
+ /* initialize stream */
+ memset ((void *) stream,0,sizeof (SENDSTREAM));
+ stream->netstream = netstream;
+ stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (netstream) : mb.host);
+ stream->debug = (mb.dbgflag || (options & NOP_DEBUG)) ? T : NIL;
+ if (mb.loser) stream->loser = T;
+ /* process greeting */
+ switch ((int) nntp_reply (stream)) {
+ case NNTPGREET: /* allow posting */
+ NNTP.post = T;
+ mm_notify (NIL,stream->reply + 4,(long) NIL);
+ break;
+ case NNTPGREETNOPOST: /* posting not allowed, must be readonly */
+ NNTP.post = NIL;
+ break;
+ default:
+ mm_log (stream->reply,ERROR);
+ stream = nntp_close (stream);
+ break;
+ }
+ }
+ }
+ } while (!stream && *++hostlist);
+
+ /* get extensions */
+ if (stream && extok)
+ extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ if (stream && !dv && stls && NNTP.ext.starttls &&
+ !mb.sslflag && !mb.notlsflag &&
+ (nntp_send_work (stream,"STARTTLS",NNTP.ext.multidomain ? mb.host : NIL)
+ == NNTPTLSSTART)) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ stream->netstream->dtb = ssld;
+ /* negotiate TLS */
+ if (stream->netstream->stream =
+ (*stls) (stream->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT:NIL)))
+ extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ else {
+ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",mb.host);
+ mm_log (tmp,ERROR);
+ /* close without doing QUIT */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ stream = nntp_close (stream);
+ }
+ }
+ else if (mb.tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ if (stream) { /* have a session? */
+ if (mb.user[0]) { /* yes, have user name? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),
+ NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (!nntp_send_auth_work (stream,&mb,tmp,NIL))
+ stream = nntp_close (stream);
+ }
+ /* authenticate if no-post and not readonly */
+ else if (!(NNTP.post || (options & NOP_READONLY) ||
+ nntp_send_auth (stream,NIL))) stream = nntp_close (stream);
+ }
+
+ /* in case server demands MODE READER */
+ if (stream) switch ((int) nntp_send_work (stream,"MODE","READER")) {
+ case NNTPGREET:
+ NNTP.post = T;
+ break;
+ case NNTPGREETNOPOST:
+ NNTP.post = NIL;
+ break;
+ case NNTPWANTAUTH: /* server wants auth first, do so and retry */
+ case NNTPWANTAUTH2: /* remote name for authentication */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ strncpy (mb.host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (nntp_send_auth_work (stream,&mb,tmp,NIL))
+ switch ((int) nntp_send (stream,"MODE","READER")) {
+ case NNTPGREET:
+ NNTP.post = T;
+ break;
+ case NNTPGREETNOPOST:
+ NNTP.post = NIL;
+ break;
+ }
+ else stream = nntp_close (stream);
+ break;
+ }
+ if (stream) { /* looks like we have a stream? */
+ /* yes, make sure can post if not readonly */
+ if (!(NNTP.post || (options & NOP_READONLY))) stream = nntp_close (stream);
+ else if (extok) nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ }
+ return stream;
+}
+
+/* NNTP extensions
+ * Accepts: stream
+ * authenticator flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_extensions (SENDSTREAM *stream,long flags)
+{
+ unsigned long i;
+ char *t,*r,*args;
+ /* zap all old extensions */
+ memset (&NNTP.ext,0,sizeof (NNTP.ext));
+ if (stream->loser) return NIL;/* nothing at all for losers */
+ /* get server extensions */
+ switch ((int) nntp_send_work (stream,"LIST","EXTENSIONS")) {
+ case NNTPEXTOK: /* what NNTP base spec says */
+ case NNTPGLIST: /* some servers do this instead */
+ break;
+ default: /* no LIST EXTENSIONS on this server */
+ return NIL;
+ }
+ NNTP.ext.ok = T; /* server offers extensions */
+ while ((t = net_getline (stream->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ /* get optional capability arguments */
+ if (args = strchr (t,' ')) *args++ = '\0';
+ if (!compare_cstring (t,"LISTGROUP")) NNTP.ext.listgroup = T;
+ else if (!compare_cstring (t,"OVER")) NNTP.ext.over = T;
+ else if (!compare_cstring (t,"HDR")) NNTP.ext.hdr = T;
+ else if (!compare_cstring (t,"PAT")) NNTP.ext.pat = T;
+ else if (!compare_cstring (t,"STARTTLS")) NNTP.ext.starttls = T;
+ else if (!compare_cstring (t,"MULTIDOMAIN")) NNTP.ext.multidomain = T;
+
+ else if (!compare_cstring (t,"AUTHINFO") && args) {
+ char *sasl = NIL;
+ for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r)) {
+ if (!compare_cstring (args,"USER")) NNTP.ext.authuser = T;
+ else if (((args[0] == 'S') || (args[0] == 's')) &&
+ ((args[1] == 'A') || (args[1] == 'a')) &&
+ ((args[2] == 'S') || (args[2] == 's')) &&
+ ((args[3] == 'L') || (args[3] == 'l')) && (args[4] == ':'))
+ sasl = args + 5;
+ }
+ if (sasl) { /* if SASL, look up authenticators */
+ for (sasl = strtok_r (sasl,",",&r); sasl; sasl = strtok_r (NIL,",",&r))
+ if ((i = mail_lookup_auth_name (sasl,flags)) &&
+ (--i < MAXAUTHENTICATORS))
+ NNTP.ext.sasl |= (1 << i);
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) &&
+ (--i < MAXAUTHENTICATORS) && (NNTP.ext.sasl & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) &&
+ (--i < MAXAUTHENTICATORS)) NNTP.ext.sasl &= ~(1 << i);
+ }
+ }
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ return LONGT;
+}
+
+/* NNTP close connection
+ * Accepts: SEND stream
+ * Returns: NIL always
+ */
+
+SENDSTREAM *nntp_close (SENDSTREAM *stream)
+{
+ if (stream) { /* send "QUIT" */
+ if (stream->netstream) nntp_send (stream,"QUIT",NIL);
+ /* do close actions */
+ if (stream->netstream) net_close (stream->netstream);
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ fs_give ((void **) &stream);/* flush the stream */
+ }
+ return NIL;
+}
+
+/* NNTP deliver news
+ * Accepts: SEND stream
+ * message envelope
+ * message body
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_mail (SENDSTREAM *stream,ENVELOPE *env,BODY *body)
+{
+ long ret;
+ RFC822BUFFER buf;
+ char *s,path[MAILTMPLEN],tmp[SENDBUFLEN+1];
+ long error = NIL;
+ long retry = NIL;
+ buf.f = nntp_soutr; /* initialize buffer */
+ buf.s = stream->netstream;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional null guard byte */
+ /* Gabba gabba hey, we need some brain damage to send netnews!!!
+ *
+ * First, we give ourselves a frontal lobotomy, and put in some UUCP
+ * syntax. It doesn't matter that it's completely bogus UUCP, and
+ * that UUCP has nothing to do with anything we're doing. It's been
+ * alleged that "Path: not-for-mail" is also acceptable, but we won't
+ * make assumptions unless the user says so.
+ *
+ * Second, we bop ourselves on the head with a ball-peen hammer. How
+ * dare we be so presumptious as to insert a *comment* in a Date:
+ * header line. Why, we were actually trying to be nice to a human
+ * by giving a symbolic timezone (such as PST) in addition to a
+ * numeric timezone (such as -0800). But the gods of news transport
+ * will have none of this. Unix weenies, tried and true, rule!!!
+ *
+ * Third, Netscape Collabra server doesn't give the NNTPWANTAUTH error
+ * until after requesting and receiving the entire message. So we can't
+ * call rely upon nntp_send() to do the auth retry.
+ */
+ /* RFC-1036 requires this cretinism */
+ sprintf (path,"Path: %s!%s\015\012",net_localhost (stream->netstream),
+ env->sender ? env->sender->mailbox :
+ (env->from ? env->from->mailbox : "not-for-mail"));
+ /* here's another cretinism */
+ if (s = strstr (env->date," (")) *s = NIL;
+ do if ((ret = nntp_send_work (stream,"POST",NIL)) == NNTPREADY)
+ /* output data, return success status */
+ ret = (net_soutr (stream->netstream,
+ nntp_hidepath ? "Path: not-for-mail\015\012" : path) &&
+ rfc822_output_full (&buf,env,body,T)) ?
+ nntp_send_work (stream,".",NIL) :
+ nntp_fake (stream,"NNTP connection broken (message text)");
+ while (((ret == NNTPWANTAUTH) || (ret == NNTPWANTAUTH2)) &&
+ nntp_send_auth (stream,LONGT));
+ if (s) *s = ' '; /* put the comment in the date back */
+ if (ret == NNTPOK) return LONGT;
+ else if (ret < 400) { /* if not an error reply */
+ sprintf (tmp,"Unexpected NNTP posting reply code %ld",ret);
+ mm_log (tmp,WARN); /* so someone looks at this eventually */
+ if ((ret >= 200) && (ret < 300)) return LONGT;
+ }
+ return NIL;
+}
+
+/* NNTP send command
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long nntp_send (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ switch ((int) (ret = nntp_send_work (stream,command,args))) {
+ case NNTPWANTAUTH: /* authenticate and retry */
+ case NNTPWANTAUTH2:
+ if (nntp_send_auth (stream,LONGT))
+ ret = nntp_send_work (stream,command,args);
+ else { /* we're probably hosed, nuke the session */
+ nntp_send (stream,"QUIT",NIL);
+ /* close net connection */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ default: /* all others just return */
+ break;
+ }
+ return ret;
+}
+
+
+/* NNTP send command worker routine
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long nntp_send_work (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0)
+ + 3);
+ if (!stream->netstream) ret = nntp_fake (stream,"NNTP connection lost");
+ else { /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,stream->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ ret = net_soutr (stream->netstream,s) ? nntp_reply (stream) :
+ nntp_fake (stream,"NNTP connection broken (command)");
+ }
+ fs_give ((void **) &s);
+ return ret;
+}
+
+/* NNTP send authentication if needed
+ * Accepts: SEND stream
+ * flags (non-NIL to get new extensions)
+ * Returns: T if need to redo command, NIL otherwise
+ */
+
+long nntp_send_auth (SENDSTREAM *stream,long flags)
+{
+ NETMBX mb;
+ char tmp[MAILTMPLEN];
+ /* remote name for authentication */
+ sprintf (tmp,"{%.200s/nntp",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ ((long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (stream->netstream) : net_host (stream->netstream)):
+ stream->host);
+ if (stream->netstream->dtb ==
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL))
+ strcat (tmp,"/ssl");
+ strcat (tmp,"}<none>");
+ mail_valid_net_parse (tmp,&mb);
+ return nntp_send_auth_work (stream,&mb,tmp,flags);
+}
+
+/* NNTP send authentication worker routine
+ * Accepts: SEND stream
+ * NETMBX structure
+ * scratch buffer of length MAILTMPLEN
+ * flags (non-NIL to get new extensions)
+ * Returns: T if authenticated, NIL otherwise
+ */
+
+long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
+{
+ unsigned long trial,auths;
+ char tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ AUTHENTICATOR *at;
+ char *lsterr = NIL;
+ long ret = NIL;
+ /* try SASL first */
+ for (auths = NNTP.ext.sasl, stream->saslcancel = NIL;
+ !ret && stream->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* empty buffer */
+ if (stream->netstream) do {
+ if (lsterr) {
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ stream->saslcancel = NIL;
+ if (nntp_send (stream,"AUTHINFO SASL",at->name) == NNTPCHALLENGE) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) stream->sensitive = T;
+ if ((*at->client) (nntp_challenge,nntp_response,"nntp",mb,stream,
+ &trial,usr)) {
+ if (stream->replycode == NNTPAUTHED) ret = LONGT;
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("NNTP Authentication cancelled",ERROR);
+ }
+ stream->sensitive = NIL;/* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) lsterr = cpystr (stream->reply);
+ } while (!ret && stream->netstream && trial &&
+ (trial < nntp_maxlogintrials));
+ }
+
+ if (lsterr) { /* SAIL failed? */
+ if (!stream->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to NNTP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ else if (mb->secflag) /* no SASL, can't do /secure */
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ else if (mb->authuser[0]) /* or /authuser */
+ mm_log ("Can't do /authuser with this server",ERROR);
+ /* Always try AUTHINFO USER, even if NNTP.ext.authuser isn't set. There
+ * are servers that require it but don't return it as an extension.
+ */
+ else for (trial = 0, pwd[0] = 'x';
+ !ret && pwd[0] && (trial < nntp_maxlogintrials) &&
+ stream->netstream; ) {
+ pwd[0] = NIL; /* get user name and password */
+ mm_login (mb,usr,pwd,trial++);
+ /* do the authentication */
+ if (pwd[0]) switch ((int) nntp_send_work (stream,"AUTHINFO USER",usr)) {
+ case NNTPBADCMD: /* give up if unrecognized command */
+ mm_log (NNTP.ext.authuser ? stream->reply :
+ "Can't do AUTHINFO USER to this server",ERROR);
+ trial = nntp_maxlogintrials;
+ break;
+ case NNTPAUTHED: /* successful authentication */
+ ret = LONGT; /* guess no password was needed */
+ break;
+ case NNTPWANTPASS: /* wants password */
+ stream->sensitive = T; /* hide this command */
+ if (nntp_send_work (stream,"AUTHINFO PASS",pwd) == NNTPAUTHED)
+ ret = LONGT; /* password OK */
+ stream->sensitive = NIL; /* unhide */
+ if (ret) break; /* OK if successful */
+ default: /* authentication failed */
+ mm_log (stream->reply,WARN);
+ if (trial == nntp_maxlogintrials)
+ mm_log ("Too many NNTP authentication failures",ERROR);
+ }
+ /* user refused to give a password */
+ else mm_log ("Login aborted",ERROR);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ /* get new extensions if needed */
+ if (ret && flags) nntp_extensions (stream,(mb->secflag ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL));
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *nntp_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ if ((stream->replycode == NNTPCHALLENGE) &&
+ !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
+ strlen (stream->reply + 4),len))) {
+ sprintf (tmp,"NNTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T, always
+ */
+
+long nntp_response (void *s,char *response,unsigned long size)
+{
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ unsigned long i,j;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string */
+ i = nntp_send_work (stream,t,NIL);
+ fs_give ((void **) &t);
+ }
+ else i = nntp_send_work (stream,"",NIL);
+ }
+ else { /* abort requested */
+ i = nntp_send_work (stream,"*",NIL);
+ stream->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return LONGT;
+}
+
+/* NNTP get reply
+ * Accepts: SEND stream
+ * Returns: reply code
+ */
+
+long nntp_reply (SENDSTREAM *stream)
+{
+ /* flush old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* get reply */
+ if (!(stream->reply = net_getline (stream->netstream)))
+ return nntp_fake (stream,"NNTP connection broken (response)");
+ if (stream->debug) mm_dlog (stream->reply);
+ /* handle continuation by recursion */
+ if (stream->reply[3] == '-') return nntp_reply (stream);
+ /* return response code */
+ return stream->replycode = atol (stream->reply);
+}
+
+
+/* NNTP set fake error
+ * Accepts: SEND stream
+ * error text
+ * Returns: error code
+ */
+
+long nntp_fake (SENDSTREAM *stream,char *text)
+{
+ if (stream->netstream) { /* close net connection if still open */
+ net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ /* flush any old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* set up pseudo-reply string */
+ stream->reply = (char *) fs_get (20+strlen (text));
+ sprintf (stream->reply,"%ld %s",NNTPSOFTFATAL,text);
+ return NNTPSOFTFATAL; /* return error code */
+}
+
+/* NNTP filter mail
+ * Accepts: stream
+ * string
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_soutr (void *stream,char *s)
+{
+ char c,*t;
+ /* "." on first line */
+ if (s[0] == '.') net_soutr (stream,".");
+ /* find lines beginning with a "." */
+ while (t = strstr (s,"\015\012.")) {
+ c = *(t += 3); /* remember next character after "." */
+ *t = '\0'; /* tie off string */
+ /* output prefix */
+ if (!net_soutr (stream,s)) return NIL;
+ *t = c; /* restore delimiter */
+ s = t - 1; /* push pointer up to the "." */
+ }
+ /* output remainder of text */
+ return *s ? net_soutr (stream,s) : T;
+}
diff --git a/imap/src/c-client/nntp.h b/imap/src/c-client/nntp.h
new file mode 100644
index 00000000..445ae4d7
--- /dev/null
+++ b/imap/src/c-client/nntp.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network News Transfer Protocol (NNTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 February 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Constants (should be in nntp.c) */
+
+#define NNTPTCPPORT (long) 119 /* assigned TCP contact port */
+
+
+/* NNTP open options
+ * For compatibility with the past, NOP_DEBUG must always be 1.
+ */
+
+#define NOP_DEBUG (long) 0x1 /* debug protocol negotiations */
+#define NOP_READONLY (long) 0x2 /* read-only open */
+#define NOP_TRYSSL (long) 0x4 /* try SSL first */
+ /* reserved for application use */
+#define NOP_RESERVED (unsigned long) 0xff000000
+
+
+/* Compatibility support names */
+
+#define nntp_open(hostlist,options) \
+ nntp_open_full (NIL,hostlist,"nntp",NIL,options)
+
+
+/* Function prototypes */
+
+SENDSTREAM *nntp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options);
+SENDSTREAM *nntp_close (SENDSTREAM *stream);
+long nntp_mail (SENDSTREAM *stream,ENVELOPE *msg,BODY *body);
diff --git a/imap/src/c-client/pop3.c b/imap/src/c-client/pop3.c
new file mode 100644
index 00000000..58a9ceb6
--- /dev/null
+++ b/imap/src/c-client/pop3.c
@@ -0,0 +1,1091 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Post Office Protocol 3 (POP3) client routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 June 1994
+ * Last Edited: 4 April 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+#include "flstring.h"
+#include "netmsg.h"
+
+/* Parameters */
+
+#define POP3TCPPORT (long) 110 /* assigned TCP contact port */
+#define POP3SSLPORT (long) 995 /* assigned SSL TCP contact port */
+#define IDLETIMEOUT (long) 10 /* defined in RFC 1939 */
+
+
+/* POP3 I/O stream local data */
+
+typedef struct pop3_local {
+ NETSTREAM *netstream; /* TCP I/O stream */
+ char *response; /* last server reply */
+ char *reply; /* text of last server reply */
+ unsigned long cached; /* current cached message uid */
+ unsigned long hdrsize; /* current cached header size */
+ FILE *txt; /* current cached file descriptor */
+ struct {
+ unsigned int capa : 1; /* server has CAPA, definitely new */
+ unsigned int expire : 1; /* server has EXPIRE */
+ unsigned int logindelay : 1;/* server has LOGIN-DELAY */
+ unsigned int stls : 1; /* server has STLS */
+ unsigned int pipelining : 1;/* server has PIPELINING */
+ unsigned int respcodes : 1; /* server has RESP-CODES */
+ unsigned int top : 1; /* server has TOP */
+ unsigned int uidl : 1; /* server has UIDL */
+ unsigned int user : 1; /* server has USER */
+ char *implementation; /* server implementation string */
+ long delaysecs; /* minimum time between login (neg variable) */
+ long expiredays; /* server-guaranteed minimum retention days */
+ /* supported authenticators */
+ unsigned int sasl : MAXAUTHENTICATORS;
+ } cap;
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+} POP3LOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((POP3LOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *pop3_valid (char *name);
+void *pop3_parameters (long function,void *value);
+void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void pop3_list (MAILSTREAM *stream,char *ref,char *pat);
+void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long pop3_subscribe (MAILSTREAM *stream,char *mailbox);
+long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long pop3_create (MAILSTREAM *stream,char *mailbox);
+long pop3_delete (MAILSTREAM *stream,char *mailbox);
+long pop3_rename (MAILSTREAM *stream,char *old,char *newname);
+long pop3_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *pop3_open (MAILSTREAM *stream);
+long pop3_capa (MAILSTREAM *stream,long flags);
+long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr);
+void *pop3_challenge (void *stream,unsigned long *len);
+long pop3_response (void *stream,char *s,unsigned long size);
+void pop3_close (MAILSTREAM *stream,long options);
+void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
+char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags);
+long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt);
+long pop3_ping (MAILSTREAM *stream);
+void pop3_check (MAILSTREAM *stream);
+long pop3_expunge (MAILSTREAM *stream,char *sequence,long options);
+long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n);
+long pop3_send (MAILSTREAM *stream,char *command,char *args);
+long pop3_reply (MAILSTREAM *stream);
+long pop3_fake (MAILSTREAM *stream,char *text);
+
+/* POP3 mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER pop3driver = {
+ "pop3", /* driver name */
+ /* driver flags */
+#ifdef INADEQUATE_MEMORY
+ DR_LOWMEM |
+#endif
+ DR_MAIL|DR_NOFAST|DR_CRLF|DR_NOSTICKY|DR_NOINTDATE|DR_NONEWMAIL,
+ (DRIVER *) NIL, /* next driver */
+ pop3_valid, /* mailbox is valid for us */
+ pop3_parameters, /* manipulate parameters */
+ pop3_scan, /* scan mailboxes */
+ pop3_list, /* find mailboxes */
+ pop3_lsub, /* find subscribed mailboxes */
+ pop3_subscribe, /* subscribe to mailbox */
+ pop3_unsubscribe, /* unsubscribe from mailbox */
+ pop3_create, /* create mailbox */
+ pop3_delete, /* delete mailbox */
+ pop3_rename, /* rename mailbox */
+ pop3_status, /* status of mailbox */
+ pop3_open, /* open mailbox */
+ pop3_close, /* close mailbox */
+ pop3_fetchfast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ pop3_header, /* fetch message header */
+ pop3_text, /* fetch message text */
+ NIL, /* fetch message */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ pop3_ping, /* ping mailbox to see if still alive */
+ pop3_check, /* check for new messages */
+ pop3_expunge, /* expunge deleted messages */
+ pop3_copy, /* copy messages to another mailbox */
+ pop3_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM pop3proto = {&pop3driver};
+
+ /* driver parameters */
+static unsigned long pop3_maxlogintrials = MAXLOGINTRIALS;
+static long pop3_port = 0;
+static long pop3_sslport = 0;
+
+/* POP3 mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *pop3_valid (char *name)
+{
+ NETMBX mb;
+ return (mail_valid_net_parse (name,&mb) &&
+ !strcmp (mb.service,pop3driver.name) && !mb.authuser[0] &&
+ !compare_cstring (mb.mailbox,"INBOX")) ? &pop3driver : NIL;
+}
+
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *pop3_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ pop3_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) pop3_maxlogintrials;
+ break;
+ case SET_POP3PORT:
+ pop3_port = (long) value;
+ break;
+ case GET_POP3PORT:
+ value = (void *) pop3_port;
+ break;
+ case SET_SSLPOPPORT:
+ pop3_sslport = (long) value;
+ break;
+ case GET_SSLPOPPORT:
+ value = (void *) pop3_sslport;
+ break;
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* POP3 mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if ((ref && *ref) ? /* have a reference */
+ (pop3_valid (ref) && pmatch ("INBOX",pat)) :
+ (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
+ mm_log ("Scan not valid for POP3 mailboxes",ERROR);
+}
+
+
+/* POP3 mail find list of all mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void pop3_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ if (pop3_valid (ref) && pmatch ("INBOX",pat)) {
+ strcpy (strchr (strcpy (tmp,ref),'}')+1,"INBOX");
+ mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
+ }
+ }
+ else if (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)) {
+ strcpy (strchr (strcpy (tmp,pat),'}')+1,"INBOX");
+ mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
+ }
+}
+
+/* POP3 mail find list of subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ if (*pat == '{') { /* if remote pattern, must be POP3 */
+ if (!pop3_valid (pat)) return;
+ ref = NIL; /* good POP3 pattern, punt reference */
+ }
+ /* if remote reference, must be valid POP3 */
+ if (ref && (*ref == '{') && !pop3_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (pop3_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* POP3 mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* POP3 mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* POP3 mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+
+/* POP3 mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+
+/* POP3 mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+/* POP3 status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ long ret = NIL;
+ MAILSTREAM *tstream =
+ (stream && LOCAL->netstream && mail_usable_network_stream (stream,mbx)) ?
+ stream : mail_open (NIL,mbx,OP_SILENT);
+ if (tstream) { /* have a usable stream? */
+ status.flags = flags; /* return status values */
+ status.messages = tstream->nmsgs;
+ status.recent = tstream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= tstream->nmsgs; i++)
+ if (!mail_elt (tstream,i)->seen) status.unseen++;
+ status.uidnext = tstream->uid_last + 1;
+ status.uidvalidity = tstream->uid_validity;
+ /* pass status to main program */
+ mm_status (tstream,mbx,&status);
+ if (stream != tstream) mail_close (tstream);
+ ret = LONGT;
+ }
+ return ret; /* success */
+}
+
+/* POP3 mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *pop3_open (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ char *s,*t,tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ NETMBX mb;
+ MESSAGECACHE *elt;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &pop3proto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ usr[0] = '\0'; /* initially no user name */
+ if (stream->local) fatal ("pop3 recycle stream");
+ /* /anonymous not supported */
+ if (mb.anoflag || stream->anonymous) {
+ mm_log ("Anonymous POP3 login not available",ERROR);
+ return NIL;
+ }
+ /* /readonly not supported either */
+ if (mb.readonlyflag || stream->rdonly) {
+ mm_log ("Read-only POP3 access not available",ERROR);
+ return NIL;
+ }
+ /* copy other switches */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.secflag) stream->secure = T;
+ mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
+ stream->local = /* instantiate localdata */
+ (void *) memset (fs_get (sizeof (POP3LOCAL)),0,sizeof (POP3LOCAL));
+ stream->sequence++; /* bump sequence number */
+ stream->perm_deleted = T; /* deleted is only valid flag */
+
+ if ((LOCAL->netstream = /* try to open connection */
+ net_open (&mb,NIL,pop3_port ? pop3_port : POP3TCPPORT,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*pop3s",pop3_sslport ? pop3_sslport : POP3SSLPORT)) &&
+ pop3_reply (stream)) {
+ mm_log (LOCAL->reply,NIL); /* give greeting */
+ if (!pop3_auth (stream,&mb,tmp,usr)) pop3_close (stream,NIL);
+ else if (pop3_send (stream,"STAT",NIL)) {
+ int silent = stream->silent;
+ stream->silent = T;
+ sprintf (tmp,"{%.200s:%lu/pop3",
+ (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (LOCAL->netstream) : mb.host,
+ net_port (LOCAL->netstream));
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->loser = mb.loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ sprintf (tmp + strlen (tmp),"/user=\"%s\"}%s",usr,mb.mailbox);
+ stream->inbox = T; /* always INBOX */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* notify upper level */
+ mail_exists (stream,stream->uid_last = strtoul (LOCAL->reply,NIL,10));
+ mail_recent (stream,stream->nmsgs);
+ /* instantiate elt */
+ for (i = 0; i < stream->nmsgs;) {
+ elt = mail_elt (stream,++i);
+ elt->valid = elt->recent = T;
+ elt->private.uid = i;
+ }
+
+ /* trust LIST output if new server */
+ if (!LOCAL->loser && LOCAL->cap.capa && pop3_send (stream,"LIST",NIL)) {
+ while ((s = net_getline (LOCAL->netstream)) && (*s != '.')) {
+ if ((i = strtoul (s,&t,10)) && (i <= stream->nmsgs) &&
+ (j = strtoul (t,NIL,10))) mail_elt (stream,i)->rfc822_size = j;
+ fs_give ((void **) &s);
+ }
+ /* flush final dot */
+ if (s) fs_give ((void **) &s);
+ else { /* lost connection */
+ mm_log ("POP3 connection broken while itemizing messages",ERROR);
+ pop3_close (stream,NIL);
+ return NIL;
+ }
+ }
+ stream->silent = silent; /* notify main program */
+ mail_exists (stream,stream->nmsgs);
+ /* notify if empty */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
+ }
+ else { /* error in STAT */
+ mm_log (LOCAL->reply,ERROR);
+ pop3_close (stream,NIL); /* too bad */
+ }
+ }
+ else { /* connection failed */
+ if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
+ pop3_close (stream,NIL); /* failed, clean up */
+ }
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* POP3 capabilities
+ * Accepts: stream
+ * authenticator flags
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_capa (MAILSTREAM *stream,long flags)
+{
+ unsigned long i;
+ char *s,*t,*r,*args;
+ if (LOCAL->cap.implementation)/* zap all old capabilities */
+ fs_give ((void **) &LOCAL->cap.implementation);
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ /* get server capabilities */
+ if (pop3_send (stream,"CAPA",NIL)) LOCAL->cap.capa = T;
+ else {
+ LOCAL->cap.user = T; /* guess worst-case old server */
+ return NIL; /* no CAPA on this server */
+ }
+ while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ /* get optional capability arguments */
+ if (args = strchr (t,' ')) *args++ = '\0';
+ if (!compare_cstring (t,"STLS")) LOCAL->cap.stls = T;
+ else if (!compare_cstring (t,"PIPELINING")) LOCAL->cap.pipelining = T;
+ else if (!compare_cstring (t,"RESP-CODES")) LOCAL->cap.respcodes = T;
+ else if (!compare_cstring (t,"TOP")) LOCAL->cap.top = T;
+ else if (!compare_cstring (t,"UIDL")) LOCAL->cap.uidl = T;
+ else if (!compare_cstring (t,"USER")) LOCAL->cap.user = T;
+ else if (!compare_cstring (t,"IMPLEMENTATION") && args)
+ LOCAL->cap.implementation = cpystr (args);
+ else if (!compare_cstring (t,"EXPIRE") && args) {
+ LOCAL->cap.expire = T; /* note that it is present */
+ if (s = strchr(args,' ')){/* separate time from possible USER */
+ *s++ = '\0';
+ /* in case they add something after USER */
+ if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
+ }
+ LOCAL->cap.expire = /* get expiration time */
+ (!compare_cstring (args,"NEVER")) ? 65535 :
+ ((s && !compare_cstring (s,"USER")) ? -atoi (args) : atoi (args));
+ }
+ else if (!compare_cstring (t,"LOGIN-DELAY") && args) {
+ LOCAL->cap.logindelay = T;/* note that it is present */
+ if (s = strchr(args,' ')){/* separate time from possible USER */
+ *s++ = '\0';
+ /* in case they add something after USER */
+ if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
+ }
+ /* get delay time */
+ LOCAL->cap.delaysecs = (s && !compare_cstring (s,"USER")) ?
+ -atoi (args) : atoi (args);
+ }
+ else if (!compare_cstring (t,"SASL") && args)
+ for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r))
+ if ((i = mail_lookup_auth_name (args,flags)) &&
+ (--i < MAXAUTHENTICATORS))
+ LOCAL->cap.sasl |= (1 << i);
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ return LONGT;
+}
+
+/* POP3 authenticate
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer of length MAILTMPLEN
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
+{
+ unsigned long i,trial,auths = 0;
+ char *t;
+ AUTHENTICATOR *at;
+ long ret = NIL;
+ long flags = (stream->secure ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL);
+ long capaok = pop3_capa (stream,flags);
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ /* server has TLS? */
+ if (stls && LOCAL->cap.stls && !mb->sslflag && !mb->notlsflag &&
+ pop3_send (stream,"STLS",NIL)) {
+ mb->tlsflag = T; /* TLS OK, get into TLS at this end */
+ LOCAL->netstream->dtb = ssld;
+ if (!(LOCAL->netstream->stream =
+ (*stls) (LOCAL->netstream->stream,mb->host,
+ (mb->tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb->novalidate ? NET_NOVALIDATECERT : NIL)))) {
+ /* drat, drop this connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream= NIL;
+ return NIL; /* TLS negotiation failed */
+ }
+ pop3_capa (stream,flags); /* get capabilities now that TLS in effect */
+ }
+ else if (mb->tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ /* get authenticators from capabilities */
+ if (capaok) auths = LOCAL->cap.sasl;
+ /* get list of authenticators */
+ else if (pop3_send (stream,"AUTH",NIL)) {
+ while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ if ((i = mail_lookup_auth_name (t,flags)) && (--i < MAXAUTHENTICATORS))
+ auths |= (1 << i);
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ }
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
+ (auths & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
+ auths &= ~(1 << i);
+
+ if (auths) { /* got any authenticators? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb->host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (LOCAL->netstream) : net_host (LOCAL->netstream),
+ NETMAXHOST-1);
+ mb->host[NETMAXHOST-1] = '\0';
+ }
+ for (t = NIL, LOCAL->saslcancel = NIL; !ret && LOCAL->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
+ if (t) { /* previous authenticator failed? */
+ sprintf (pwd,"Retrying using %.80s authentication after %.80s",
+ at->name,t);
+ mm_log (pwd,NIL);
+ fs_give ((void **) &t);
+ }
+ trial = 0; /* initial trial count */
+ do {
+ if (t) {
+ sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
+ mm_log (pwd,WARN);
+ fs_give ((void **) &t);
+ }
+ LOCAL->saslcancel = NIL;
+ if (pop3_send (stream,"AUTH",at->name)) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
+ if ((*at->client) (pop3_challenge,pop3_response,"pop",mb,stream,
+ &trial,usr) && LOCAL->response) {
+ if (*LOCAL->response == '+') ret = LONGT;
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("POP3 Authentication cancelled",ERROR);
+ }
+ LOCAL->sensitive=NIL; /* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) t = cpystr (LOCAL->reply);
+ } while (!ret && trial && (trial < pop3_maxlogintrials) &&
+ LOCAL->netstream);
+ }
+ if (t) { /* previous authenticator failed? */
+ if (!LOCAL->saslcancel) { /* don't do this if a cancel */
+ sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
+ mm_log (pwd,ERROR);
+ }
+ fs_give ((void **) &t);
+ }
+ }
+
+ else if (stream->secure)
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ else if (mb->authuser[0])
+ mm_log ("Can't do /authuser with this server",ERROR);
+ else if (!LOCAL->cap.user) mm_log ("Can't login to this server",ERROR);
+ else { /* traditional login */
+ trial = 0; /* initial trial count */
+ do {
+ pwd[0] = 0; /* prompt user for password */
+ mm_login (mb,usr,pwd,trial++);
+ if (pwd[0]) { /* send login sequence if have password */
+ if (pop3_send (stream,"USER",usr)) {
+ LOCAL->sensitive = T; /* hide this command */
+ if (pop3_send (stream,"PASS",pwd)) ret = LONGT;
+ LOCAL->sensitive=NIL; /* unhide */
+ }
+ if (!ret) { /* failure */
+ mm_log (LOCAL->reply,WARN);
+ if (trial == pop3_maxlogintrials)
+ mm_log ("Too many login failures",ERROR);
+ }
+ }
+ /* user refused to give password */
+ else mm_log ("Login aborted",ERROR);
+ } while (!ret && pwd[0] && (trial < pop3_maxlogintrials) &&
+ LOCAL->netstream);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ /* get capabilities if logged in */
+ if (ret && capaok) pop3_capa (stream,flags);
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *pop3_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ if (stream && LOCAL->response &&
+ (*LOCAL->response == '+') && (LOCAL->response[1] == ' ') &&
+ !(ret = rfc822_base64 ((unsigned char *) LOCAL->reply,
+ strlen (LOCAL->reply),len))) {
+ sprintf (tmp,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL->reply);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T if successful, else NIL
+ */
+
+long pop3_response (void *s,char *response,unsigned long size)
+{
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ unsigned long i,j,ret;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string for mm_dlog() */
+ if (stream->debug) mail_dlog (t,LOCAL->sensitive);
+ /* append CRLF */
+ *u++ = '\015'; *u++ = '\012'; *u = '\0';
+ ret = net_sout (LOCAL->netstream,t,u - t);
+ fs_give ((void **) &t);
+ }
+ else ret = net_sout (LOCAL->netstream,"\015\012",2);
+ }
+ else { /* abort requested */
+ ret = net_sout (LOCAL->netstream,"*\015\012",3);
+ LOCAL->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ pop3_reply (stream); /* get response */
+ return ret;
+}
+
+/* POP3 mail close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void pop3_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->netstream) { /* close POP3 connection */
+ stream->silent = T;
+ if (options & CL_EXPUNGE) pop3_expunge (stream,NIL,NIL);
+ stream->silent = silent;
+ pop3_send (stream,"QUIT",NIL);
+ mm_notify (stream,LOCAL->reply,BYE);
+ }
+ /* close POP3 connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ /* clean up */
+ if (LOCAL->cap.implementation)
+ fs_give ((void **) &LOCAL->cap.implementation);
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* POP3 mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * This is ugly and slow
+ */
+
+void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* get sequence */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) {
+ ENVELOPE **env = NIL;
+ ENVELOPE *e = NIL;
+ if (!stream->scache) env = &elt->private.msg.env;
+ else if (stream->msgno == i) env = &stream->env;
+ else env = &e;
+ if (!*env || !elt->rfc822_size) {
+ STRING bs;
+ unsigned long hs;
+ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
+ /* need to make an envelope? */
+ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
+ stream->dtb->flags);
+ /* need message size too, ugh */
+ if (!elt->rfc822_size) {
+ (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
+ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date)
+ mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ mail_free_envelope (&e);
+ }
+}
+
+/* POP3 fetch header as text
+ * Accepts: mail stream
+ * message number
+ * pointer to return size
+ * flags
+ * Returns: header text
+ */
+
+char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ FILE *f = NIL;
+ *size = 0; /* initially no header size */
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
+ /* have header text already? */
+ if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
+ /* if have CAPA and TOP, assume good TOP */
+ if (!LOCAL->loser && LOCAL->cap.top) {
+ sprintf (tmp,"TOP %lu 0",mail_uid (stream,msgno));
+ if (pop3_send (stream,tmp,NIL))
+ f = netmsg_slurp (LOCAL->netstream,&i,
+ &elt->private.msg.header.text.size);
+ }
+ /* otherwise load the cache with the message */
+ else if (elt->private.msg.header.text.size = pop3_cache (stream,elt))
+ f = LOCAL->txt;
+ if (f) { /* got it, make sure at start of file */
+ fseek (f,(unsigned long) 0,SEEK_SET);
+ /* read header from the cache */
+ fread (elt->private.msg.header.text.data = (unsigned char *)
+ fs_get ((size_t) elt->private.msg.header.text.size + 1),
+ (size_t) 1,(size_t) elt->private.msg.header.text.size,f);
+ /* tie off header text */
+ elt->private.msg.header.text.data[elt->private.msg.header.text.size] =
+ '\0';
+ /* close if not the cache */
+ if (f != LOCAL->txt) fclose (f);
+ }
+ }
+ /* return size of text */
+ if (size) *size = elt->private.msg.header.text.size;
+ return elt->private.msg.header.text.data ?
+ (char *) elt->private.msg.header.text.data : "";
+}
+
+/* POP3 fetch body
+ * Accepts: mail stream
+ * message number
+ * pointer to stringstruct to initialize
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ INIT (bs,mail_string,(void *) "",0);
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
+ elt = mail_elt (stream,msgno);
+ pop3_cache (stream,elt); /* make sure cache loaded */
+ if (!LOCAL->txt) return NIL; /* error if don't have a file */
+ if (!(flags & FT_PEEK)) { /* mark seen if needed */
+ elt->seen = T;
+ mm_flags (stream,elt->msgno);
+ }
+ INIT (bs,file_string,(void *) LOCAL->txt,elt->rfc822_size);
+ SETPOS (bs,LOCAL->hdrsize); /* skip past header */
+ return T;
+}
+
+/* POP3 cache message
+ * Accepts: mail stream
+ * message number
+ * Returns: header size
+ */
+
+unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* already cached? */
+ if (LOCAL->cached != mail_uid (stream,elt->msgno)) {
+ /* no, close current file */
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ LOCAL->cached = LOCAL->hdrsize = 0;
+ if (pop3_send_num (stream,"RETR",elt->msgno) &&
+ (LOCAL->txt = netmsg_slurp (LOCAL->netstream,&elt->rfc822_size,
+ &LOCAL->hdrsize)))
+ /* set as current message number */
+ LOCAL->cached = mail_uid (stream,elt->msgno);
+ else elt->deleted = T;
+ }
+ return LOCAL->hdrsize;
+}
+
+/* POP3 mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long pop3_ping (MAILSTREAM *stream)
+{
+ return pop3_send (stream,"NOOP",NIL);
+}
+
+
+/* POP3 mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void pop3_check (MAILSTREAM *stream)
+{
+ if (pop3_ping (stream)) mm_log ("Check completed",NIL);
+}
+
+
+/* POP3 mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long pop3_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ unsigned long i = 1,n = 0;
+ long ret;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) :
+ LONGT) { /* build selected sequence if needed */
+ while (i <= stream->nmsgs) {
+ elt = mail_elt (stream,i);
+ if (elt->deleted && (sequence ? elt->sequence : T) &&
+ pop3_send_num (stream,"DELE",i)) {
+ /* expunging currently cached message? */
+ if (LOCAL->cached == mail_uid (stream,i)) {
+ /* yes, close current file */
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ LOCAL->cached = LOCAL->hdrsize = 0;
+ }
+ mail_expunged (stream,i);
+ n++;
+ }
+ else i++; /* try next message */
+ }
+ if (!stream->silent) { /* only if not silent */
+ if (n) { /* did we expunge anything? */
+ sprintf (tmp,"Expunged %lu messages",n);
+ mm_log (tmp,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ }
+ return ret;
+}
+
+/* POP3 mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for POP3",ERROR);
+ return NIL;
+}
+
+
+/* POP3 mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for POP3",ERROR);
+ return NIL;
+}
+
+/* Internal routines */
+
+
+/* Post Office Protocol 3 send command with number argument
+ * Accepts: MAIL stream
+ * command
+ * number
+ * Returns: T if successful, NIL if failure
+ */
+
+long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%lu",mail_uid (stream,n));
+ return pop3_send (stream,command,tmp);
+}
+
+
+/* Post Office Protocol 3 send command
+ * Accepts: MAIL stream
+ * command
+ * command argument
+ * Returns: T if successful, NIL if failure
+ */
+
+long pop3_send (MAILSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1: 0)
+ + 3);
+ mail_lock (stream); /* lock up the stream */
+ if (!LOCAL->netstream) ret = pop3_fake (stream,"POP3 connection lost");
+ else { /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,LOCAL->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ ret = net_soutr (LOCAL->netstream,s) ? pop3_reply (stream) :
+ pop3_fake (stream,"POP3 connection broken in command");
+ }
+ fs_give ((void **) &s);
+ mail_unlock (stream); /* unlock stream */
+ return ret;
+}
+
+/* Post Office Protocol 3 get reply
+ * Accepts: MAIL stream
+ * Returns: T if success reply, NIL if error reply
+ */
+
+long pop3_reply (MAILSTREAM *stream)
+{
+ char *s;
+ /* flush old reply */
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ /* get reply */
+ if (!(LOCAL->response = net_getline (LOCAL->netstream)))
+ return pop3_fake (stream,"POP3 connection broken in response");
+ if (stream->debug) mm_dlog (LOCAL->response);
+ LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
+ /* return success or failure */
+ return (*LOCAL->response =='+') ? T : NIL;
+}
+
+
+/* Post Office Protocol 3 set fake error
+ * Accepts: MAIL stream
+ * error text
+ * Returns: NIL, always
+ */
+
+long pop3_fake (MAILSTREAM *stream,char *text)
+{
+ mm_notify (stream,text,BYE); /* send bye alert */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL; /* farewell, dear TCP stream */
+ /* flush any old reply */
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ LOCAL->reply = text; /* set up pseudo-reply string */
+ return NIL; /* return error code */
+}
diff --git a/imap/src/c-client/rfc822.c b/imap/src/c-client/rfc822.c
new file mode 100644
index 00000000..d9239646
--- /dev/null
+++ b/imap/src/c-client/rfc822.c
@@ -0,0 +1,2373 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: RFC 2822 and MIME routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 14 May 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+
+
+/* Support for deprecated features in earlier specifications. Note that this
+ * module follows RFC 2822, and all use of "rfc822" in function names is
+ * for compatibility. Only the code identified by the conditionals below
+ * follows the earlier documents.
+ */
+
+#define RFC733 1 /* parse "at" */
+#define RFC822 0 /* generate A-D-L (MUST be 0 for 2822) */
+
+/* RFC-822 static data */
+
+#define RFC822CONT " " /* RFC 2822 continuation */
+
+ /* should have been "Remailed-" */
+#define RESENTPREFIX "ReSent-"
+static char *resentprefix = RESENTPREFIX;
+ /* syntax error host string */
+static const char *errhst = ERRHOST;
+
+
+/* Body formats constant strings, must match definitions in mail.h */
+
+char *body_types[TYPEMAX+1] = {
+ "TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO",
+ "MODEL", "X-UNKNOWN"
+};
+
+
+char *body_encodings[ENCMAX+1] = {
+ "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "X-UNKNOWN"
+};
+
+
+/* Token delimiting special characters */
+
+ /* RFC 2822 specials */
+const char *specials = " ()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2822 phrase specials (no space) */
+const char *rspecials = "()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2822 dot-atom specials (no dot) */
+const char *wspecials = " ()<>@,;:\\\"[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2045 MIME body token specials */
+const char *tspecials = " ()<>@,;:\\\"[]/?=\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+
+/* Subtype defaulting (a no-no, but regretably necessary...)
+ * Accepts: type code
+ * Returns: default subtype name
+ */
+
+char *rfc822_default_subtype (unsigned short type)
+{
+ switch (type) {
+ case TYPETEXT: /* default is TEXT/PLAIN */
+ return "PLAIN";
+ case TYPEMULTIPART: /* default is MULTIPART/MIXED */
+ return "MIXED";
+ case TYPEMESSAGE: /* default is MESSAGE/RFC822 */
+ return "RFC822";
+ case TYPEAPPLICATION: /* default is APPLICATION/OCTET-STREAM */
+ return "OCTET-STREAM";
+ case TYPEAUDIO: /* default is AUDIO/BASIC */
+ return "BASIC";
+ default: /* others have no default subtype */
+ return "UNKNOWN";
+ }
+}
+
+/* RFC 2822 parsing routines */
+
+
+/* Parse an RFC 2822 message
+ * Accepts: pointer to return envelope
+ * pointer to return body
+ * pointer to header
+ * header byte count
+ * pointer to body stringstruct
+ * pointer to local host name
+ * recursion depth
+ * source driver flags
+ */
+
+void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
+ STRING *bs,char *host,unsigned long depth,
+ unsigned long flags)
+{
+ char c,*t,*d;
+ char *tmp = (char *) fs_get ((size_t) i + 100);
+ ENVELOPE *env = (*en = mail_newenvelope ());
+ BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL;
+ long MIMEp = -1; /* flag that MIME semantics are in effect */
+ long PathP = NIL; /* flag that a Path: was seen */
+ parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL);
+ if (!host) host = BADHOST; /* make sure that host is non-null */
+ while (i && *s != '\n') { /* until end of header */
+ t = tmp; /* initialize buffer pointer */
+ c = ' '; /* and previous character */
+ while (i && c) { /* collect text until logical end of line */
+ switch (c = *s++) { /* slurp a character */
+ case '\015': /* return, possible end of logical line */
+ if (*s == '\n') break; /* ignore if LF follows */
+ case '\012': /* LF, possible end of logical line */
+ /* tie off unless next line starts with WS */
+ if (*s != ' ' && *s != '\t') *t++ = c = '\0';
+ break;
+ case '\t': /* tab */
+ *t++ = ' '; /* coerce to space */
+ break;
+ default: /* all other characters */
+ *t++ = c; /* insert the character into the line */
+ break;
+ }
+ if (!--i) *t++ = '\0'; /* see if end of header */
+ }
+
+ /* find header item type */
+ if (t = d = strchr (tmp,':')) {
+ *d++ = '\0'; /* tie off header item, point at its data */
+ while (*d == ' ') d++; /* flush whitespace */
+ while ((tmp < t--) && (*t == ' ')) *t = '\0';
+ ucase (tmp); /* coerce to uppercase */
+ /* external callback */
+ if (pl) (*pl) (env,tmp,d,host);
+ switch (*tmp) { /* dispatch based on first character */
+ case '>': /* possible >From: */
+ if (!strcmp (tmp+1,"FROM")) rfc822_parse_adrlist (&env->from,d,host);
+ break;
+ case 'B': /* possible bcc: */
+ if (!strcmp (tmp+1,"CC")) rfc822_parse_adrlist (&env->bcc,d,host);
+ break;
+ case 'C': /* possible cc: or Content-<mumble>*/
+ if (!strcmp (tmp+1,"C")) rfc822_parse_adrlist (&env->cc,d,host);
+ else if ((tmp[1] == 'O') && (tmp[2] == 'N') && (tmp[3] == 'T') &&
+ (tmp[4] == 'E') && (tmp[5] == 'N') && (tmp[6] == 'T') &&
+ (tmp[7] == '-') && body)
+ switch (MIMEp) {
+ case -1: /* unknown if MIME or not */
+ if (!(MIMEp = /* see if MIME-Version header exists */
+ search ((unsigned char *) s-1,i,
+ (unsigned char *)"\012MIME-Version",(long) 13))) {
+#if 1
+ /* This is a disgusting kludge, and most of the messages which
+ * benefit from it are spam.
+ */
+ if (!strcmp (tmp+8,"TRANSFER-ENCODING") ||
+ (!strcmp (tmp+8,"TYPE") && strchr (d,'/'))) {
+ MM_LOG ("Warning: MIME header encountered in non-MIME message",
+ PARSE);
+ MIMEp = 1; /* declare MIME now */
+ }
+ else
+#endif
+ break; /* non-MIME message */
+ }
+ case T: /* definitely MIME */
+ rfc822_parse_content_header (body,tmp+8,d);
+ }
+ break;
+ case 'D': /* possible Date: */
+ if (!env->date && !strcmp (tmp+1,"ATE")) env->date = cpystr (d);
+ break;
+ case 'F': /* possible From: */
+ if (!strcmp (tmp+1,"ROM")) rfc822_parse_adrlist (&env->from,d,host);
+ else if (!strcmp (tmp+1,"OLLOWUP-TO")) {
+ t = env->followup_to = (char *) fs_get (1 + strlen (d));
+ while (c = *d++) if (c != ' ') *t++ = c;
+ *t++ = '\0';
+ }
+ break;
+ case 'I': /* possible In-Reply-To: */
+ if (!env->in_reply_to && !strcmp (tmp+1,"N-REPLY-TO"))
+ env->in_reply_to = cpystr (d);
+ break;
+
+ case 'M': /* possible Message-ID: or MIME-Version: */
+ if (!env->message_id && !strcmp (tmp+1,"ESSAGE-ID"))
+ env->message_id = cpystr (d);
+ else if (!strcmp (tmp+1,"IME-VERSION")) {
+ /* tie off at end of phrase */
+ if (t = rfc822_parse_phrase (d)) *t = '\0';
+ rfc822_skipws (&d); /* skip whitespace */
+ /* known version? */
+ if (strcmp (d,"1.0") && strcmp (d,"RFC-XXXX"))
+ MM_LOG ("Warning: message has unknown MIME version",PARSE);
+ MIMEp = T; /* note that we are MIME */
+ }
+ break;
+ case 'N': /* possible Newsgroups: */
+ if (!env->newsgroups && !strcmp (tmp+1,"EWSGROUPS")) {
+ t = env->newsgroups = (char *) fs_get (1 + strlen (d));
+ while (c = *d++) if (c != ' ') *t++ = c;
+ *t++ = '\0';
+ }
+ break;
+ case 'P': /* possible Path: */
+ if (!strcmp (tmp+1,"ATH")) env->ngpathexists = T;
+ break;
+ case 'R': /* possible Reply-To: */
+ if (!strcmp (tmp+1,"EPLY-TO"))
+ rfc822_parse_adrlist (&env->reply_to,d,host);
+ else if (!env->references && !strcmp (tmp+1,"EFERENCES"))
+ env->references = cpystr (d);
+ break;
+ case 'S': /* possible Subject: or Sender: */
+ if (!env->subject && !strcmp (tmp+1,"UBJECT"))
+ env->subject = cpystr (d);
+ else if (!strcmp (tmp+1,"ENDER"))
+ rfc822_parse_adrlist (&env->sender,d,host);
+ break;
+ case 'T': /* possible To: */
+ if (!strcmp (tmp+1,"O")) rfc822_parse_adrlist (&env->to,d,host);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ fs_give ((void **) &tmp); /* done with scratch buffer */
+ /* default Sender: and Reply-To: to From: */
+ if (!env->sender) env->sender = rfc822_cpy_adr (env->from);
+ if (!env->reply_to) env->reply_to = rfc822_cpy_adr (env->from);
+ /* now parse the body */
+ if (body) rfc822_parse_content (body,bs,host,depth,flags);
+}
+
+/* Parse a message body content
+ * Accepts: pointer to body structure
+ * body string
+ * pointer to local host name
+ * recursion depth
+ * source driver flags
+ */
+
+void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth,
+ unsigned long flags)
+{
+ char c,c1,*s,*s1;
+ int f;
+ unsigned long i,j,k,m;
+ PARAMETER *param;
+ PART *part = NIL;
+ if (depth > MAXMIMEDEPTH) { /* excessively deep recursion? */
+ body->type = TYPETEXT; /* yes, probably a malicious MIMEgram */
+ MM_LOG ("Ignoring excessively deep MIME recursion",PARSE);
+ }
+ if (!body->subtype) /* default subtype if still unknown */
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ /* note offset and sizes */
+ body->contents.offset = GETPOS (bs);
+ /* note internal body size in all cases */
+ body->size.bytes = body->contents.text.size = i = SIZE (bs);
+ if (!(flags & DR_CRLF)) body->size.bytes = strcrlflen (bs);
+ switch (body->type) { /* see if anything else special to do */
+ case TYPETEXT: /* text content */
+ if (!body->parameter) { /* no parameters set */
+ body->parameter = mail_newbody_parameter ();
+ body->parameter->attribute = cpystr ("CHARSET");
+ while (i--) { /* count lines and guess charset */
+ c = SNX (bs); /* get current character */
+ /* charset still unknown? */
+ if (!body->parameter->value) {
+ if ((c == I2C_ESC) && (i && i--) && ((c = SNX (bs)) == I2C_MULTI) &&
+ (i && i--) && (((c = SNX (bs)) == I2CS_94x94_JIS_NEW) ||
+ (c == I2CS_94x94_JIS_OLD)))
+ body->parameter->value = cpystr ("ISO-2022-JP");
+ else if (c & 0x80) body->parameter->value = cpystr ("X-UNKNOWN");
+ }
+ if (c == '\n') body->size.lines++;
+ }
+ /* 7-bit content */
+ if (!body->parameter->value) switch (body->encoding) {
+ case ENC7BIT: /* unadorned 7-bit */
+ case ENC8BIT: /* unadorned 8-bit (but 7-bit content) */
+ case ENCBINARY: /* binary (but 7-bit content( */
+ body->parameter->value = cpystr ("US-ASCII");
+ break;
+ default: /* QUOTED-PRINTABLE, BASE64, etc. */
+ body->parameter->value = cpystr ("X-UNKNOWN");
+ break;
+ }
+ }
+ /* just count lines */
+ else while (i--) if ((SNX (bs)) == '\n') body->size.lines++;
+ break;
+
+ case TYPEMESSAGE: /* encapsulated message */
+ /* encapsulated RFC-822 message? */
+ if (!strcmp (body->subtype,"RFC822")) {
+ body->nested.msg = mail_newmsg ();
+ switch (body->encoding) { /* make sure valid encoding */
+ case ENC7BIT: /* these are valid nested encodings */
+ case ENC8BIT:
+ case ENCBINARY:
+ break;
+ default:
+ MM_LOG ("Ignoring nested encoding of message contents",PARSE);
+ }
+ /* hunt for blank line */
+ for (c = '\012',j = 0; (i > j) && ((c != '\012') || (CHR(bs) != '\012'));
+ j++) if ((c1 = SNX (bs)) != '\015') c = c1;
+ if (i > j) { /* unless no more text */
+ c1 = SNX (bs); /* body starts here */
+ j++; /* advance count */
+ }
+ /* note body text offset and header size */
+ body->nested.msg->header.text.size = j;
+ body->nested.msg->text.text.size = body->contents.text.size - j;
+ body->nested.msg->text.offset = GETPOS (bs);
+ body->nested.msg->full.offset = body->nested.msg->header.offset =
+ body->contents.offset;
+ body->nested.msg->full.text.size = body->contents.text.size;
+ /* copy header string */
+ SETPOS (bs,body->contents.offset);
+ s = (char *) fs_get ((size_t) j + 1);
+ for (s1 = s,k = j; k--; *s1++ = SNX (bs));
+ s[j] = '\0'; /* tie off string (not really necessary) */
+ /* now parse the body */
+ rfc822_parse_msg_full (&body->nested.msg->env,&body->nested.msg->body,s,
+ j,bs,h,depth+1,flags);
+ fs_give ((void **) &s); /* free header string */
+ /* restore position */
+ SETPOS (bs,body->contents.offset);
+ }
+ /* count number of lines */
+ while (i--) if (SNX (bs) == '\n') body->size.lines++;
+ break;
+ case TYPEMULTIPART: /* multiple parts */
+ switch (body->encoding) { /* make sure valid encoding */
+ case ENC7BIT: /* these are valid nested encodings */
+ case ENC8BIT:
+ case ENCBINARY:
+ break;
+ default:
+ MM_LOG ("Ignoring nested encoding of multipart contents",PARSE);
+ }
+ /* remember if digest */
+ f = !strcmp (body->subtype,"DIGEST");
+ /* find cookie */
+ for (s1 = NIL,param = body->parameter; param && !s1; param = param->next)
+ if (!strcmp (param->attribute,"BOUNDARY")) s1 = param->value;
+ if (!s1) s1 = "-"; /* yucky default */
+ j = strlen (s1) + 2; /* length of cookie and header */
+ c = '\012'; /* initially at beginning of line */
+
+ while (i > j) { /* examine data */
+ if (m = GETPOS (bs)) m--; /* get position in front of character */
+ switch (c) { /* examine each line */
+ case '\015': /* handle CRLF form */
+ if (CHR (bs) == '\012'){/* following LF? */
+ c = SNX (bs); i--; /* yes, slurp it */
+ }
+ case '\012': /* at start of a line, start with -- ? */
+ if (!(i && i-- && ((c = SNX (bs)) == '-') && i-- &&
+ ((c = SNX (bs)) == '-'))) break;
+ /* see if cookie matches */
+ if (k = j - 2) for (s = s1; i-- && *s++ == (c = SNX (bs)) && --k;);
+ if (k) break; /* strings didn't match if non-zero */
+ /* terminating delimiter? */
+ if ((c = ((i && i--) ? (SNX (bs)) : '\012')) == '-') {
+ if ((i && i--) && ((c = SNX (bs)) == '-') &&
+ ((i && i--) ? (((c = SNX (bs)) == '\015') || (c=='\012')):T)) {
+ /* if have a final part calculate its size */
+ if (part) part->body.mime.text.size =
+ (m > part->body.mime.offset) ? (m - part->body.mime.offset) :0;
+ part = NIL; i = 1; /* terminate scan */
+ }
+ break;
+ }
+ /* swallow trailing whitespace */
+ while ((c == ' ') || (c == '\t'))
+ c = ((i && i--) ? (SNX (bs)) : '\012');
+ switch (c) { /* need newline after all of it */
+ case '\015': /* handle CRLF form */
+ if (i && CHR (bs) == '\012') {
+ c = SNX (bs); i--;/* yes, slurp it */
+ }
+ case '\012': /* new line */
+ if (part) { /* calculate size of previous */
+ part->body.mime.text.size =
+ (m > part->body.mime.offset) ? (m-part->body.mime.offset) : 0;
+ /* instantiate next */
+ part = part->next = mail_newbody_part ();
+ } /* otherwise start new list */
+ else part = body->nested.part = mail_newbody_part ();
+ /* digest has a different default */
+ if (f) part->body.type = TYPEMESSAGE;
+ /* note offset from main body */
+ part->body.mime.offset = GETPOS (bs);
+ break;
+ default: /* whatever it was it wasn't valid */
+ break;
+ }
+ break;
+ default: /* not at a line */
+ c = SNX (bs); i--; /* get next character */
+ break;
+ }
+ }
+
+ /* calculate size of any final part */
+ if (part) part->body.mime.text.size = i +
+ ((GETPOS(bs) > part->body.mime.offset) ?
+ (GETPOS(bs) - part->body.mime.offset) : 0);
+ /* make a scratch buffer */
+ s1 = (char *) fs_get ((size_t) (k = MAILTMPLEN));
+ /* in case empty multipart */
+ if (!body->nested.part) body->nested.part = mail_newbody_part ();
+ /* parse non-empty body parts */
+ for (part = body->nested.part; part; part = part->next) {
+ /* part non-empty (header and/or content)? */
+ if (i = part->body.mime.text.size) {
+ /* move to that part of the body */
+ SETPOS (bs,part->body.mime.offset);
+ /* until end of header */
+ while (i && ((c = CHR (bs)) != '\015') && (c != '\012')) {
+ /* collect text until logical end of line */
+ for (j = 0,c = ' '; c; ) {
+ /* make sure buffer big enough */
+ if (j > (k - 10)) fs_resize ((void **) &s1,k += MAILTMPLEN);
+ switch (c1 = SNX (bs)) {
+ case '\015': /* return */
+ if (i && (CHR (bs) == '\012')) {
+ c1 = SNX (bs); /* eat any LF following */
+ i--;
+ }
+ case '\012': /* newline, possible end of logical line */
+ /* tie off unless continuation */
+ if (!i || ((CHR (bs) != ' ') && (CHR (bs) != '\t')))
+ s1[j] = c = '\0';
+ break;
+ case '\t': /* tab */
+ case ' ': /* insert whitespace if not already there */
+ if (c != ' ') s1[j++] = c = ' ';
+ break;
+ default: /* all other characters */
+ s1[j++] = c = c1; /* insert the character into the line */
+ break;
+ }
+ /* end of data ties off the header */
+ if (!i || !--i) s1[j++] = c = '\0';
+ }
+
+ /* find header item type */
+ if (((s1[0] == 'C') || (s1[0] == 'c')) &&
+ ((s1[1] == 'O') || (s1[1] == 'o')) &&
+ ((s1[2] == 'N') || (s1[2] == 'n')) &&
+ ((s1[3] == 'T') || (s1[3] == 't')) &&
+ ((s1[4] == 'E') || (s1[4] == 'e')) &&
+ ((s1[5] == 'N') || (s1[5] == 'n')) &&
+ ((s1[6] == 'T') || (s1[6] == 't')) &&
+ (s1[7] == '-') && (s = strchr (s1+8,':'))) {
+ /* tie off and flush whitespace */
+ for (*s++ = '\0'; *s == ' '; s++);
+ /* parse the header */
+ rfc822_parse_content_header (&part->body,ucase (s1+8),s);
+ }
+ }
+ /* skip header trailing (CR)LF */
+ if (i && (CHR (bs) =='\015')) {i--; c1 = SNX (bs);}
+ if (i && (CHR (bs) =='\012')) {i--; c1 = SNX (bs);}
+ j = bs->size; /* save upper level size */
+ /* set offset for next level, fake size to i */
+ bs->size = GETPOS (bs) + i;
+ part->body.mime.text.size -= i;
+ /* now parse it */
+ rfc822_parse_content (&part->body,bs,h,depth+1,flags);
+ bs->size = j; /* restore current level size */
+ }
+ else { /* zero-length part, use default subtype */
+ part->body.subtype = cpystr (rfc822_default_subtype (part->body.type));
+ /* see if anything else special to do */
+ switch (part->body.type) {
+ case TYPETEXT: /* text content */
+ /* default parameters */
+ if (!part->body.parameter) {
+ part->body.parameter = mail_newbody_parameter ();
+ part->body.parameter->attribute = cpystr ("CHARSET");
+ /* only assume US-ASCII if 7BIT */
+ part->body.parameter->value =
+ cpystr ((part->body.encoding == ENC7BIT) ?
+ "US-ASCII" : "X-UNKNOWN");
+ }
+ break;
+ case TYPEMESSAGE: /* encapsulated message in digest */
+ part->body.nested.msg = mail_newmsg ();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ fs_give ((void **) &s1); /* finished with scratch buffer */
+ break;
+ default: /* nothing special to do in any other case */
+ break;
+ }
+}
+
+/* Parse RFC 2822 body content header
+ * Accepts: body to write to
+ * possible content name
+ * remainder of header
+ */
+
+void rfc822_parse_content_header (BODY *body,char *name,char *s)
+{
+ char c,*t,tmp[MAILTMPLEN];
+ long i;
+ STRINGLIST *stl;
+ rfc822_skipws (&s); /* skip leading comments */
+ /* flush whitespace */
+ if (t = strchr (name,' ')) *t = '\0';
+ switch (*name) { /* see what kind of content */
+ case 'I': /* possible Content-ID */
+ if (!(strcmp (name+1,"D") || body->id)) body->id = cpystr (s);
+ break;
+ case 'D': /* possible Content-Description */
+ if (!(strcmp (name+1,"ESCRIPTION") || body->description))
+ body->description = cpystr (s);
+ if (!(strcmp (name+1,"ISPOSITION") || body->disposition.type)) {
+ /* get type word */
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off type */
+ body->disposition.type = ucase (cpystr (s));
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ rfc822_parse_parameter (&body->disposition.parameter,name);
+ }
+ break;
+ case 'L': /* possible Content-Language */
+ if (!(strcmp (name+1,"ANGUAGE") || body->language)) {
+ stl = NIL; /* process languages */
+ while (s && (name = rfc822_parse_word (s,tspecials))) {
+ c = *name; /* save delimiter */
+ *name = '\0'; /* tie off subtype */
+ if (stl) stl = stl->next = mail_newstringlist ();
+ else stl = body->language = mail_newstringlist ();
+ stl->text.data = (unsigned char *) ucase (cpystr (s));
+ stl->text.size = strlen ((char *) stl->text.data);
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ if (*name == ',') { /* any more languages? */
+ s = ++name; /* advance to it them */
+ rfc822_skipws (&s);
+ }
+ else s = NIL; /* bogus or end of list */
+ }
+ }
+ else if (!(strcmp (name+1,"OCATION") || body->location))
+ body->location = cpystr (s);
+ break;
+ case 'M': /* possible Content-MD5 */
+ if (!(strcmp (name+1,"D5") || body->md5)) body->md5 = cpystr (s);
+ break;
+
+ case 'T': /* possible Content-Type/Transfer-Encoding */
+ if (!(strcmp (name+1,"YPE") || body->subtype || body->parameter)) {
+ /* get type word */
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off type */
+ /* search for body type */
+ for (i = 0,s = rfc822_cpy (s);
+ (i <= TYPEMAX) && body_types[i] &&
+ compare_cstring (s,body_types[i]); i++);
+ if (i > TYPEMAX) { /* fell off end of loop? */
+ body->type = TYPEOTHER; /* coerce to X-UNKNOWN */
+ sprintf (tmp,"MIME type table overflow: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ else { /* record body type index */
+ body->type = (unsigned short) i;
+ /* and name if new type */
+ if (body_types[body->type]) fs_give ((void **) &s);
+ else { /* major MIME body type unknown to us */
+ body_types[body->type] = ucase (s);
+ sprintf (tmp,"Unknown MIME type: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ if ((*name == '/') && /* subtype? */
+ (name = rfc822_parse_word ((s = ++name),tspecials))) {
+ c = *name; /* save delimiter */
+ *name = '\0'; /* tie off subtype */
+ rfc822_skipws (&s); /* copy subtype */
+ if (s) body->subtype = ucase (rfc822_cpy (s));
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ }
+ else if (!name) { /* no subtype, was a subtype delimiter? */
+ name = s; /* barf, restore pointer */
+ rfc822_skipws (&name); /* skip leading whitespace */
+ }
+ rfc822_parse_parameter (&body->parameter,name);
+ }
+
+ else if (!strcmp (name+1,"RANSFER-ENCODING")) {
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off encoding */
+ /* search for body encoding */
+ for (i = 0,s = rfc822_cpy (s);
+ (i <= ENCMAX) && body_encodings[i] &&
+ compare_cstring (s,body_encodings[i]); i++);
+ if (i > ENCMAX) { /* fell off end of loop? */
+ body->encoding = ENCOTHER;
+ sprintf (tmp,"MIME encoding table overflow: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ else { /* record body encoding index */
+ body->encoding = (unsigned short) i;
+ /* and name if new encoding */
+ if (body_encodings[body->encoding]) fs_give ((void **) &s);
+ else {
+ body_encodings[body->encoding] = ucase (s);
+ sprintf (tmp,"Unknown MIME transfer encoding: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ *name = c; /* restore delimiter */
+ /* ??check for cruft here?? */
+ }
+ break;
+ default: /* otherwise unknown */
+ break;
+ }
+}
+
+/* Parse RFC 2822 body parameter list
+ * Accepts: parameter list to write to
+ * text of list
+ */
+
+void rfc822_parse_parameter (PARAMETER **par,char *text)
+{
+ char c,*s,tmp[MAILTMPLEN];
+ PARAMETER *param = NIL;
+ /* parameter list? */
+ while (text && (*text == ';') &&
+ (text = rfc822_parse_word ((s = ++text),tspecials))) {
+ c = *text; /* remember delimiter */
+ *text = '\0'; /* tie off attribute name */
+ rfc822_skipws (&s); /* skip leading attribute whitespace */
+ if (!*s) *text = c; /* must have an attribute name */
+ else { /* instantiate a new parameter */
+ if (*par) param = param->next = mail_newbody_parameter ();
+ else param = *par = mail_newbody_parameter ();
+ param->attribute = ucase (cpystr (s));
+ *text = c; /* restore delimiter */
+ rfc822_skipws (&text); /* skip whitespace before equal sign */
+ if ((*text == '=') && /* make sure have value */
+ (text = rfc822_parse_word ((s = ++text),tspecials))) {
+ c = *text; /* remember delimiter */
+ *text = '\0'; /* tie off value */
+ rfc822_skipws (&s); /* skip leading value whitespace */
+ if (*s) param->value = rfc822_cpy (s);
+ *text = c; /* restore delimiter */
+ rfc822_skipws (&text);
+ }
+ if (!param->value) { /* value not found? */
+ param->value = cpystr ("MISSING_PARAMETER_VALUE");
+ sprintf (tmp,"Missing parameter value: %.80s",param->attribute);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ }
+ /* string not present */
+ if (!text) MM_LOG ("Missing parameter",PARSE);
+ else if (*text) { /* must be end of poop */
+ sprintf (tmp,"Unexpected characters at end of parameters: %.80s",text);
+ MM_LOG (tmp,PARSE);
+ }
+}
+
+/* Parse RFC 2822 address list
+ * Accepts: address list to write to
+ * input string
+ * default host name
+ */
+
+void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host)
+{
+ int c;
+ char *s,tmp[MAILTMPLEN];
+ ADDRESS *last = *lst;
+ ADDRESS *adr;
+ if (!string) return; /* no string */
+ rfc822_skipws (&string); /* skip leading WS */
+ if (!*string) return; /* empty string */
+ /* run to tail of list */
+ if (last) while (last->next) last = last->next;
+ while (string) { /* loop until string exhausted */
+ while (*string == ',') { /* RFC 822 allowed null addresses!! */
+ ++string; /* skip the comma */
+ rfc822_skipws (&string); /* and any leading WS */
+ }
+ if (!*string) string = NIL; /* punt if ran out of string */
+ /* got an address? */
+ else if (adr = rfc822_parse_address (lst,last,&string,host,0)) {
+ last = adr; /* new tail address */
+ if (string) { /* analyze what follows */
+ rfc822_skipws (&string);
+ switch (c = *(unsigned char *) string) {
+ case ',': /* comma? */
+ ++string; /* then another address follows */
+ break;
+ default:
+ s = isalnum (c) ? "Must use comma to separate addresses: %.80s" :
+ "Unexpected characters at end of address: %.80s";
+ sprintf (tmp,s,string);
+ MM_LOG (tmp,PARSE);
+ last = last->next = mail_newaddr ();
+ last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS");
+ last->host = cpystr (errhst);
+ /* falls through */
+ case '\0': /* null-specified address? */
+ string = NIL; /* punt remainder of parse */
+ break;
+ }
+ }
+ }
+ else if (string) { /* bad mailbox */
+ rfc822_skipws (&string); /* skip WS */
+ if (!*string) strcpy (tmp,"Missing address after comma");
+ else sprintf (tmp,"Invalid mailbox list: %.80s",string);
+ MM_LOG (tmp,PARSE);
+ string = NIL;
+ (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS");
+ adr->host = cpystr (errhst);
+ if (last) last = last->next = adr;
+ else *lst = last = adr;
+ break;
+ }
+ }
+}
+
+/* Parse RFC 2822 address
+ * Accepts: address list to write to
+ * tail of address list
+ * pointer to input string
+ * default host name
+ * group nesting depth
+ * Returns: new list tail
+ */
+
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth)
+{
+ ADDRESS *adr;
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* skip leading WS */
+ if (!**string) return NIL; /* empty string */
+ if (adr = rfc822_parse_group (lst,last,string,defaulthost,depth)) last = adr;
+ /* got an address? */
+ else if (adr = rfc822_parse_mailbox (string,defaulthost)) {
+ if (!*lst) *lst = adr; /* yes, first time through? */
+ else last->next = adr; /* no, append to the list */
+ /* set for subsequent linking */
+ for (last = adr; last->next; last = last->next);
+ }
+ else if (*string) return NIL;
+ return last;
+}
+
+/* Parse RFC 2822 group
+ * Accepts: address list to write to
+ * pointer to tail of address list
+ * pointer to input string
+ * default host name
+ * group nesting depth
+ */
+
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth)
+{
+ char tmp[MAILTMPLEN];
+ char *p,*s;
+ ADDRESS *adr;
+ if (depth > MAXGROUPDEPTH) { /* excessively deep recursion? */
+ MM_LOG ("Ignoring excessively deep group recursion",PARSE);
+ return NIL; /* probably abusive */
+ }
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* skip leading WS */
+ if (!**string || /* trailing whitespace or not group */
+ ((*(p = *string) != ':') && !(p = rfc822_parse_phrase (*string))))
+ return NIL;
+ s = p; /* end of candidate phrase */
+ rfc822_skipws (&s); /* find delimiter */
+ if (*s != ':') return NIL; /* not really a group */
+ *p = '\0'; /* tie off group name */
+ p = ++s; /* continue after the delimiter */
+ rfc822_skipws (&p); /* skip subsequent whitespace */
+ /* write as address */
+ (adr = mail_newaddr ())->mailbox = rfc822_cpy (*string);
+ if (!*lst) *lst = adr; /* first time through? */
+ else last->next = adr; /* no, append to the list */
+ last = adr; /* set for subsequent linking */
+ *string = p; /* continue after this point */
+ while (*string && **string && (**string != ';')) {
+ if (adr = rfc822_parse_address (lst,last,string,defaulthost,depth+1)) {
+ last = adr; /* new tail address */
+ if (*string) { /* anything more? */
+ rfc822_skipws (string); /* skip whitespace */
+ switch (**string) { /* see what follows */
+ case ',': /* another address? */
+ ++*string; /* yes, skip past the comma */
+ case ';': /* end of group? */
+ case '\0': /* end of string */
+ break;
+ default:
+ sprintf (tmp,"Unexpected characters after address in group: %.80s",
+ *string);
+ MM_LOG (tmp,PARSE);
+ *string = NIL; /* cancel remainder of parse */
+ last = last->next = mail_newaddr ();
+ last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS_IN_GROUP");
+ last->host = cpystr (errhst);
+ }
+ }
+ }
+ else { /* bogon */
+ sprintf (tmp,"Invalid group mailbox list: %.80s",*string);
+ MM_LOG (tmp,PARSE);
+ *string = NIL; /* cancel remainder of parse */
+ (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS_IN_GROUP");
+ adr->host = cpystr (errhst);
+ last = last->next = adr;
+ }
+ }
+ if (*string) { /* skip close delimiter */
+ if (**string == ';') ++*string;
+ rfc822_skipws (string);
+ }
+ /* append end of address mark to the list */
+ last->next = (adr = mail_newaddr ());
+ last = adr; /* set for subsequent linking */
+ return last; /* return the tail */
+}
+
+/* Parse RFC 2822 mailbox
+ * Accepts: pointer to string pointer
+ * default host
+ * Returns: address list
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost)
+{
+ ADDRESS *adr = NIL;
+ char *s,*end;
+ parsephrase_t pp = (parsephrase_t) mail_parameters (NIL,GET_PARSEPHRASE,NIL);
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* flush leading whitespace */
+ if (!**string) return NIL; /* empty string */
+ if (*(s = *string) == '<') /* note start, handle case of phraseless RA */
+ adr = rfc822_parse_routeaddr (s,string,defaulthost);
+ /* otherwise, expect at least one word */
+ else if (end = rfc822_parse_phrase (s)) {
+ if ((adr = rfc822_parse_routeaddr (end,string,defaulthost))) {
+ /* phrase is a personal name */
+ if (adr->personal) fs_give ((void **) &adr->personal);
+ *end = '\0'; /* tie off phrase */
+ adr->personal = rfc822_cpy (s);
+ }
+ /* call external phraseparser if phrase only */
+ else if (pp && rfc822_phraseonly (end) &&
+ (adr = (*pp) (s,end,defaulthost))) {
+ *string = end; /* update parse pointer */
+ rfc822_skipws (string); /* skip WS in the normal way */
+ }
+ else adr = rfc822_parse_addrspec (s,string,defaulthost);
+ }
+ return adr; /* return the address */
+}
+
+
+/* Check if address is a phrase only
+ * Accepts: pointer to end of phrase
+ * Returns: T if phrase only, else NIL;
+ */
+
+long rfc822_phraseonly (char *end)
+{
+ while (*end == ' ') ++end; /* call rfc822_skipws() instead?? */
+ switch (*end) {
+ case '\0': case ',': case ';':
+ return LONGT; /* is a phrase only */
+ }
+ return NIL; /* something other than phase is here */
+}
+
+/* Parse RFC 2822 route-address
+ * Accepts: string pointer
+ * pointer to string pointer to update
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost)
+{
+ char tmp[MAILTMPLEN];
+ ADDRESS *adr;
+ char *s,*t,*adl;
+ size_t adllen,i;
+ if (!string) return NIL;
+ rfc822_skipws (&string); /* flush leading whitespace */
+ /* must start with open broket */
+ if (*string != '<') return NIL;
+ t = ++string; /* see if A-D-L there */
+ rfc822_skipws (&t); /* flush leading whitespace */
+ for (adl = NIL,adllen = 0; /* parse possible A-D-L */
+ (*t == '@') && (s = rfc822_parse_domain (t+1,&t));) {
+ i = strlen (s) + 2; /* @ plus domain plus delimiter or NUL */
+ if (adl) { /* have existing A-D-L? */
+ fs_resize ((void **) &adl,adllen + i);
+ sprintf (adl + adllen - 1,",@%s",s);
+ }
+ /* write initial A-D-L */
+ else sprintf (adl = (char *) fs_get (i),"@%s",s);
+ adllen += i; /* new A-D-L length */
+ fs_give ((void **) &s); /* don't need domain any more */
+ rfc822_skipws (&t); /* skip WS */
+ if (*t != ',') break; /* put if not comma */
+ t++; /* skip the comma */
+ rfc822_skipws (&t); /* skip WS */
+ }
+ if (adl) { /* got an A-D-L? */
+ if (*t != ':') { /* make sure syntax good */
+ sprintf (tmp,"Unterminated at-domain-list: %.80s%.80s",adl,t);
+ MM_LOG (tmp,PARSE);
+ }
+ else string = ++t; /* continue parse from this point */
+ }
+
+ /* parse address spec */
+ if (!(adr = rfc822_parse_addrspec (string,ret,defaulthost))) {
+ if (adl) fs_give ((void **) &adl);
+ return NIL;
+ }
+ if (adl) adr->adl = adl; /* have an A-D-L? */
+ if (*ret) if (**ret == '>') { /* make sure terminated OK */
+ ++*ret; /* skip past the broket */
+ rfc822_skipws (ret); /* flush trailing WS */
+ if (!**ret) *ret = NIL; /* wipe pointer if at end of string */
+ return adr; /* return the address */
+ }
+ sprintf (tmp,"Unterminated mailbox: %.80s@%.80s",adr->mailbox,
+ *adr->host == '@' ? "<null>" : adr->host);
+ MM_LOG (tmp,PARSE);
+ adr->next = mail_newaddr ();
+ adr->next->mailbox = cpystr ("MISSING_MAILBOX_TERMINATOR");
+ adr->next->host = cpystr (errhst);
+ return adr; /* return the address */
+}
+
+/* Parse RFC 2822 address-spec
+ * Accepts: string pointer
+ * pointer to string pointer to update
+ * default host
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost)
+{
+ ADDRESS *adr;
+ char c,*s,*t,*v,*end;
+ if (!string) return NIL; /* no string */
+ rfc822_skipws (&string); /* flush leading whitespace */
+ if (!*string) return NIL; /* empty string */
+ /* find end of mailbox */
+ if (!(t = rfc822_parse_word (string,wspecials))) return NIL;
+ adr = mail_newaddr (); /* create address block */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off mailbox */
+ /* copy mailbox */
+ adr->mailbox = rfc822_cpy (string);
+ *t = c; /* restore delimiter */
+ end = t; /* remember end of mailbox */
+ rfc822_skipws (&t); /* skip whitespace */
+ while (*t == '.') { /* some cretin taking RFC 822 too seriously? */
+ string = ++t; /* skip past the dot and any WS */
+ rfc822_skipws (&string);
+ /* get next word of mailbox */
+ if (t = rfc822_parse_word (string,wspecials)) {
+ end = t; /* remember new end of mailbox */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off word */
+ s = rfc822_cpy (string); /* copy successor part */
+ *t = c; /* restore delimiter */
+ /* build new mailbox */
+ sprintf (v = (char *) fs_get (strlen (adr->mailbox) + strlen (s) + 2),
+ "%s.%s",adr->mailbox,s);
+ fs_give ((void **) &adr->mailbox);
+ adr->mailbox = v; /* new host name */
+ rfc822_skipws (&t); /* skip WS after mailbox */
+ }
+ else { /* barf */
+ MM_LOG ("Invalid mailbox part after .",PARSE);
+ break;
+ }
+ }
+ t = end; /* remember delimiter in case no host */
+
+ rfc822_skipws (&end); /* sniff ahead at what follows */
+#if RFC733 /* RFC 733 used "at" instead of "@" */
+ if (((*end == 'a') || (*end == 'A')) &&
+ ((end[1] == 't') || (end[1] == 'T')) &&
+ ((end[2] == ' ') || (end[2] == '\t') || (end[2] == '\015') ||
+ (end[2] == '\012') || (end[2] == '(')))
+ *++end = '@';
+#endif
+ if (*end != '@') end = t; /* host name missing */
+ /* otherwise parse host name */
+ else if (!(adr->host = rfc822_parse_domain (++end,&end)))
+ adr->host = cpystr (errhst);
+ /* default host if missing */
+ if (!adr->host) adr->host = cpystr (defaulthost);
+ /* try person name in comments if missing */
+ if (end && !(adr->personal && *adr->personal)) {
+ while (*end == ' ') ++end; /* see if we can find a person name here */
+ if ((*end == '(') && (s = rfc822_skip_comment (&end,LONGT)) && strlen (s))
+ adr->personal = rfc822_cpy (s);
+ rfc822_skipws (&end); /* skip any other WS in the normal way */
+ }
+ /* set return to end pointer */
+ *ret = (end && *end) ? end : NIL;
+ return adr; /* return the address we got */
+}
+
+/* Parse RFC 2822 domain
+ * Accepts: string pointer
+ * pointer to return end of domain
+ * Returns: domain name or NIL if failure
+ */
+
+char *rfc822_parse_domain (char *string,char **end)
+{
+ char *ret = NIL;
+ char c,*s,*t,*v;
+ rfc822_skipws (&string); /* skip whitespace */
+ if (*string == '[') { /* domain literal? */
+ if (!(*end = rfc822_parse_word (string + 1,"]\\")))
+ MM_LOG ("Empty domain literal",PARSE);
+ else if (**end != ']') MM_LOG ("Unterminated domain literal",PARSE);
+ else {
+ size_t len = ++*end - string;
+ strncpy (ret = (char *) fs_get (len + 1),string,len);
+ ret[len] = '\0'; /* tie off literal */
+ }
+ }
+ /* search for end of host */
+ else if (t = rfc822_parse_word (string,wspecials)) {
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off host */
+ ret = rfc822_cpy (string); /* copy host */
+ *t = c; /* restore delimiter */
+ *end = t; /* remember end of domain */
+ rfc822_skipws (&t); /* skip WS after host */
+ while (*t == '.') { /* some cretin taking RFC 822 too seriously? */
+ string = ++t; /* skip past the dot and any WS */
+ rfc822_skipws (&string);
+ if (string = rfc822_parse_domain (string,&t)) {
+ *end = t; /* remember new end of domain */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off host */
+ s = rfc822_cpy (string);/* copy successor part */
+ *t = c; /* restore delimiter */
+ /* build new domain */
+ sprintf (v = (char *) fs_get (strlen (ret) + strlen (s) + 2),
+ "%s.%s",ret,s);
+ fs_give ((void **) &ret);
+ ret = v; /* new host name */
+ rfc822_skipws (&t); /* skip WS after domain */
+ }
+ else { /* barf */
+ MM_LOG ("Invalid domain part after .",PARSE);
+ break;
+ }
+ }
+ }
+ else MM_LOG ("Missing or invalid host name after @",PARSE);
+ return ret;
+}
+
+/* Parse RFC 2822 phrase
+ * Accepts: string pointer
+ * Returns: pointer to end of phrase
+ */
+
+char *rfc822_parse_phrase (char *s)
+{
+ char *curpos;
+ if (!s) return NIL; /* no-op if no string */
+ /* find first word of phrase */
+ curpos = rfc822_parse_word (s,NIL);
+ if (!curpos) return NIL; /* no words means no phrase */
+ if (!*curpos) return curpos; /* check if string ends with word */
+ s = curpos; /* sniff past the end of this word and WS */
+ rfc822_skipws (&s); /* skip whitespace */
+ /* recurse to see if any more */
+ return (s = rfc822_parse_phrase (s)) ? s : curpos;
+}
+
+/* Parse RFC 2822 word
+ * Accepts: string pointer
+ * delimiter (or NIL for phrase word parsing)
+ * Returns: pointer to end of word
+ */
+
+char *rfc822_parse_word (char *s,const char *delimiters)
+{
+ char *st,*str;
+ if (!s) return NIL; /* no string */
+ rfc822_skipws (&s); /* flush leading whitespace */
+ if (!*s) return NIL; /* empty string */
+ str = s; /* hunt pointer for strpbrk */
+ while (T) { /* look for delimiter, return if none */
+ if (!(st = strpbrk (str,delimiters ? delimiters : wspecials)))
+ return str + strlen (str);
+ /* ESC in phrase */
+ if (!delimiters && (*st == I2C_ESC)) {
+ str = ++st; /* always skip past ESC */
+ switch (*st) { /* special hack for RFC 1468 (ISO-2022-JP) */
+ case I2C_MULTI: /* multi byte sequence */
+ switch (*++st) {
+ case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
+ case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
+ str = ++st; /* skip past the shift to JIS */
+ while (st = strchr (st,I2C_ESC))
+ if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
+ (st[1] == I2CS_94_JIS_ROMAN) ||
+ (st[1] == I2CS_94_JIS_BUGROM))) {
+ str = st += 2; /* skip past the shift back to ASCII */
+ break;
+ }
+ /* eats entire text if no shift back */
+ if (!st || !*st) return str + strlen (str);
+ }
+ break;
+ case I2C_G0_94: /* single byte sequence */
+ switch (st[1]) {
+ case I2CS_94_ASCII: /* shift to ASCII */
+ case I2CS_94_JIS_ROMAN: /* shift to JIS-Roman */
+ case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
+ str = st + 2; /* skip past the shift */
+ break;
+ }
+ }
+ }
+
+ else switch (*st) { /* dispatch based on delimiter */
+ case '"': /* quoted string */
+ /* look for close quote */
+ while (*++st != '"') switch (*st) {
+ case '\0': /* unbalanced quoted string */
+ return NIL; /* sick sick sick */
+ case '\\': /* quoted character */
+ if (!*++st) return NIL; /* skip the next character */
+ default: /* ordinary character */
+ break; /* no special action */
+ }
+ str = ++st; /* continue parse */
+ break;
+ case '\\': /* quoted character */
+ /* This is wrong; a quoted-pair can not be part of a word. However,
+ * domain-literal is parsed as a word and quoted-pairs can be used
+ * *there*. Either way, it's pretty pathological.
+ */
+ if (st[1]) { /* not on NUL though... */
+ str = st + 2; /* skip quoted character and go on */
+ break;
+ }
+ default: /* found a word delimiter */
+ return (st == s) ? NIL : st;
+ }
+ }
+}
+
+/* Copy an RFC 2822 format string
+ * Accepts: string
+ * Returns: copy of string
+ */
+
+char *rfc822_cpy (char *src)
+{
+ /* copy and unquote */
+ return rfc822_quote (cpystr (src));
+}
+
+
+/* Unquote an RFC 2822 format string
+ * Accepts: string
+ * Returns: string
+ */
+
+char *rfc822_quote (char *src)
+{
+ char *ret = src;
+ if (strpbrk (src,"\\\"")) { /* any quoting in string? */
+ char *dst = ret;
+ while (*src) { /* copy string */
+ if (*src == '\"') src++; /* skip double quote entirely */
+ else {
+ if (*src == '\\') src++;/* skip over single quote, copy next always */
+ *dst++ = *src++; /* copy character */
+ }
+ }
+ *dst = '\0'; /* tie off string */
+ }
+ return ret; /* return our string */
+}
+
+
+/* Copy address list
+ * Accepts: address list
+ * Returns: address list
+ */
+
+ADDRESS *rfc822_cpy_adr (ADDRESS *adr)
+{
+ ADDRESS *dadr;
+ ADDRESS *ret = NIL;
+ ADDRESS *prev = NIL;
+ while (adr) { /* loop while there's still an MAP adr */
+ dadr = mail_newaddr (); /* instantiate a new address */
+ if (!ret) ret = dadr; /* note return */
+ if (prev) prev->next = dadr;/* tie on to the end of any previous */
+ dadr->personal = cpystr (adr->personal);
+ dadr->adl = cpystr (adr->adl);
+ dadr->mailbox = cpystr (adr->mailbox);
+ dadr->host = cpystr (adr->host);
+ prev = dadr; /* this is now the previous */
+ adr = adr->next; /* go to next address in list */
+ }
+ return (ret); /* return the MTP address list */
+}
+
+/* Skips RFC 2822 whitespace
+ * Accepts: pointer to string pointer
+ */
+
+void rfc822_skipws (char **s)
+{
+ while (T) switch (**s) {
+ case ' ': case '\t': case '\015': case '\012':
+ ++*s; /* skip all forms of LWSP */
+ break;
+ case '(': /* start of comment */
+ if (rfc822_skip_comment (s,(long) NIL)) break;
+ default:
+ return; /* end of whitespace */
+ }
+}
+
+
+/* Skips RFC 2822 comment
+ * Accepts: pointer to string pointer
+ * trim flag
+ * Returns: pointer to first non-blank character of comment
+ */
+
+char *rfc822_skip_comment (char **s,long trim)
+{
+ char *ret,tmp[MAILTMPLEN];
+ char *s1 = *s;
+ char *t = NIL;
+ /* skip past whitespace */
+ for (ret = ++s1; *ret == ' '; ret++);
+ do switch (*s1) { /* get character of comment */
+ case '(': /* nested comment? */
+ if (!rfc822_skip_comment (&s1,(long) NIL)) return NIL;
+ t = --s1; /* last significant char at end of comment */
+ break;
+ case ')': /* end of comment? */
+ *s = ++s1; /* skip past end of comment */
+ if (trim) { /* if level 0, must trim */
+ if (t) t[1] = '\0'; /* tie off comment string */
+ else *ret = '\0'; /* empty comment */
+ }
+ return ret;
+ case '\\': /* quote next character? */
+ if (*++s1) { /* next character non-null? */
+ t = s1; /* update last significant character pointer */
+ break; /* all OK */
+ }
+ case '\0': /* end of string */
+ sprintf (tmp,"Unterminated comment: %.80s",*s);
+ MM_LOG (tmp,PARSE);
+ **s = '\0'; /* nuke duplicate messages in case reparse */
+ return NIL; /* this is wierd if it happens */
+ case ' ': /* whitespace isn't significant */
+ break;
+ default: /* random character */
+ t = s1; /* update last significant character pointer */
+ break;
+ } while (s1++);
+ return NIL; /* impossible, but pacify lint et al */
+}
+
+/* Buffered output routines */
+
+
+/* Output character to buffer
+ * Accepts: buffer
+ * character to write
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_char (RFC822BUFFER *buf,int c)
+{
+ if ((buf->cur == buf->end) && !rfc822_output_flush (buf)) return NIL;
+ *buf->cur++ = c; /* add character, soutr buffer if full */
+ return (buf->cur == buf->end) ? rfc822_output_flush (buf) : LONGT;
+}
+
+
+/* Output data to buffer
+ * Accepts: buffer
+ * data to write
+ * size of data
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_data (RFC822BUFFER *buf,char *string,long len)
+{
+ while (len) { /* until request satified */
+ long i;
+ if (i = min (len,buf->end - buf->cur)) {
+ memcpy (buf->cur,string,i);
+ buf->cur += i; /* blat data */
+ string += i;
+ len -= i;
+ }
+ /* soutr buffer now if full */
+ if ((len || (buf->cur == buf->end)) && !rfc822_output_flush (buf))
+ return NIL;
+ }
+ return LONGT;
+}
+
+/* Output string to buffer
+ * Accepts: buffer
+ * string to write
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_string (RFC822BUFFER *buf,char *string)
+{
+ return rfc822_output_data (buf,string,strlen (string));
+}
+
+
+/* Flush buffer
+ * Accepts: buffer
+ * I/O routine
+ * stream for I/O routine
+ * Returns: T if success, NIL if error
+ */
+
+long rfc822_output_flush (RFC822BUFFER *buf)
+{
+ *buf->cur = '\0'; /* tie off buffer at this point */
+ return (*buf->f) (buf->s,buf->cur = buf->beg);
+}
+
+/* Message writing routines */
+
+
+/* Output RFC 822 message
+ * Accepts: temporary buffer as a SIZEDTEXT
+ * envelope
+ * body
+ * I/O routine
+ * stream for I/O routine
+ * non-zero if 8-bit output desired
+ * Returns: T if successful, NIL if failure
+ *
+ * This routine always uses standard specials for phrases and does not write
+ * bcc entries, since it is called from the SMTP and NNTP routines. If you
+ * need to do something different you need to arm an rfc822outfull_t and/or
+ * rfc822out_t function.
+ */
+
+long rfc822_output_full (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,long ok8)
+{
+ rfc822outfull_t r822of =
+ (rfc822outfull_t) mail_parameters (NIL,GET_RFC822OUTPUTFULL,NIL);
+ rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
+ /* call external RFC 2822 output generator */
+ if (r822of) return (*r822of) (buf,env,body,ok8);
+ else if (r822o) return (*r822o) (buf->cur,env,body,buf->f,buf->s,ok8);
+ /* encode body as necessary */
+ if (ok8) rfc822_encode_body_8bit (env,body);
+ else rfc822_encode_body_7bit (env,body);
+ /* output header and body */
+ return rfc822_output_header (buf,env,body,NIL,NIL) &&
+ rfc822_output_text (buf,body) && rfc822_output_flush (buf);
+}
+
+/* Output RFC 822 header
+ * Accepts: buffer
+ * envelope
+ * body
+ * non-standard specials to be used for phrases if non-NIL
+ * flags (non-zero to include bcc
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_header (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ const char *specials,long flags)
+{
+ long i = env->remail ? strlen (env->remail) : 0;
+ return /* write header */
+ (!i || /* snip extra CRLF from remail header */
+ rfc822_output_data (buf,env->remail,
+ ((i > 4) && (env->remail[i-4] == '\015')) ?
+ i - 2 : i)) &&
+ rfc822_output_header_line (buf,"Newsgroups",i,env->newsgroups) &&
+ rfc822_output_header_line (buf,"Date",i,env->date) &&
+ rfc822_output_address_line (buf,"From",i,env->from,specials) &&
+ rfc822_output_address_line (buf,"Sender",i,env->sender,specials) &&
+ rfc822_output_address_line (buf,"Reply-To",i,env->reply_to,specials) &&
+ rfc822_output_header_line (buf,"Subject",i,env->subject) &&
+ ((env->bcc && !(env->to || env->cc)) ?
+ rfc822_output_string (buf,"To: undisclosed recipients: ;\015\012") :
+ LONGT) &&
+ rfc822_output_address_line (buf,"To",i,env->to,specials) &&
+ rfc822_output_address_line (buf,"cc",i,env->cc,specials) &&
+ (flags ? rfc822_output_address_line (buf,"bcc",i,env->bcc,specials) : T) &&
+ rfc822_output_header_line (buf,"In-Reply-To",i,env->in_reply_to) &&
+ rfc822_output_header_line (buf,"Message-ID",i,env->message_id) &&
+ rfc822_output_header_line (buf,"Followup-to",i,env->followup_to) &&
+ rfc822_output_header_line (buf,"References",i,env->references) &&
+ (env->remail || !body ||
+ (rfc822_output_string (buf,"MIME-Version: 1.0\015\012") &&
+ rfc822_output_body_header (buf,body))) &&
+ /* write terminating blank line */
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Output RFC 2822 header text line
+ * Accepts: buffer
+ * pointer to header type
+ * non-NIL if resending
+ * pointer to text
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_header_line (RFC822BUFFER *buf,char *type,long resent,
+ char *text)
+{
+ return !text ||
+ ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
+ rfc822_output_string (buf,type) && rfc822_output_string (buf,": ") &&
+ rfc822_output_string (buf,text) && rfc822_output_string (buf,"\015\012"));
+}
+
+
+/* Output RFC 2822 header address line
+ * Accepts: buffer
+ * pointer to header type
+ * non-NIL if resending
+ * address(s) to interpret
+ * non-standard specials to be used for phrases if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address_line (RFC822BUFFER *buf,char *type,long resent,
+ ADDRESS *adr,const char *specials)
+{
+ long pretty = strlen (type);
+ return !adr ||
+ ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
+ rfc822_output_data (buf,type,pretty) && rfc822_output_string (buf,": ") &&
+ rfc822_output_address_list (buf,adr,
+ resent ? pretty + sizeof (RESENTPREFIX) - 1 :
+ pretty,specials) &&
+ rfc822_output_string (buf,"\015\012"));
+}
+
+/* Output RFC 2822 address list
+ * Accepts: buffer
+ * pointer to address list
+ * non-zero if pretty-printing
+ * non-standard specials to be used for phrases if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
+ const char *specials)
+{
+ long n;
+ /* default to rspecials */
+ if (!specials) specials = rspecials;
+ for (n = 0; adr; adr = adr->next) {
+ char *base = buf->cur;
+ if (adr->host) { /* ordinary address? */
+ if (!(pretty && n)) { /* suppress if pretty and in group */
+ if ( /* use phrase <route-addr> if phrase */
+#if RFC822
+ adr->adl || /* or A-D-L */
+#endif
+ (adr->personal && *adr->personal)) {
+ if (!((adr->personal ? rfc822_output_cat (buf,adr->personal,
+ rspecials) : LONGT) &&
+ rfc822_output_string (buf," <") &&
+ rfc822_output_address (buf,adr) &&
+ rfc822_output_string (buf,">"))) return NIL;
+ }
+ else if (!rfc822_output_address (buf,adr)) return NIL;
+ if (adr->next && adr->next->mailbox &&
+ !rfc822_output_string (buf,", ")) return NIL;
+ }
+ }
+ else if (adr->mailbox) { /* start of group? */
+ /* yes, write group */
+ if (!(rfc822_output_cat (buf,adr->mailbox,rspecials) &&
+ rfc822_output_string (buf,": "))) return NIL;
+ ++n; /* in a group now */
+ }
+ else if (n) { /* must be end of group (but be paranoid) */
+ if (!rfc822_output_char (buf,';') ||
+ ((!--n && adr->next && adr->next->mailbox) &&
+ !rfc822_output_string (buf,", "))) return NIL;
+ }
+ if (pretty && adr->next && /* pretty printing? */
+ ((pretty += ((buf->cur > base) ? buf->cur - base :
+ (buf->end - base) + (buf->cur - buf->beg))) >= 78)) {
+ if (!(rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_string (buf,RFC822CONT))) return NIL;
+ base = buf->cur; /* update base for pretty printing */
+ pretty = sizeof (RFC822CONT) - 1;
+ }
+ }
+ return LONGT;
+}
+
+/* Write RFC 2822 route-address to string
+ * Accepts: buffer
+ * pointer to single address
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address (RFC822BUFFER *buf,ADDRESS *adr)
+{
+ return !adr || !adr->host ||
+ (
+#if RFC822 /* old code with A-D-L support */
+ (!adr->adl || (rfc822_output_string (buf,adr->adl) &&
+ rfc822_output_char (buf,':'))) &&
+#endif
+ rfc822_output_cat (buf,adr->mailbox,NIL) &&
+ ((*adr->host == '@') || /* unless null host (HIGHLY discouraged!) */
+ (rfc822_output_char (buf,'@') &&
+ rfc822_output_cat (buf,adr->host,NIL))));
+}
+
+
+/* Output RFC 2822 string with concatenation
+ * Accepts: buffer
+ * string to concatenate
+ * list of special characters or NIL for dot-atom format
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_cat (RFC822BUFFER *buf,char *src,const char *specials)
+{
+ char *s;
+ if (!*src || /* empty string or any specials present? */
+ (specials ? (T && strpbrk (src,specials)) :
+ (strpbrk (src,wspecials) || (*src == '.') || strstr (src,"..") ||
+ (src[strlen (src) - 1] == '.')))) {
+ /* yes, write as quoted string*/
+ if (!rfc822_output_char (buf,'"')) return NIL;
+ /* embedded quote characters? */
+ for (; s = strpbrk (src,"\\\""); src = s + 1) {
+ /* yes, insert quoting */
+ if (!(rfc822_output_data (buf,src,s-src) &&
+ rfc822_output_char (buf,'\\') &&
+ rfc822_output_char (buf,*s))) return NIL;
+ }
+ /* return string and trailing quote*/
+ return rfc822_output_string (buf,src) && rfc822_output_char (buf,'"');
+ }
+ /* easy case */
+ return rfc822_output_string (buf,src);
+}
+
+/* Output MIME parameter list
+ * Accepts: buffer
+ * parameter list
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_parameter (RFC822BUFFER *buf,PARAMETER *param)
+{
+ while (param) {
+ if (rfc822_output_string (buf,"; ") &&
+ rfc822_output_string (buf,param->attribute) &&
+ rfc822_output_char (buf,'=') &&
+ rfc822_output_cat (buf,param->value,tspecials)) param = param->next;
+ else return NIL;
+ }
+ return LONGT;
+}
+
+
+/* Output RFC 2822 stringlist
+ * Accepts: buffer
+ * stringlist
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_stringlist (RFC822BUFFER *buf,STRINGLIST *stl)
+{
+ while (stl)
+ if (!rfc822_output_cat (buf,(char *) stl->text.data,tspecials) ||
+ ((stl = stl->next) && !rfc822_output_string (buf,", ")))
+ return NIL;
+ return LONGT;
+}
+
+/* Output body content header
+ * Accepts: buffer
+ * body to interpret
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_body_header (RFC822BUFFER *buf,BODY *body)
+{
+ return /* type and subtype*/
+ rfc822_output_string (buf,"Content-Type: ") &&
+ rfc822_output_string (buf,body_types[body->type]) &&
+ rfc822_output_char (buf,'/') &&
+ rfc822_output_string (buf,body->subtype ? body->subtype :
+ rfc822_default_subtype (body->type)) &&
+ /* parameters (w/ US-ASCII default */
+ (body->parameter ? rfc822_output_parameter (buf,body->parameter) :
+ ((body->type != TYPETEXT) ||
+ (rfc822_output_string (buf,"; CHARSET=") &&
+ rfc822_output_string (buf,(body->encoding == ENC7BIT) ?
+ "US-ASCII" : "X-UNKNOWN")))) &&
+ (!body->encoding || /* note: 7BIT never output as encoding! */
+ (rfc822_output_string (buf,"\015\012Content-Transfer-Encoding: ") &&
+ rfc822_output_string (buf,body_encodings[body->encoding]))) &&
+ (!body->id || /* identification */
+ (rfc822_output_string (buf,"\015\012Content-ID: ") &&
+ rfc822_output_string (buf,body->id))) &&
+ (!body->description || /* description */
+ (rfc822_output_string (buf,"\015\012Content-Description: ") &&
+ rfc822_output_string (buf,body->description))) &&
+ (!body->md5 || /* MD5 checksum */
+ (rfc822_output_string (buf,"\015\012Content-MD5: ") &&
+ rfc822_output_string (buf,body->md5))) &&
+ (!body->language || /* language */
+ (rfc822_output_string (buf,"\015\012Content-Language: ") &&
+ rfc822_output_stringlist (buf,body->language))) &&
+ (!body->location || /* location */
+ (rfc822_output_string (buf,"\015\012Content-Location: ") &&
+ rfc822_output_string (buf,body->location))) &&
+ (!body->disposition.type || /* disposition */
+ (rfc822_output_string (buf,"\015\012Content-Disposition: ") &&
+ rfc822_output_string (buf,body->disposition.type) &&
+ rfc822_output_parameter (buf,body->disposition.parameter))) &&
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Encode a body for 7BIT transmittal
+ * Accepts: envelope
+ * body
+ */
+
+void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
+{
+ void *f;
+ PART *part;
+ PARAMETER **param;
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+ for (param = &body->parameter;
+ *param && strcmp ((*param)->attribute,"BOUNDARY");
+ param = &(*param)->next);
+ if (!*param) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (*param) = mail_newbody_parameter ();
+ (*param)->attribute = cpystr ("BOUNDARY");
+ (*param)->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do rfc822_encode_body_7bit (env,&part->body);
+ while (part = part->next); /* until done */
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ switch (body->encoding) {
+ case ENC7BIT:
+ break;
+ case ENC8BIT:
+ MM_LOG ("8-bit included message in 7-bit message body",PARSE);
+ break;
+ case ENCBINARY:
+ MM_LOG ("Binary included message in 7-bit message body",PARSE);
+ break;
+ default:
+ fatal ("Invalid rfc822_encode_body_7bit message encoding");
+ }
+ break; /* can't change encoding */
+ default: /* all else has some encoding */
+ switch (body->encoding) {
+ case ENC8BIT: /* encode 8BIT into QUOTED-PRINTABLE */
+ /* remember old 8-bit contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_8bit (body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCQUOTEDPRINTABLE;
+ fs_give (&f); /* flush old binary contents */
+ break;
+ case ENCBINARY: /* encode binary into BASE64 */
+ /* remember old binary contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_binary ((void *) body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCBASE64;
+ fs_give (&f); /* flush old binary contents */
+ default: /* otherwise OK */
+ break;
+ }
+ break;
+ }
+}
+
+/* Encode a body for 8BIT transmittal
+ * Accepts: envelope
+ * body
+ */
+
+void rfc822_encode_body_8bit (ENVELOPE *env,BODY *body)
+{
+ void *f;
+ PART *part;
+ PARAMETER **param;
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+ for (param = &body->parameter;
+ *param && strcmp ((*param)->attribute,"BOUNDARY");
+ param = &(*param)->next);
+ if (!*param) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (*param) = mail_newbody_parameter ();
+ (*param)->attribute = cpystr ("BOUNDARY");
+ (*param)->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do rfc822_encode_body_8bit (env,&part->body);
+ while (part = part->next); /* until done */
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ switch (body->encoding) {
+ case ENC7BIT:
+ case ENC8BIT:
+ break;
+ case ENCBINARY:
+ MM_LOG ("Binary included message in 8-bit message body",PARSE);
+ break;
+ default:
+ fatal ("Invalid rfc822_encode_body_7bit message encoding");
+ }
+ break; /* can't change encoding */
+ default: /* other type, encode binary into BASE64 */
+ if (body->encoding == ENCBINARY) {
+ /* remember old binary contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_binary ((void *) body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCBASE64;
+ fs_give (&f); /* flush old binary contents */
+ }
+ break;
+ }
+}
+
+/* Output RFC 822 text
+ * Accepts: buffer
+ * body
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output_text (RFC822BUFFER *buf,BODY *body)
+{
+ /* MULTIPART gets special handling */
+ if (body->type == TYPEMULTIPART) {
+ char *cookie,tmp[MAILTMPLEN];
+ PARAMETER *param;
+ PART *part;
+ /* find cookie */
+ for (param = body->parameter; param && strcmp (param->attribute,"BOUNDARY");
+ param = param->next);
+ if (param) cookie = param->value;
+ else { /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (cookie = tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (param = mail_newbody_parameter ())->attribute = cpystr ("BOUNDARY");
+ param->value = cpystr (tmp);
+ param->next = body->parameter;
+ body->parameter = param;
+ }
+ /* output each part */
+ for (part = body->nested.part; part; part = part->next)
+ if (!(rfc822_output_string (buf,"--") &&
+ rfc822_output_string (buf,cookie) &&
+ rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_body_header (buf,&part->body) &&
+ rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_text (buf,&part->body))) return NIL;
+ /* output trailing cookie */
+ return rfc822_output_string (buf,"--") &&
+ rfc822_output_string (buf,cookie) &&
+ rfc822_output_string (buf,"--\015\012");
+ }
+ /* output segment and trailing CRLF */
+ return (!body->contents.text.data ||
+ rfc822_output_string (buf,(char *) body->contents.text.data)) &&
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Body contents encoding/decoding routines */
+
+
+/* Convert BASE64 contents to binary
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as binary or NIL if error
+ */
+
+#define WSP 0176 /* NUL, TAB, LF, FF, CR, SPC */
+#define JNK 0177
+#define PAD 0100
+
+void *rfc822_base64 (unsigned char *src,unsigned long srcl,unsigned long *len)
+{
+ char c,*s,tmp[MAILTMPLEN];
+ void *ret = fs_get ((size_t) ((*len = 4 + ((srcl * 3) / 4))) + 1);
+ char *d = (char *) ret;
+ int e;
+ static char decode[256] = {
+ WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,WSP,WSP,JNK,WSP,WSP,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,076,JNK,JNK,JNK,077,
+ 064,065,066,067,070,071,072,073,074,075,JNK,JNK,JNK,PAD,JNK,JNK,
+ JNK,000,001,002,003,004,005,006,007,010,011,012,013,014,015,016,
+ 017,020,021,022,023,024,025,026,027,030,031,JNK,JNK,JNK,JNK,JNK,
+ JNK,032,033,034,035,036,037,040,041,042,043,044,045,046,047,050,
+ 051,052,053,054,055,056,057,060,061,062,063,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK
+ };
+ /* initialize block */
+ memset (ret,0,((size_t) *len) + 1);
+ *len = 0; /* in case we return an error */
+
+ /* simple-minded decode */
+ for (e = 0; srcl--; ) switch (c = decode[*src++]) {
+ default: /* valid BASE64 data character */
+ switch (e++) { /* install based on quantum position */
+ case 0:
+ *d = c << 2; /* byte 1: high 6 bits */
+ break;
+ case 1:
+ *d++ |= c >> 4; /* byte 1: low 2 bits */
+ *d = c << 4; /* byte 2: high 4 bits */
+ break;
+ case 2:
+ *d++ |= c >> 2; /* byte 2: low 4 bits */
+ *d = c << 6; /* byte 3: high 2 bits */
+ break;
+ case 3:
+ *d++ |= c; /* byte 3: low 6 bits */
+ e = 0; /* reinitialize mechanism */
+ break;
+ }
+ break;
+ case WSP: /* whitespace */
+ break;
+ case PAD: /* padding */
+ switch (e++) { /* check quantum position */
+ case 3: /* one = is good enough in quantum 3 */
+ /* make sure no data characters in remainder */
+ for (; srcl; --srcl) switch (decode[*src++]) {
+ /* ignore space, junk and extraneous padding */
+ case WSP: case JNK: case PAD:
+ break;
+ default: /* valid BASE64 data character */
+ /* This indicates bad MIME. One way that it can be caused is if
+ a single-section message was BASE64 encoded and then something
+ (e.g. a mailing list processor) appended text. The problem is
+ that in 1 out of 3 cases, there is no padding and hence no way
+ to detect the end of the data. Consequently, prudent software
+ will always encapsulate a BASE64 segment inside a MULTIPART.
+ */
+ sprintf (tmp,"Possible data truncation in rfc822_base64(): %.80s",
+ (char *) src - 1);
+ if (s = strpbrk (tmp,"\015\012")) *s = NIL;
+ mm_log (tmp,PARSE);
+ srcl = 1; /* don't issue any more messages */
+ break;
+ }
+ break;
+ case 2: /* expect a second = in quantum 2 */
+ if (srcl && (*src == '=')) break;
+ default: /* impossible quantum position */
+ fs_give (&ret);
+ return NIL;
+ }
+ break;
+ case JNK: /* junk character */
+ fs_give (&ret);
+ return NIL;
+ }
+ *len = d - (char *) ret; /* calculate data length */
+ *d = '\0'; /* NUL terminate just in case */
+ return ret; /* return the string */
+}
+
+/* Convert binary contents to BASE64
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as BASE64
+ */
+
+unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len)
+{
+ unsigned char *ret,*d;
+ unsigned char *s = (unsigned char *) src;
+ char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned long i = ((srcl + 2) / 3) * 4;
+ *len = i += 2 * ((i / 60) + 1);
+ d = ret = (unsigned char *) fs_get ((size_t) ++i);
+ /* process tuplets */
+ for (i = 0; srcl >= 3; s += 3, srcl -= 3) {
+ *d++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (s[1] >> 4)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = v[((s[1] << 2) + (s[2] >> 6)) & 0x3f];
+ *d++ = v[s[2] & 0x3f]; /* byte 4: low 6 bits (3) */
+ if ((++i) == 15) { /* output 60 characters? */
+ i = 0; /* restart line break count, insert CRLF */
+ *d++ = '\015'; *d++ = '\012';
+ }
+ }
+ if (srcl) {
+ *d++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) : 0)) & 0x3f] : '=';
+ /* byte 4: low 6 bits (3) */
+ *d++ = srcl ? v[s[2] & 0x3f] : '=';
+ if (srcl) srcl--; /* count third character if processed */
+ if ((++i) == 15) { /* output 60 characters? */
+ i = 0; /* restart line break count, insert CRLF */
+ *d++ = '\015'; *d++ = '\012';
+ }
+ }
+ *d++ = '\015'; *d++ = '\012'; /* insert final CRLF */
+ *d = '\0'; /* tie off string */
+ if (((unsigned long) (d - ret)) != *len) fatal ("rfc822_binary logic flaw");
+ return ret; /* return the resulting string */
+}
+
+/* Convert QUOTED-PRINTABLE contents to 8BIT
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as 8-bit text or NIL if error
+ */
+
+unsigned char *rfc822_qprint (unsigned char *src,unsigned long srcl,
+ unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ unsigned int bogon = NIL;
+ unsigned char *ret = (unsigned char *) fs_get ((size_t) srcl + 1);
+ unsigned char *d = ret;
+ unsigned char *t = d;
+ unsigned char *s = src;
+ unsigned char c,e;
+ *len = 0; /* in case we return an error */
+ /* until run out of characters */
+ while (((unsigned long) (s - src)) < srcl) {
+ switch (c = *s++) { /* what type of character is it? */
+ case '=': /* quoting character */
+ if (((unsigned long) (s - src)) < srcl) switch (c = *s++) {
+ case '\0': /* end of data */
+ s--; /* back up pointer */
+ break;
+ case '\015': /* non-significant line break */
+ if ((((unsigned long) (s - src)) < srcl) && (*s == '\012')) s++;
+ case '\012': /* bare LF */
+ t = d; /* accept any leading spaces */
+ break;
+ default: /* two hex digits then */
+ if (!(isxdigit (c) && (((unsigned long) (s - src)) < srcl) &&
+ (e = *s++) && isxdigit (e))) {
+ /* This indicates bad MIME. One way that it can be caused is if
+ a single-section message was QUOTED-PRINTABLE encoded and then
+ something (e.g. a mailing list processor) appended text. The
+ problem is that there is no way to determine where the encoded
+ data ended and the appended crud began. Consequently, prudent
+ software will always encapsulate a QUOTED-PRINTABLE segment
+ inside a MULTIPART.
+ */
+ if (!bogon++) { /* only do this once */
+ sprintf (tmp,"Invalid quoted-printable sequence: =%.80s",
+ (char *) s - 1);
+ mm_log (tmp,PARSE);
+ }
+ *d++ = '='; /* treat = as ordinary character */
+ *d++ = c; /* and the character following */
+ t = d; /* note point of non-space */
+ break;
+ }
+ *d++ = hex2byte (c,e); /* merge the two hex digits */
+ t = d; /* note point of non-space */
+ break;
+ }
+ break;
+ case ' ': /* space, possibly bogus */
+ *d++ = c; /* stash the space but don't update s */
+ break;
+ case '\015': /* end of line */
+ case '\012': /* bare LF */
+ d = t; /* slide back to last non-space, drop in */
+ default:
+ *d++ = c; /* stash the character */
+ t = d; /* note point of non-space */
+ }
+ }
+ *d = '\0'; /* tie off results */
+ *len = d - ret; /* calculate length */
+ return ret; /* return the string */
+}
+
+/* Convert 8BIT contents to QUOTED-PRINTABLE
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as quoted-printable text
+ */
+
+#define MAXL (size_t) 75 /* 76th position only used by continuation = */
+
+unsigned char *rfc822_8bit (unsigned char *src,unsigned long srcl,
+ unsigned long *len)
+{
+ unsigned long lp = 0;
+ unsigned char *ret = (unsigned char *)
+ fs_get ((size_t) (3*srcl + 3*(((3*srcl)/MAXL) + 1)));
+ unsigned char *d = ret;
+ char *hex = "0123456789ABCDEF";
+ unsigned char c;
+ while (srcl--) { /* for each character */
+ /* true line break? */
+ if (((c = *src++) == '\015') && (*src == '\012') && srcl) {
+ *d++ = '\015'; *d++ = *src++; srcl--;
+ lp = 0; /* reset line count */
+ }
+ else { /* not a line break */
+ /* quoting required? */
+ if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') ||
+ ((c == ' ') && (*src == '\015'))) {
+ if ((lp += 3) > MAXL) { /* yes, would line overflow? */
+ *d++ = '='; *d++ = '\015'; *d++ = '\012';
+ lp = 3; /* set line count */
+ }
+ *d++ = '='; /* quote character */
+ *d++ = hex[c >> 4]; /* high order 4 bits */
+ *d++ = hex[c & 0xf]; /* low order 4 bits */
+ }
+ else { /* ordinary character */
+ if ((++lp) > MAXL) { /* would line overflow? */
+ *d++ = '='; *d++ = '\015'; *d++ = '\012';
+ lp = 1; /* set line count */
+ }
+ *d++ = c; /* ordinary character */
+ }
+ }
+ }
+ *d = '\0'; /* tie off destination */
+ *len = d - ret; /* calculate true size */
+ /* try to give some space back */
+ fs_resize ((void **) &ret,(size_t) *len + 1);
+ return ret;
+}
+
+/* Legacy Routines */
+
+/*
+ * WARNING: These routines are for compatibility with old software only.
+ *
+ * Their use in new software is to be avoided.
+ *
+ * These interfaces do not provide satisfactory buffer checking. In
+ * versions of c-client prior to imap-2005, they did not provide any
+ * buffer checking at all.
+ *
+ * As a half-hearted attempt, these new compatability functions for the
+ * legacy interfaces limit what they write to size SENDBUFLEN and will
+ * fatal() if more than that is written. However, that isn't good enough
+ * since several of these functions *append* to the buffer, and return an
+ * updated pointer. Consequently, there is no way of knowing what the
+ * actual available space is in the buffer, yet the function will still
+ * write up to SENDBUFLEN bytes even if there is much less space actually
+ * available. The result is a buffer overflow.
+ *
+ * You won't get a buffer overflow if you never attempt to append using
+ * these interfaces, but you can get the fatal() if it tries to write
+ * more than SENDBUFLEN bytes.
+ *
+ * To avoid this problem, use the corresponding rfc822_output_???()
+ * functions instead, e.g., rfc822_output_address() instead of
+ * rfc822_address().
+ *
+ */
+
+/* Flush routine, only called if overflow
+ * Accepts: stream
+ * string to output
+ * Returns: never
+ */
+
+static long rfc822_legacy_soutr (void *stream,char *string)
+{
+ fatal ("rfc822.c legacy routine buffer overflow");
+ return NIL;
+}
+
+/* Legacy write RFC 2822 header from message structure
+ * Accepts: scratch buffer to write into
+ * message envelope
+ * message body
+ */
+
+void rfc822_header (char *header,ENVELOPE *env,BODY *body)
+{
+ RFC822BUFFER buf;
+ /* write at start of buffer */
+ buf.end = (buf.beg = buf.cur = header) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_header (&buf,env,body,NIL,NIL);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write RFC 2822 text from header line
+ * Accepts: pointer to destination string pointer
+ * pointer to header type
+ * message to interpret
+ * pointer to text
+ */
+
+void rfc822_header_line (char **header,char *type,ENVELOPE *env,char *text)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_header_line (&buf,type,env->remail ? LONGT : NIL,text);
+ *(*header = buf.cur) = '\0'; /* tie off buffer */
+}
+
+/* Legacy write RFC 2822 address from header line
+ * Accepts: pointer to destination string pointer
+ * pointer to header type
+ * message to interpret
+ * address to interpret
+ */
+
+void rfc822_address_line (char **header,char *type,ENVELOPE *env,ADDRESS *adr)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address_line (&buf,type,env->remail ? LONGT : NIL,adr,NIL);
+ *(*header = buf.cur) = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write RFC 2822 address list
+ * Accepts: pointer to destination string
+ * address to interpret
+ * header base if pretty-printing
+ * Returns: end of destination string
+ */
+
+char *rfc822_write_address_full (char *dest,ADDRESS *adr,char *base)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address_list (&buf,adr,base ? dest - base : 0,NIL);
+ *buf.cur = '\0'; /* tie off buffer */
+ return buf.cur;
+}
+
+
+/* Legacy write RFC 2822 route-address to string
+ * Accepts: pointer to destination string
+ * address to interpret
+ */
+
+void rfc822_address (char *dest,ADDRESS *adr)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address (&buf,adr);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+/* Concatenate RFC 2822 string
+ * Accepts: pointer to destination string
+ * pointer to string to concatenate
+ * list of special characters or NIL for dot-atom format
+ */
+
+void rfc822_cat (char *dest,char *src,const char *specials)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_cat (&buf,src,specials);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write body content header
+ * Accepts: pointer to destination string pointer
+ * pointer to body to interpret
+ */
+
+void rfc822_write_body_header (char **dst,BODY *body)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *dst + strlen (*dst)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_body_header (&buf,body);
+ *(*dst = buf.cur) = '\0'; /* tie off buffer */
+}
+
+/* Legacy output RFC 822 message
+ * Accepts: temporary buffer
+ * envelope
+ * body
+ * I/O routine
+ * stream for I/O routine
+ * non-zero if 8-bit output desired
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
+ long ok8bit)
+{
+ long ret;
+ rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
+ /* call external RFC 2822 output generator */
+ if (r822o) ret = (*r822o) (t,env,body,f,s,ok8bit);
+ else { /* output generator not armed */
+ RFC822BUFFER buf; /* use our own buffer rather than trust */
+ char tmp[SENDBUFLEN+1]; /* client to give us a big enough one */
+ buf.f = f;
+ buf.s = s;
+ buf.end = (buf.beg = buf.cur = t) + SENDBUFLEN - 1;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional guard byte */
+ ret = rfc822_output_full (&buf,env,body,ok8bit);
+ }
+ return ret;
+}
+
+
+/* Legacy output RFC 822 body
+ * Accepts: body
+ * I/O routine
+ * stream for I/O routine
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output_body (BODY *body,soutr_t f,void *s)
+{
+ RFC822BUFFER buf;
+ char tmp[SENDBUFLEN+1];
+ buf.f = f;
+ buf.s = s;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional guard byte */
+ return rfc822_output_text (&buf,body) && rfc822_output_flush (&buf);
+}
diff --git a/imap/src/c-client/rfc822.h b/imap/src/c-client/rfc822.h
new file mode 100644
index 00000000..0621732a
--- /dev/null
+++ b/imap/src/c-client/rfc822.h
@@ -0,0 +1,127 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: RFC 2822 and MIME routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 30 August 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+#define MAXGROUPDEPTH 50 /* RFC [2]822 doesn't allow any group nesting */
+#define MAXMIMEDEPTH 50 /* more than any sane MIMEgram */
+
+/* Output buffering for RFC [2]822 */
+
+typedef long (*soutr_t) (void *stream,char *string);
+typedef long (*rfc822out_t) (char *tmp,ENVELOPE *env,BODY *body,soutr_t f,
+ void *s,long ok8bit);
+
+typedef struct rfc822buffer {
+ soutr_t f; /* I/O flush routine */
+ void *s; /* stream for I/O routine */
+ char *beg; /* start of buffer */
+ char *cur; /* current buffer pointer */
+ char *end; /* end of buffer */
+} RFC822BUFFER;
+
+typedef long (*rfc822outfull_t) (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ long ok8bit);
+
+/* Function prototypes */
+
+char *rfc822_default_subtype (unsigned short type);
+void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
+ STRING *bs,char *host,unsigned long depth,
+ unsigned long flags);
+void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth,
+ unsigned long flags);
+void rfc822_parse_content_header (BODY *body,char *name,char *s);
+void rfc822_parse_parameter (PARAMETER **par,char *text);
+void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host);
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth);
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth);
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost);
+long rfc822_phraseonly (char *end);
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost);
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost);
+char *rfc822_parse_domain (char *string,char **end);
+char *rfc822_parse_phrase (char *string);
+char *rfc822_parse_word (char *string,const char *delimiters);
+char *rfc822_cpy (char *src);
+char *rfc822_quote (char *src);
+ADDRESS *rfc822_cpy_adr (ADDRESS *adr);
+void rfc822_skipws (char **s);
+char *rfc822_skip_comment (char **s,long trim);
+
+long rfc822_output_full (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,long ok8);
+long rfc822_output_flush (RFC822BUFFER *buf);
+long rfc822_output_header (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ const char *specials,long flags);
+long rfc822_output_header_line (RFC822BUFFER *buf,char *type,long resent,
+ char *text);
+long rfc822_output_address_line (RFC822BUFFER *buf,char *type,long resent,
+ ADDRESS *adr,const char *specials);
+long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
+ const char *specials);
+long rfc822_output_address (RFC822BUFFER *buf,ADDRESS *adr);
+long rfc822_output_cat (RFC822BUFFER *buf,char *src,const char *specials);
+long rfc822_output_parameter (RFC822BUFFER *buf,PARAMETER *param);
+long rfc822_output_stringlist (RFC822BUFFER *buf,STRINGLIST *stl);
+long rfc822_output_body_header (RFC822BUFFER *buf,BODY *body);
+void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body);
+void rfc822_encode_body_8bit (ENVELOPE *env,BODY *body);
+long rfc822_output_text (RFC822BUFFER *buf,BODY *body);
+void *rfc822_base64 (unsigned char *src,unsigned long srcl,unsigned long *len);
+unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len);
+unsigned char *rfc822_qprint (unsigned char *src,unsigned long srcl,
+ unsigned long *len);
+unsigned char *rfc822_8bit (unsigned char *src,unsigned long srcl,
+ unsigned long *len);
+
+/* Legacy routines for compatibility with the past */
+
+void rfc822_header (char *header,ENVELOPE *env,BODY *body);
+void rfc822_header_line (char **header,char *type,ENVELOPE *env,char *text);
+void rfc822_address_line (char **header,char *type,ENVELOPE *env,ADDRESS *adr);
+char *rfc822_write_address_full (char *dest,ADDRESS *adr,char *base);
+void rfc822_address (char *dest,ADDRESS *adr);
+void rfc822_cat (char *dest,char *src,const char *specials);
+void rfc822_write_body_header (char **header,BODY *body);
+long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
+ long ok8bit);
+long rfc822_output_body (BODY *body,soutr_t f,void *s);
+
+
+#define rfc822_write_address(dest,adr) \
+ rfc822_write_address_full (dest,adr,NIL)
+
+#define rfc822_parse_msg(en,bdy,s,i,bs,host,flags) \
+ rfc822_parse_msg_full (en,bdy,s,i,bs,host,0,flags)
diff --git a/imap/src/c-client/smanager.c b/imap/src/c-client/smanager.c
new file mode 100644
index 00000000..cee659ff
--- /dev/null
+++ b/imap/src/c-client/smanager.c
@@ -0,0 +1,129 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Subscription Manager
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 December 1992
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* Subscribe to mailbox
+ * Accepts: mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long sm_subscribe (char *mailbox)
+{
+ FILE *f;
+ char *s,db[MAILTMPLEN],tmp[MAILTMPLEN];
+ /* canonicalize INBOX */
+ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX";
+ SUBSCRIPTIONFILE (db); /* open subscription database */
+ if (f = fopen (db,"r")) { /* make sure not already there */
+ while (fgets (tmp,MAILTMPLEN,f)) {
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ if (!strcmp (tmp,mailbox)) {/* already subscribed? */
+ sprintf (tmp,"Already subscribed to mailbox %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ fclose (f);
+ return NIL;
+ }
+ }
+ fclose (f);
+ }
+ if (!(f = fopen (db,"a"))) { /* append new entry */
+ MM_LOG ("Can't append to subscription database",ERROR);
+ return NIL;
+ }
+ fprintf (f,"%s\n",mailbox);
+ return (fclose (f) == EOF) ? NIL : T;
+}
+
+/* Unsubscribe from mailbox
+ * Accepts: mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long sm_unsubscribe (char *mailbox)
+{
+ FILE *f,*tf;
+ char *s,tmp[MAILTMPLEN],old[MAILTMPLEN],newname[MAILTMPLEN];
+ int found = NIL;
+ /* canonicalize INBOX */
+ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX";
+ SUBSCRIPTIONFILE (old); /* make file names */
+ SUBSCRIPTIONTEMP (newname);
+ if (!(f = fopen (old,"r"))) /* open subscription database */
+ MM_LOG ("No subscriptions",ERROR);
+ else if (!(tf = fopen (newname,"w"))) {
+ MM_LOG ("Can't create subscription temporary file",ERROR);
+ fclose (f);
+ }
+ else {
+ while (fgets (tmp,MAILTMPLEN,f)) {
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ if (strcmp (tmp,mailbox)) fprintf (tf,"%s\n",tmp);
+ else found = T; /* found the name */
+ }
+ fclose (f);
+ if (fclose (tf) == EOF)
+ MM_LOG ("Can't write subscription temporary file",ERROR);
+ else if (!found) {
+ sprintf (tmp,"Not subscribed to mailbox %.80s",mailbox);
+ MM_LOG (tmp,ERROR); /* error if at end */
+ }
+ else if (!unlink (old) && !rename (newname,old)) return LONGT;
+ else MM_LOG ("Can't update subscription database",ERROR);
+ }
+ return NIL;
+}
+
+/* Read subscription database
+ * Accepts: pointer to subscription database handle (handle NIL if first time)
+ * Returns: character string for subscription database or NIL if done
+ */
+
+static char sbname[MAILTMPLEN];
+
+char *sm_read (void **sdb)
+{
+ FILE *f = (FILE *) *sdb;
+ char *s;
+ if (!f) { /* first time through? */
+ SUBSCRIPTIONFILE (sbname); /* open subscription database */
+ /* make sure not already there */
+ if (f = fopen (sbname,"r")) *sdb = (void *) f;
+ else return NIL;
+ }
+ if (fgets (sbname,MAILTMPLEN,f)) {
+ if (s = strchr (sbname,'\n')) *s = '\0';
+ return sbname;
+ }
+ fclose (f); /* all done */
+ *sdb = NIL; /* zap sdb */
+ return NIL;
+}
diff --git a/imap/src/c-client/smtp.c b/imap/src/c-client/smtp.c
new file mode 100644
index 00000000..0a3cf489
--- /dev/null
+++ b/imap/src/c-client/smtp.c
@@ -0,0 +1,793 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Simple Mail Transfer Protocol (SMTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 28 January 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the National Institutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+
+/* Constants */
+
+#define SMTPSSLPORT (long) 465 /* former assigned SSL TCP contact port */
+#define SMTPGREET (long) 220 /* SMTP successful greeting */
+#define SMTPAUTHED (long) 235 /* SMTP successful authentication */
+#define SMTPOK (long) 250 /* SMTP OK code */
+#define SMTPAUTHREADY (long) 334/* SMTP ready for authentication */
+#define SMTPREADY (long) 354 /* SMTP ready for data */
+#define SMTPSOFTFATAL (long) 421/* SMTP soft fatal code */
+#define SMTPWANTAUTH (long) 505 /* SMTP authentication needed */
+#define SMTPWANTAUTH2 (long) 530/* SMTP authentication needed */
+#define SMTPUNAVAIL (long) 550 /* SMTP mailbox unavailable */
+#define SMTPHARDERROR (long) 554/* SMTP miscellaneous hard failure */
+
+
+/* Convenient access to protocol-specific data */
+
+#define ESMTP stream->protocol.esmtp
+
+
+/* Function prototypes */
+
+void *smtp_challenge (void *s,unsigned long *len);
+long smtp_response (void *s,char *response,unsigned long size);
+long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp);
+long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error);
+long smtp_send (SENDSTREAM *stream,char *command,char *args);
+long smtp_reply (SENDSTREAM *stream);
+long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb);
+long smtp_fake (SENDSTREAM *stream,char *text);
+static long smtp_seterror (SENDSTREAM *stream,long code,char *text);
+long smtp_soutr (void *stream,char *s);
+
+/* Mailer parameters */
+
+static unsigned long smtp_maxlogintrials = MAXLOGINTRIALS;
+static long smtp_port = 0; /* default port override */
+static long smtp_sslport = 0;
+
+
+#ifndef RFC2821
+#define RFC2821 /* RFC 2821 compliance */
+#endif
+
+/* SMTP limits, current as of RFC 2821 */
+
+#define SMTPMAXLOCALPART 64
+#define SMTPMAXDOMAIN 255
+#define SMTPMAXPATH 256
+
+
+/* I have seen local parts of more than 64 octets, in spite of the SMTP
+ * limits. So, we'll have a more generous limit that's still guaranteed
+ * not to pop the buffer, and let the server worry about it. As of this
+ * writing, it comes out to 240. Anyone with a mailbox name larger than
+ * that is in serious need of a life or at least a new ISP! 23 June 1998
+ */
+
+#define MAXLOCALPART ((MAILTMPLEN - (SMTPMAXDOMAIN + SMTPMAXPATH + 32)) / 2)
+
+/* Mail Transfer Protocol manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *smtp_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ smtp_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) smtp_maxlogintrials;
+ break;
+ case SET_SMTPPORT:
+ smtp_port = (long) value;
+ break;
+ case GET_SMTPPORT:
+ value = (void *) smtp_port;
+ break;
+ case SET_SSLSMTPPORT:
+ smtp_sslport = (long) value;
+ break;
+ case GET_SSLSMTPPORT:
+ value = (void *) smtp_sslport;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* Mail Transfer Protocol open connection
+ * Accepts: network driver
+ * service host list
+ * port number
+ * service name
+ * SMTP open options
+ * Returns: SEND stream on success, NIL on failure
+ */
+
+SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options)
+{
+ SENDSTREAM *stream = NIL;
+ long reply;
+ char *s,tmp[MAILTMPLEN];
+ NETSTREAM *netstream;
+ NETMBX mb;
+ if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
+ /* maximum domain name is 64 characters */
+ else do if (strlen (*hostlist) < SMTPMAXDOMAIN) {
+ sprintf (tmp,"{%.1000s}",*hostlist);
+ if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") ||
+ mb.anoflag || mb.readonlyflag) {
+ sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
+ mm_log (tmp,ERROR);
+ }
+ else { /* light tryssl flag if requested */
+ mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL;
+ /* explicit port overrides all */
+ if (mb.port) port = mb.port;
+ /* else /submit overrides port argument */
+ else if (!compare_cstring (mb.service,"submit")) {
+ port = SUBMITTCPPORT; /* override port, use IANA name */
+ strcpy (mb.service,"submission");
+ }
+ /* else port argument overrides SMTP port */
+ else if (!port) port = smtp_port ? smtp_port : SMTPTCPPORT;
+ if (netstream = /* try to open ordinary connection */
+ net_open (&mb,dv,port,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*smtps",smtp_sslport ? smtp_sslport : SMTPSSLPORT)) {
+ stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0,
+ sizeof (SENDSTREAM));
+ stream->netstream = netstream;
+ stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (netstream) : mb.host);
+ stream->debug = (mb.dbgflag || (options & OP_DEBUG)) ? T : NIL;
+ if (options & SOP_SECURE) mb.secflag = T;
+ /* get name of local host to use */
+ s = compare_cstring ("localhost",mb.host) ?
+ net_localhost (netstream) : "localhost";
+
+ do reply = smtp_reply (stream);
+ while ((reply < 100) || (stream->reply[3] == '-'));
+ if (reply != SMTPGREET){/* get SMTP greeting */
+ sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ /* try EHLO first, then HELO */
+ else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) &&
+ ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) {
+ sprintf (tmp,"SMTP hello failure: %.80s",stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ else {
+ NETDRIVER *ssld =(NETDRIVER *)mail_parameters(NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters(NIL,GET_SSLSTART,NIL);
+ ESMTP.ok = T; /* ESMTP server, start TLS if present */
+ if (!dv && stls && ESMTP.service.starttls &&
+ !mb.sslflag && !mb.notlsflag &&
+ (smtp_send (stream,"STARTTLS",NIL) == SMTPGREET)) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ stream->netstream->dtb = ssld;
+ /* TLS started, negotiate it */
+ if (!(stream->netstream->stream = (*stls)
+ (stream->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT:NIL)))){
+ /* TLS negotiation failed after STARTTLS */
+ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",
+ mb.host);
+ mm_log (tmp,ERROR);
+ /* close without doing QUIT */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ stream = smtp_close (stream);
+ }
+ /* TLS OK, re-negotiate EHLO */
+ else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) {
+ sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s",
+ stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ else ESMTP.ok = T; /* TLS OK and EHLO successful */
+ }
+ else if (mb.tlsflag) {/* user specified /tls but can't do it */
+ sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+
+ /* remote name for authentication */
+ if (stream && ((mb.secflag || mb.user[0]))) {
+ if (ESMTP.auth) { /* use authenticator? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,
+ (long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),
+ NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream);
+ }
+ else { /* no available authenticators? */
+ sprintf (tmp,"%sSMTP authentication not available: %.80s",
+ mb.secflag ? "Secure " : "",mb.host);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ }
+ }
+ }
+ }
+ } while (!stream && *++hostlist);
+ if (stream) { /* set stream options if have a stream */
+ if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY |
+ SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) {
+ ESMTP.dsn.want = T;
+ if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T;
+ if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T;
+ if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T;
+ if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T;
+ }
+ if (options & SOP_8BITMIME) ESMTP.eightbit.want = T;
+ }
+ return stream;
+}
+
+/* SMTP authenticate
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
+{
+ unsigned long trial,auths;
+ char *lsterr = NIL;
+ char usr[MAILTMPLEN];
+ AUTHENTICATOR *at;
+ long ret = NIL;
+ for (auths = ESMTP.auth, stream->saslcancel = NIL;
+ !ret && stream->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* empty buffer */
+ if (stream->netstream) do {
+ if (lsterr) {
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ stream->saslcancel = NIL;
+ if (smtp_send (stream,"AUTH",at->name) == SMTPAUTHREADY) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) stream->sensitive = T;
+ if ((*at->client) (smtp_challenge,smtp_response,"smtp",mb,stream,
+ &trial,usr)) {
+ if (stream->replycode == SMTPAUTHED) {
+ ESMTP.auth = NIL; /* disable authenticators */
+ ret = LONGT;
+ }
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("SMTP Authentication cancelled",ERROR);
+ }
+ stream->sensitive = NIL;/* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) lsterr = cpystr (stream->reply);
+ } while (!ret && stream->netstream && trial &&
+ (trial < smtp_maxlogintrials));
+ }
+ if (lsterr) { /* previous authenticator failed? */
+ if (!stream->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to SMTP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ return ret; /* authentication failed */
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *smtp_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ if ((stream->replycode == SMTPAUTHREADY) &&
+ !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
+ strlen (stream->reply + 4),len))) {
+ sprintf (tmp,"SMTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T, always
+ */
+
+long smtp_response (void *s,char *response,unsigned long size)
+{
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ unsigned long i,j;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string */
+ i = smtp_send (stream,t,NIL);
+ fs_give ((void **) &t);
+ }
+ else i = smtp_send (stream,"",NIL);
+ }
+ else { /* abort requested */
+ i = smtp_send (stream,"*",NIL);
+ stream->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return LONGT;
+}
+
+/* Mail Transfer Protocol close connection
+ * Accepts: SEND stream
+ * Returns: NIL always
+ */
+
+SENDSTREAM *smtp_close (SENDSTREAM *stream)
+{
+ if (stream) { /* send "QUIT" */
+ if (stream->netstream) { /* do close actions if have netstream */
+ smtp_send (stream,"QUIT",NIL);
+ if (stream->netstream) /* could have been closed during "QUIT" */
+ net_close (stream->netstream);
+ }
+ /* clean up */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ if (ESMTP.dsn.envid) fs_give ((void **) &ESMTP.dsn.envid);
+ if (ESMTP.atrn.domains) fs_give ((void **) &ESMTP.atrn.domains);
+ fs_give ((void **) &stream);/* flush the stream */
+ }
+ return NIL;
+}
+
+/* Mail Transfer Protocol deliver mail
+ * Accepts: SEND stream
+ * delivery option (MAIL, SEND, SAML, SOML)
+ * message envelope
+ * message body
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *env,BODY *body)
+{
+ RFC822BUFFER buf;
+ char tmp[SENDBUFLEN+1];
+ long error = NIL;
+ long retry = NIL;
+ buf.f = smtp_soutr; /* initialize buffer */
+ buf.s = stream->netstream;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional null guard byte */
+ if (!(env->to || env->cc || env->bcc)) {
+ /* no recipients in request */
+ smtp_seterror (stream,SMTPHARDERROR,"No recipients specified");
+ return NIL;
+ }
+ do { /* make sure stream is in good shape */
+ smtp_send (stream,"RSET",NIL);
+ if (retry) { /* need to retry with authentication? */
+ NETMBX mb;
+ /* yes, build remote name for authentication */
+ sprintf (tmp,"{%.200s/smtp%s}<none>",
+ (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ ((long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (stream->netstream) :
+ net_host (stream->netstream)) :
+ stream->host,
+ (stream->netstream->dtb ==
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL)) ?
+ "/ssl" : "");
+ mail_valid_net_parse (tmp,&mb);
+ if (!smtp_auth (stream,&mb,tmp)) return NIL;
+ retry = NIL; /* no retry at this point */
+ }
+
+ strcpy (tmp,"FROM:<"); /* compose "MAIL FROM:<return-path>" */
+#ifdef RFC2821
+ if (env->return_path && env->return_path->host &&
+ !((strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) ||
+ (strlen (env->return_path->host) > SMTPMAXDOMAIN))) {
+ rfc822_cat (tmp,env->return_path->mailbox,NIL);
+ sprintf (tmp + strlen (tmp),"@%s",env->return_path->host);
+ }
+#else /* old code with A-D-L support */
+ if (env->return_path && env->return_path->host &&
+ !((env->return_path->adl &&
+ (strlen (env->return_path->adl) > SMTPMAXPATH)) ||
+ (strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) ||
+ (strlen (env->return_path->host) > SMTPMAXDOMAIN)))
+ rfc822_address (tmp,env->return_path);
+#endif
+ strcat (tmp,">");
+ if (ESMTP.ok) {
+ if (ESMTP.eightbit.ok && ESMTP.eightbit.want)
+ strcat (tmp," BODY=8BITMIME");
+ if (ESMTP.dsn.ok && ESMTP.dsn.want) {
+ strcat (tmp,ESMTP.dsn.full ? " RET=FULL" : " RET=HDRS");
+ if (ESMTP.dsn.envid)
+ sprintf (tmp + strlen (tmp)," ENVID=%.100s",ESMTP.dsn.envid);
+ }
+ }
+ /* send "MAIL FROM" command */
+ switch (smtp_send (stream,type,tmp)) {
+ case SMTPUNAVAIL: /* mailbox unavailable? */
+ case SMTPWANTAUTH: /* wants authentication? */
+ case SMTPWANTAUTH2:
+ if (ESMTP.auth) retry = T;/* yes, retry with authentication */
+ case SMTPOK: /* looks good */
+ break;
+ default: /* other failure */
+ return NIL;
+ }
+ /* negotiate the recipients */
+ if (!retry && env->to) retry = smtp_rcpt (stream,env->to,&error);
+ if (!retry && env->cc) retry = smtp_rcpt (stream,env->cc,&error);
+ if (!retry && env->bcc) retry = smtp_rcpt (stream,env->bcc,&error);
+ if (!retry && error) { /* any recipients failed? */
+ smtp_send (stream,"RSET",NIL);
+ smtp_seterror (stream,SMTPHARDERROR,"One or more recipients failed");
+ return NIL;
+ }
+ } while (retry);
+ /* negotiate data command */
+ if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL;
+ /* send message data */
+ if (!rfc822_output_full (&buf,env,body,
+ ESMTP.eightbit.ok && ESMTP.eightbit.want)) {
+ smtp_fake (stream,"SMTP connection broken (message data)");
+ return NIL; /* can't do much else here */
+ }
+ /* send trailing dot */
+ return (smtp_send (stream,".",NIL) == SMTPOK) ? LONGT : NIL;
+}
+
+/* Simple Mail Transfer Protocol send VERBose
+ * Accepts: SMTP stream
+ * Returns: T if successful, else NIL
+ *
+ * Descriptive text formerly in [al]pine sources:
+ * At worst, this command may cause the SMTP connection to get nuked. Modern
+ * sendmail's recognize it, and possibly other SMTP implementations (the "ON"
+ * arg is for PMDF). What's more, if it works, the reply code and accompanying
+ * text may vary from server to server.
+ */
+
+long smtp_verbose (SENDSTREAM *stream)
+{
+ /* accept any 2xx reply code */
+ return ((smtp_send (stream,"VERB","ON") / (long) 100) == 2) ? LONGT : NIL;
+}
+
+/* Internal routines */
+
+
+/* Simple Mail Transfer Protocol send recipient
+ * Accepts: SMTP stream
+ * address list
+ * pointer to error flag
+ * Returns: T if should retry, else NIL
+ */
+
+long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error)
+{
+ char *s,tmp[2*MAILTMPLEN],orcpt[MAILTMPLEN];
+ while (adr) { /* for each address on the list */
+ /* clear any former error */
+ if (adr->error) fs_give ((void **) &adr->error);
+ if (adr->host) { /* ignore group syntax */
+ /* enforce SMTP limits to protect the buffer */
+ if (strlen (adr->mailbox) > MAXLOCALPART) {
+ adr->error = cpystr ("501 Recipient name too long");
+ *error = T;
+ }
+ else if ((strlen (adr->host) > SMTPMAXDOMAIN)) {
+ adr->error = cpystr ("501 Recipient domain too long");
+ *error = T;
+ }
+#ifndef RFC2821 /* old code with A-D-L support */
+ else if (adr->adl && (strlen (adr->adl) > SMTPMAXPATH)) {
+ adr->error = cpystr ("501 Path too long");
+ *error = T;
+ }
+#endif
+
+ else {
+ strcpy (tmp,"TO:<"); /* compose "RCPT TO:<return-path>" */
+#ifdef RFC2821
+ rfc822_cat (tmp,adr->mailbox,NIL);
+ sprintf (tmp + strlen (tmp),"@%s>",adr->host);
+#else /* old code with A-D-L support */
+ rfc822_address (tmp,adr);
+ strcat (tmp,">");
+#endif
+ /* want notifications */
+ if (ESMTP.ok && ESMTP.dsn.ok && ESMTP.dsn.want) {
+ /* yes, start with prefix */
+ strcat (tmp," NOTIFY=");
+ s = tmp + strlen (tmp);
+ if (ESMTP.dsn.notify.failure) strcat (s,"FAILURE,");
+ if (ESMTP.dsn.notify.delay) strcat (s,"DELAY,");
+ if (ESMTP.dsn.notify.success) strcat (s,"SUCCESS,");
+ /* tie off last comma */
+ if (*s) s[strlen (s) - 1] = '\0';
+ else strcat (tmp,"NEVER");
+ if (adr->orcpt.addr) {
+ sprintf (orcpt,"%.498s;%.498s",
+ adr->orcpt.type ? adr->orcpt.type : "rfc822",
+ adr->orcpt.addr);
+ sprintf (tmp + strlen (tmp)," ORCPT=%.500s",orcpt);
+ }
+ }
+ switch (smtp_send (stream,"RCPT",tmp)) {
+ case SMTPOK: /* looks good */
+ break;
+ case SMTPUNAVAIL: /* mailbox unavailable? */
+ case SMTPWANTAUTH: /* wants authentication? */
+ case SMTPWANTAUTH2:
+ if (ESMTP.auth) return T;
+ default: /* other failure */
+ *error = T; /* note that an error occurred */
+ adr->error = cpystr (stream->reply);
+ }
+ }
+ }
+ adr = adr->next; /* do any subsequent recipients */
+ }
+ return NIL; /* no retry called for */
+}
+
+/* Simple Mail Transfer Protocol send command
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long smtp_send (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0)
+ + 3);
+ /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,stream->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ if (stream->netstream && net_soutr (stream->netstream,s)) {
+ do stream->replycode = smtp_reply (stream);
+ while ((stream->replycode < 100) || (stream->reply[3] == '-'));
+ ret = stream->replycode;
+ }
+ else ret = smtp_fake (stream,"SMTP connection broken (command)");
+ fs_give ((void **) &s);
+ return ret;
+}
+
+
+/* Simple Mail Transfer Protocol get reply
+ * Accepts: SMTP stream
+ * Returns: reply code
+ */
+
+long smtp_reply (SENDSTREAM *stream)
+{
+ smtpverbose_t pv = (smtpverbose_t) mail_parameters (NIL,GET_SMTPVERBOSE,NIL);
+ long reply;
+ /* flush old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* get reply */
+ if (stream->netstream && (stream->reply = net_getline (stream->netstream))) {
+ if (stream->debug) mm_dlog (stream->reply);
+ /* return response code */
+ reply = atol (stream->reply);
+ if (pv && (reply < 100)) (*pv) (stream->reply);
+ }
+ else reply = smtp_fake (stream,"SMTP connection broken (reply)");
+ return reply;
+}
+
+/* Simple Mail Transfer Protocol send EHLO
+ * Accepts: SMTP stream
+ * host name to use in EHLO
+ * NETMBX structure
+ * Returns: reply code
+ */
+
+long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb)
+{
+ unsigned long i,j;
+ long flags = (mb->secflag ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL);
+ char *s,*t,*r,tmp[MAILTMPLEN];
+ /* clear ESMTP data */
+ memset (&ESMTP,0,sizeof (ESMTP));
+ if (mb->loser) return 500; /* never do EHLO if a loser */
+ sprintf (tmp,"EHLO %s",host); /* build the complete command */
+ if (stream->debug) mm_dlog (tmp);
+ strcat (tmp,"\015\012");
+ /* send the command */
+ if (!net_soutr (stream->netstream,tmp))
+ return smtp_fake (stream,"SMTP connection broken (EHLO)");
+ /* got an OK reply? */
+ do if ((i = smtp_reply (stream)) == SMTPOK) {
+ /* hack for AUTH= */
+ if (stream->reply[4] && stream->reply[5] && stream->reply[6] &&
+ stream->reply[7] && (stream->reply[8] == '=')) stream->reply[8] = ' ';
+ /* get option code */
+ if (!(s = strtok_r (stream->reply+4," ",&r)));
+ /* have option, does it have a value */
+ else if ((t = strtok_r (NIL," ",&r)) && *t) {
+ /* EHLO options which take arguments */
+ if (!compare_cstring (s,"SIZE")) {
+ if (isdigit (*t)) ESMTP.size.limit = strtoul (t,&t,10);
+ ESMTP.size.ok = T;
+ }
+ else if (!compare_cstring (s,"DELIVERBY")) {
+ if (isdigit (*t)) ESMTP.deliverby.minby = strtoul (t,&t,10);
+ ESMTP.deliverby.ok = T;
+ }
+ else if (!compare_cstring (s,"ATRN")) {
+ ESMTP.atrn.domains = cpystr (t);
+ ESMTP.atrn.ok = T;
+ }
+ else if (!compare_cstring (s,"AUTH"))
+ do if ((j = mail_lookup_auth_name (t,flags)) &&
+ (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j);
+ while ((t = strtok_r (NIL," ",&r)) && *t);
+ }
+ /* EHLO options which do not take arguments */
+ else if (!compare_cstring (s,"SIZE")) ESMTP.size.ok = T;
+ else if (!compare_cstring (s,"8BITMIME")) ESMTP.eightbit.ok = T;
+ else if (!compare_cstring (s,"DSN")) ESMTP.dsn.ok = T;
+ else if (!compare_cstring (s,"ATRN")) ESMTP.atrn.ok = T;
+ else if (!compare_cstring (s,"SEND")) ESMTP.service.send = T;
+ else if (!compare_cstring (s,"SOML")) ESMTP.service.soml = T;
+ else if (!compare_cstring (s,"SAML")) ESMTP.service.saml = T;
+ else if (!compare_cstring (s,"EXPN")) ESMTP.service.expn = T;
+ else if (!compare_cstring (s,"HELP")) ESMTP.service.help = T;
+ else if (!compare_cstring (s,"TURN")) ESMTP.service.turn = T;
+ else if (!compare_cstring (s,"ETRN")) ESMTP.service.etrn = T;
+ else if (!compare_cstring (s,"STARTTLS")) ESMTP.service.starttls = T;
+ else if (!compare_cstring (s,"RELAY")) ESMTP.service.relay = T;
+ else if (!compare_cstring (s,"PIPELINING")) ESMTP.service.pipe = T;
+ else if (!compare_cstring (s,"ENHANCEDSTATUSCODES"))
+ ESMTP.service.ensc = T;
+ else if (!compare_cstring (s,"BINARYMIME")) ESMTP.service.bmime = T;
+ else if (!compare_cstring (s,"CHUNKING")) ESMTP.service.chunk = T;
+ }
+ while ((i < 100) || (stream->reply[3] == '-'));
+ /* disable LOGIN if PLAIN also advertised */
+ if ((j = mail_lookup_auth_name ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) &&
+ (ESMTP.auth & (1 << j)) &&
+ (j = mail_lookup_auth_name ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS))
+ ESMTP.auth &= ~(1 << j);
+ return i; /* return the response code */
+}
+
+/* Simple Mail Transfer Protocol set fake error and abort
+ * Accepts: SMTP stream
+ * error text
+ * Returns: SMTPSOFTFATAL, always
+ */
+
+long smtp_fake (SENDSTREAM *stream,char *text)
+{
+ if (stream->netstream) { /* close net connection if still open */
+ net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ /* set last error */
+ return smtp_seterror (stream,SMTPSOFTFATAL,text);
+}
+
+
+/* Simple Mail Transfer Protocol set error
+ * Accepts: SMTP stream
+ * SMTP error code
+ * error text
+ * Returns: error code
+ */
+
+static long smtp_seterror (SENDSTREAM *stream,long code,char *text)
+{
+ /* flush any old reply */
+ if (stream->reply ) fs_give ((void **) &stream->reply);
+ /* set up pseudo-reply string */
+ stream->reply = (char *) fs_get (20+strlen (text));
+ sprintf (stream->reply,"%ld %s",code,text);
+ return code; /* return error code */
+}
+
+
+/* Simple Mail Transfer Protocol filter mail
+ * Accepts: stream
+ * string
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_soutr (void *stream,char *s)
+{
+ char c,*t;
+ /* "." on first line */
+ if (s[0] == '.') net_sout (stream,".",1);
+ /* find lines beginning with a "." */
+ while (t = strstr (s,"\015\012.")) {
+ c = *(t += 3); /* remember next character after "." */
+ *t = '\0'; /* tie off string */
+ /* output prefix */
+ if (!net_sout (stream,s,t-s)) return NIL;
+ *t = c; /* restore delimiter */
+ s = t - 1; /* push pointer up to the "." */
+ }
+ /* output remainder of text */
+ return *s ? net_soutr (stream,s) : T;
+}
diff --git a/imap/src/c-client/smtp.h b/imap/src/c-client/smtp.h
new file mode 100644
index 00000000..4be82db4
--- /dev/null
+++ b/imap/src/c-client/smtp.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Simple Mail Transfer Protocol (SMTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 15 August 2007
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+/* Constants (should be in smtp.c) */
+
+#define SMTPTCPPORT (long) 25 /* assigned TCP contact port */
+#define SUBMITTCPPORT (long) 587/* assigned TCP contact port */
+
+
+/* SMTP open options
+ * For compatibility with the past, SOP_DEBUG must always be 1.
+ */
+
+#define SOP_DEBUG (long) 1 /* debug protocol negotiations */
+#define SOP_DSN (long) 2 /* DSN requested */
+ /* DSN notification, none set mean NEVER */
+#define SOP_DSN_NOTIFY_FAILURE (long) 4
+#define SOP_DSN_NOTIFY_DELAY (long) 8
+#define SOP_DSN_NOTIFY_SUCCESS (long) 16
+ /* DSN return full msg vs. header */
+
+#define SOP_DSN_RETURN_FULL (long) 32
+#define SOP_8BITMIME (long) 64 /* 8-bit MIME requested */
+#define SOP_SECURE (long) 256 /* don't do non-secure authentication */
+#define SOP_TRYSSL (long) 512 /* try SSL first */
+#define SOP_TRYALT SOP_TRYSSL /* old name */
+ /* reserved for application use */
+#define SOP_RESERVED (unsigned long) 0xff000000
+
+
+/* Compatibility support names */
+
+#define smtp_open(hostlist,options) \
+ smtp_open_full (NIL,hostlist,"smtp",NIL,options)
+
+
+/* Function prototypes */
+
+void *smtp_parameters (long function,void *value);
+SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options);
+SENDSTREAM *smtp_close (SENDSTREAM *stream);
+long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *msg,BODY *body);
+long smtp_verbose (SENDSTREAM *stream);
diff --git a/imap/src/c-client/sslio.h b/imap/src/c-client/sslio.h
new file mode 100644
index 00000000..8063f3fc
--- /dev/null
+++ b/imap/src/c-client/sslio.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* SSL driver */
+
+struct ssl_driver { /* must parallel NETDRIVER in mail.h */
+ SSLSTREAM *(*open) (char *host,char *service,unsigned long port);
+ SSLSTREAM *(*aopen) (NETMBX *mb,char *service,char *usrbuf);
+ char *(*getline) (SSLSTREAM *stream);
+ long (*getbuffer) (SSLSTREAM *stream,unsigned long size,char *buffer);
+ long (*soutr) (SSLSTREAM *stream,char *string);
+ long (*sout) (SSLSTREAM *stream,char *string,unsigned long size);
+ void (*close) (SSLSTREAM *stream);
+ char *(*host) (SSLSTREAM *stream);
+ char *(*remotehost) (SSLSTREAM *stream);
+ unsigned long (*port) (SSLSTREAM *stream);
+ char *(*localhost) (SSLSTREAM *stream);
+};
+
+
+/* SSL stdio stream */
+
+typedef struct ssl_stdiostream {
+ SSLSTREAM *sslstream; /* SSL stream */
+ int octr; /* output counter */
+ char *optr; /* output pointer */
+ char obuf[SSLBUFLEN]; /* output buffer */
+} SSLSTDIOSTREAM;
+
+
+/* Function prototypes */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port);
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf);
+char *ssl_getline (SSLSTREAM *stream);
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer);
+long ssl_getdata (SSLSTREAM *stream);
+long ssl_soutr (SSLSTREAM *stream,char *string);
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size);
+void ssl_close (SSLSTREAM *stream);
+char *ssl_host (SSLSTREAM *stream);
+char *ssl_remotehost (SSLSTREAM *stream);
+unsigned long ssl_port (SSLSTREAM *stream);
+char *ssl_localhost (SSLSTREAM *stream);
+long ssl_server_input_wait (long seconds);
diff --git a/imap/src/c-client/tcp.h b/imap/src/c-client/tcp.h
new file mode 100644
index 00000000..b9f095a8
--- /dev/null
+++ b/imap/src/c-client/tcp.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 January 2007
+ */
+
+
+/* Dummy definition overridden by TCP routines */
+
+#ifndef TCPSTREAM
+#define TCPSTREAM void
+#endif
+
+
+/* Function prototypes */
+
+void *tcp_parameters (long function,void *value);
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port);
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf);
+char *tcp_getline (TCPSTREAM *stream);
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer);
+long tcp_getdata (TCPSTREAM *stream);
+long tcp_soutr (TCPSTREAM *stream,char *string);
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size);
+void tcp_close (TCPSTREAM *stream);
+char *tcp_host (TCPSTREAM *stream);
+char *tcp_remotehost (TCPSTREAM *stream);
+unsigned long tcp_port (TCPSTREAM *stream);
+char *tcp_localhost (TCPSTREAM *stream);
+char *tcp_clientaddr (void);
+char *tcp_clienthost (void);
+long tcp_clientport (void);
+char *tcp_serveraddr (void);
+char *tcp_serverhost (void);
+long tcp_serverport (void);
+char *tcp_canonical (char *name);
+long tcp_isclienthost (char *host);
diff --git a/imap/src/c-client/utf8.c b/imap/src/c-client/utf8.c
new file mode 100644
index 00000000..8fda7ffd
--- /dev/null
+++ b/imap/src/c-client/utf8.c
@@ -0,0 +1,2554 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 17 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* *** IMPORTANT ***
+ *
+ * There is a very important difference between "character set" and "charset",
+ * and the comments in this file reflect these differences. A "character set"
+ * (also known as "coded character set") is a mapping between codepoints and
+ * characters. A "charset" is as defined in MIME, and incorporates one or more
+ * coded character sets in a character encoding scheme. See RFC 2130 for more
+ * details.
+ */
+
+
+/* Character set conversion tables */
+
+#include "iso_8859.c" /* 8-bit single-byte coded graphic */
+#include "koi8_r.c" /* Cyrillic - Russia */
+#include "koi8_u.c" /* Cyrillic - Ukraine */
+#include "tis_620.c" /* Thai */
+#include "viscii.c" /* Vietnamese */
+#include "windows.c" /* Windows */
+#include "ibm.c" /* IBM */
+#include "gb_2312.c" /* Chinese (PRC) - simplified */
+#include "gb_12345.c" /* Chinese (PRC) - traditional */
+#include "jis_0208.c" /* Japanese - basic */
+#include "jis_0212.c" /* Japanese - supplementary */
+#include "ksc_5601.c" /* Korean */
+#include "big5.c" /* Taiwanese (ROC) - industrial standard */
+#include "cns11643.c" /* Taiwanese (ROC) - national standard */
+
+
+#include "widths.c" /* Unicode character widths */
+#include "tmap.c" /* Unicode titlecase mapping */
+#include "decomtab.c" /* Unicode decomposions */
+
+/* EUC parameters */
+
+#ifdef GBTOUNICODE /* PRC simplified Chinese */
+static const struct utf8_eucparam gb_param = {
+ BASE_GB2312_KU,BASE_GB2312_TEN,MAX_GB2312_KU,MAX_GB2312_TEN,
+ (void *) gb2312tab};
+#endif
+
+
+#ifdef GB12345TOUNICODE /* PRC traditional Chinese */
+static const struct utf8_eucparam gbt_param = {
+ BASE_GB12345_KU,BASE_GB12345_TEN,MAX_GB12345_KU,MAX_GB12345_TEN,
+ (void *) gb12345tab};
+#endif
+
+
+#ifdef BIG5TOUNICODE /* ROC traditional Chinese */
+static const struct utf8_eucparam big5_param[] = {
+ {BASE_BIG5_KU,BASE_BIG5_TEN_0,MAX_BIG5_KU,MAX_BIG5_TEN_0,(void *) big5tab},
+ {BASE_BIG5_KU,BASE_BIG5_TEN_1,MAX_BIG5_KU,MAX_BIG5_TEN_1,NIL}
+};
+#endif
+
+
+#ifdef JISTOUNICODE /* Japanese */
+static const struct utf8_eucparam jis_param[] = {
+ {BASE_JIS0208_KU,BASE_JIS0208_TEN,MAX_JIS0208_KU,MAX_JIS0208_TEN,
+ (void *) jis0208tab},
+ {MIN_KANA_8,0,MAX_KANA_8,0,(void *) KANA_8},
+#ifdef JIS0212TOUNICODE /* Japanese extended */
+ {BASE_JIS0212_KU,BASE_JIS0212_TEN,MAX_JIS0212_KU,MAX_JIS0212_TEN,
+ (void *) jis0212tab}
+#else
+ {0,0,0,0,NIL}
+#endif
+};
+#endif
+
+
+#ifdef KSCTOUNICODE /* Korean */
+static const struct utf8_eucparam ksc_param = {
+ BASE_KSC5601_KU,BASE_KSC5601_TEN,MAX_KSC5601_KU,MAX_KSC5601_TEN,
+ (void *) ksc5601tab};
+#endif
+
+/* List of supported charsets */
+
+static const CHARSET utf8_csvalid[] = {
+ {"US-ASCII",CT_ASCII,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,NIL,NIL},
+ {"UTF-8",CT_UTF8,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_UNICODE,NIL},
+ {"UTF-7",CT_UTF7,CF_PRIMARY | CF_POSTING | CF_UNSUPRT,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"ISO-8859-1",CT_1BYTE0,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_LATIN_1,NIL},
+ {"ISO-8859-2",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_2tab,SC_LATIN_2,NIL},
+ {"ISO-8859-3",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_3tab,SC_LATIN_3,NIL},
+ {"ISO-8859-4",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_4tab,SC_LATIN_4,NIL},
+ {"ISO-8859-5",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_5tab,SC_CYRILLIC,"KOI8-R"},
+ {"ISO-8859-6",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_6tab,SC_ARABIC,NIL},
+ {"ISO-8859-7",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_7tab,SC_GREEK,NIL},
+ {"ISO-8859-8",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_8tab,SC_HEBREW,NIL},
+ {"ISO-8859-9",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_9tab,SC_LATIN_5,NIL},
+ {"ISO-8859-10",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_10tab,SC_LATIN_6,NIL},
+ {"ISO-8859-11",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_11tab,SC_THAI,NIL},
+#if 0 /* ISO 8859-12 reserved for ISCII(?) */
+ {"ISO-8859-12",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_12tab,NIL,NIL},
+#endif
+ {"ISO-8859-13",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_13tab,SC_LATIN_7,NIL},
+ {"ISO-8859-14",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_14tab,SC_LATIN_8,NIL},
+ {"ISO-8859-15",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_15tab,SC_LATIN_9,NIL},
+ {"ISO-8859-16",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_16tab,SC_LATIN_10,NIL},
+ {"KOI8-R",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) koi8rtab,SC_CYRILLIC,NIL},
+ {"KOI8-U",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) koi8utab,SC_CYRILLIC | SC_UKRANIAN,NIL},
+ {"KOI8-RU",CT_1BYTE,CF_DISPLAY,
+ (void *) koi8utab,SC_CYRILLIC | SC_UKRANIAN,"KOI8-U"},
+ {"TIS-620",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) tis620tab,SC_THAI,"ISO-8859-11"},
+ {"VISCII",CT_1BYTE8,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) visciitab,SC_VIETNAMESE,NIL},
+
+#ifdef GBTOUNICODE
+ {"GBK",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,NIL},
+ {"GB2312",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+ {"CN-GB",CT_DBYTE,CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+#ifdef CNS1TOUNICODE
+ {"ISO-2022-CN",CT_2022,CF_PRIMARY | CF_UNSUPRT,
+ NIL,SC_CHINESE_SIMPLIFIED | SC_CHINESE_TRADITIONAL,
+ NIL},
+#endif
+#endif
+#ifdef GB12345TOUNICODE
+ {"CN-GB-12345",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &gbt_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+#endif
+#ifdef BIG5TOUNICODE
+ {"BIG5",CT_DBYTE2,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,NIL},
+ {"CN-BIG5",CT_DBYTE2,CF_DISPLAY,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+ {"BIG-5",CT_DBYTE2,CF_DISPLAY,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+#endif
+#ifdef JISTOUNICODE
+ {"ISO-2022-JP",CT_2022,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_JAPANESE,NIL},
+ {"EUC-JP",CT_EUC,CF_PRIMARY | CF_DISPLAY,
+ (void *) jis_param,SC_JAPANESE,"ISO-2022-JP"},
+ {"SHIFT_JIS",CT_SJIS,CF_PRIMARY | CF_DISPLAY,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+ {"SHIFT-JIS",CT_SJIS,CF_PRIMARY | CF_DISPLAY,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+#ifdef JIS0212TOUNICODE
+ {"ISO-2022-JP-1",CT_2022,CF_UNSUPRT,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+#ifdef GBTOUNICODE
+#ifdef KSCTOUNICODE
+ {"ISO-2022-JP-2",CT_2022,CF_UNSUPRT,
+ NIL,
+ SC_LATIN_1 | SC_LATIN_2 | SC_LATIN_3 | SC_LATIN_4 | SC_LATIN_5 |
+ SC_LATIN_6 | SC_LATIN_7 | SC_LATIN_8 | SC_LATIN_9 | SC_LATIN_10 |
+ SC_ARABIC | SC_CYRILLIC | SC_GREEK | SC_HEBREW | SC_THAI |
+ SC_VIETNAMESE | SC_CHINESE_SIMPLIFIED | SC_JAPANESE | SC_KOREAN
+#ifdef CNS1TOUNICODE
+ | SC_CHINESE_TRADITIONAL
+#endif
+ ,"UTF-8"},
+#endif
+#endif
+#endif
+#endif
+
+#ifdef KSCTOUNICODE
+ {"ISO-2022-KR",CT_2022,CF_PRIMARY | CF_DISPLAY | CF_UNSUPRT,
+ NIL,SC_KOREAN,"EUC-KR"},
+ {"EUC-KR",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &ksc_param,SC_KOREAN,NIL},
+ {"KSC5601",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KSC_5601",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1987",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1989",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1992",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1997",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+#endif
+
+ /* deep sigh */
+ {"WINDOWS-874",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_874tab,SC_THAI,"ISO-8859-11"},
+ {"CP874",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_874tab,SC_THAI,"ISO-8859-11"},
+#ifdef GBTOUNICODE
+ {"WINDOWS-936",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+ {"CP936",CT_DBYTE,CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+#endif
+#ifdef KSCTOUNICODE
+ {"WINDOWS-949",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"CP949",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"X-WINDOWS-949",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+#endif
+ {"WINDOWS-1250",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1250tab,SC_LATIN_2,"ISO-8859-2"},
+ {"CP1250",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1250tab,SC_LATIN_2,"ISO-8859-2"},
+ {"WINDOWS-1251",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) windows_1251tab,SC_CYRILLIC,"KOI8-R"},
+ {"CP1251",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1251tab,SC_CYRILLIC,"KOI8-R"},
+ {"WINDOWS-1252",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1252tab,SC_LATIN_1,"ISO-8859-1"},
+ {"CP1252",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1252tab,SC_LATIN_1,"ISO-8859-1"},
+ {"WINDOWS-1253",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1253tab,SC_GREEK,"ISO-8859-7"},
+ {"CP1253",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1253tab,SC_GREEK,"ISO-8859-7"},
+ {"WINDOWS-1254",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1254tab,SC_LATIN_5,"ISO-8859-9"},
+ {"CP1254",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1254tab,SC_LATIN_5,"ISO-8859-9"},
+ {"WINDOWS-1255",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1255tab,SC_HEBREW,"ISO-8859-8"},
+ {"CP1255",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1255tab,SC_HEBREW,"ISO-8859-8"},
+ {"WINDOWS-1256",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1256tab,SC_ARABIC,"ISO-8859-6"},
+ {"CP1256",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1256tab,SC_ARABIC,"ISO-8859-6"},
+ {"WINDOWS-1257",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1257tab,SC_LATIN_7,"ISO-8859-13"},
+ {"CP1257",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1257tab,SC_LATIN_7,"ISO-8859-13"},
+ {"WINDOWS-1258",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1258tab,SC_VIETNAMESE,"VISCII"},
+ {"CP1258",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1258tab,SC_VIETNAMESE,"VISCII"},
+
+ /* deeper sigh */
+ {"IBM367",CT_ASCII,CF_PRIMARY | CF_DISPLAY,
+ NIL,NIL,"US-ASCII"},
+ {"IBM437",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_437tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM737",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_737tab,SC_GREEK,"ISO-8859-7"},
+ {"IBM775",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_775tab,SC_LATIN_7,"ISO-8859-13"},
+ {"IBM850",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_850tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM852",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_852tab,SC_LATIN_2,"ISO-8859-2"},
+ {"IBM855",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_855tab,SC_CYRILLIC,"ISO-8859-5"},
+ {"IBM857",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_857tab,SC_LATIN_5,"ISO-8859-9"},
+ {"IBM860",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_860tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM861",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_861tab,SC_LATIN_6,"ISO-8859-10"},
+ {"IBM862",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_862tab,SC_HEBREW,"ISO-8859-8"},
+ {"IBM863",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_863tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM864",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_864tab,SC_ARABIC,"ISO-8859-6"},
+ {"IBM865",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_865tab,SC_LATIN_6,"ISO-8859-10"},
+ {"IBM866",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_866tab,SC_CYRILLIC,"KOI8-R"},
+ {"IBM869",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_869tab,SC_GREEK,"ISO-8859-7"},
+ {"IBM874",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_874tab,SC_THAI,"ISO-8859-11"},
+ /* deepest sigh */
+ {"ANSI_X3.4-1968",CT_ASCII,CF_DISPLAY,
+ NIL,NIL,"US-ASCII"},
+ {"UNICODE-1-1-UTF-7",CT_UTF7,CF_UNSUPRT,
+ NIL,SC_UNICODE,"UTF-8"},
+ /* these should never appear in email */
+ {"UCS-2",CT_UCS2,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"UCS-4",CT_UCS4,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"UTF-16",CT_UTF16,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ NIL
+};
+
+/* Non-Unicode Script table */
+
+static const SCRIPT utf8_scvalid[] = {
+ {"Arabic",NIL,SC_ARABIC},
+ {"Chinese Simplified","China, Singapore",SC_CHINESE_SIMPLIFIED},
+ {"Chinese Traditional","Taiwan, Hong Kong, Macao",SC_CHINESE_TRADITIONAL},
+ {"Cyrillic",NIL,SC_CYRILLIC},
+ {"Cyrillic Ukranian",NIL,SC_UKRANIAN},
+ {"Greek",NIL,SC_GREEK},
+ {"Hebrew",NIL,SC_HEBREW},
+ {"Japanese",NIL,SC_JAPANESE},
+ {"Korean",NIL,SC_KOREAN},
+ {"Latin-1","Western Europe",SC_LATIN_1},
+ {"Latin-2","Eastern Europe",SC_LATIN_2},
+ {"Latin-3","Southern Europe",SC_LATIN_3},
+ {"Latin-4","Northern Europe",SC_LATIN_4},
+ {"Latin-5","Turkish",SC_LATIN_5},
+ {"Latin-6","Nordic",SC_LATIN_6},
+ {"Latin-7","Baltic",SC_LATIN_7},
+ {"Latin-8","Celtic",SC_LATIN_8},
+ {"Latin-9","Euro",SC_LATIN_9},
+ {"Latin-10","Balkan",SC_LATIN_10},
+ {"Thai",NIL,SC_THAI},
+ {"Vietnamese",NIL,SC_VIETNAMESE},
+ NIL
+};
+
+/* Look up script name or return entire table
+ * Accepts: script name or NIL
+ * Returns: pointer to script table entry or NIL if unknown
+ */
+
+SCRIPT *utf8_script (char *script)
+{
+ unsigned long i;
+ if (!script) return (SCRIPT *) &utf8_scvalid[0];
+ else if (*script && (strlen (script) < 128))
+ for (i = 0; utf8_scvalid[i].name; i++)
+ if (!compare_cstring (script,utf8_scvalid[i].name))
+ return (SCRIPT *) &utf8_scvalid[i];
+ return NIL; /* failed */
+}
+
+
+/* Look up charset name or return entire table
+ * Accepts: charset name or NIL
+ * Returns: charset table entry or NIL if unknown
+ */
+
+const CHARSET *utf8_charset (char *charset)
+{
+ unsigned long i;
+ if (!charset) return (CHARSET *) &utf8_csvalid[0];
+ else if (*charset && (strlen (charset) < 128))
+ for (i = 0; utf8_csvalid[i].name; i++)
+ if (!compare_cstring (charset,utf8_csvalid[i].name))
+ return (CHARSET *) &utf8_csvalid[i];
+ return NIL; /* failed */
+}
+
+/* Validate charset and generate error message if invalid
+ * Accepts: bad character set
+ * Returns: NIL if good charset, else error message string
+ */
+
+#define BADCSS "[BADCHARSET ("
+#define BADCSE ")] Unknown charset: "
+
+char *utf8_badcharset (char *charset)
+{
+ char *msg = NIL;
+ if (!utf8_charset (charset)) {
+ char *s,*t;
+ unsigned long i,j;
+ /* calculate size of header, trailer, and bad
+ * charset plus charset names */
+ for (i = 0, j = sizeof (BADCSS) + sizeof (BADCSE) + strlen (charset) - 2;
+ utf8_csvalid[i].name; i++)
+ j += strlen (utf8_csvalid[i].name) + 1;
+ /* not built right */
+ if (!i) fatal ("No valid charsets!");
+ /* header */
+ for (s = msg = (char *) fs_get (j), t = BADCSS; *t; *s++ = *t++);
+ /* each charset */
+ for (i = 0; utf8_csvalid[i].name; *s++ = ' ', i++)
+ for (t = utf8_csvalid[i].name; *t; *s++ = *t++);
+ /* back over last space, trailer */
+ for (t = BADCSE, --s; *t; *s++ = *t++);
+ /* finally bogus charset */
+ for (t = charset; *t; *s++ = *t++);
+ *s++ = '\0'; /* finally tie off string */
+ if (s != (msg + j)) fatal ("charset msg botch");
+ }
+ return msg;
+}
+
+/* Convert charset labelled sized text to UTF-8
+ * Accepts: source sized text
+ * charset
+ * pointer to returned sized text if non-NIL
+ * flags
+ * Returns: T if successful, NIL if failure
+ */
+
+long utf8_text (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,long flags)
+{
+ ucs4cn_t cv = (flags & U8T_CASECANON) ? ucs4_titlecase : NIL;
+ ucs4de_t de = (flags & U8T_DECOMPOSE) ? ucs4_decompose_recursive : NIL;
+ const CHARSET *cs = (charset && *charset) ?
+ utf8_charset (charset) : utf8_infercharset (text);
+ if (cs) return (text && ret) ? utf8_text_cs (text,cs,ret,cv,de) : LONGT;
+ if (ret) { /* no conversion possible */
+ ret->data = text->data; /* so return source */
+ ret->size = text->size;
+ }
+ return NIL; /* failure */
+}
+
+
+/* Operations used in converting data */
+
+#define UTF8_COUNT_BMP(count,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do count += UTF8_SIZE_BMP(c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_WRITE_BMP(b,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do UTF8_PUT_BMP (b,c) \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_COUNT(count,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do count += utf8_size (c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_WRITE(b,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do b = utf8_put (b,c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+/* Convert sized text to UTF-8 given CHARSET block
+ * Accepts: source sized text
+ * CHARSET block
+ * pointer to returned sized text
+ * canonicalization function
+ * decomposition function
+ * Returns: T if successful, NIL if failure
+ */
+
+long utf8_text_cs (SIZEDTEXT *text,const CHARSET *cs,SIZEDTEXT *ret,
+ ucs4cn_t cv,ucs4de_t de)
+{
+ ret->data = text->data; /* default to source */
+ ret->size = text->size;
+ switch (cs->type) { /* convert if type known */
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_UTF8: /* variable UTF-8 encoded Unicode no table */
+ if (cv || de) utf8_text_utf8 (text,ret,cv,de);
+ break;
+ case CT_1BYTE0: /* 1 byte no table */
+ utf8_text_1byte0 (text,ret,cv,de);
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ utf8_text_1byte (text,ret,cs->tab,cv,de);
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ utf8_text_1byte8 (text,ret,cs->tab,cv,de);
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ utf8_text_euc (text,ret,cs->tab,cv,de);
+ break;
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ utf8_text_dbyte (text,ret,cs->tab,cv,de);
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ utf8_text_dbyte2 (text,ret,cs->tab,cv,de);
+ break;
+ case CT_UTF7: /* variable UTF-7 encoded Unicode no table */
+ utf8_text_utf7 (text,ret,cv,de);
+ break;
+ case CT_UCS2: /* 2 byte 16-bit Unicode no table */
+ utf8_text_ucs2 (text,ret,cv,de);
+ break;
+ case CT_UCS4: /* 4 byte 32-bit Unicode no table */
+ utf8_text_ucs4 (text,ret,cv,de);
+ break;
+ case CT_UTF16: /* variable UTF-16 encoded Unicode no table */
+ utf8_text_utf16 (text,ret,cv,de);
+ break;
+ case CT_2022: /* variable ISO-2022 encoded no table*/
+ utf8_text_2022 (text,ret,cv,de);
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS encoded JIS no table */
+ utf8_text_sjis (text,ret,cv,de);
+ break;
+ default: /* unknown character set type */
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Reverse mapping routines
+ *
+ * These routines only support character sets, not all possible charsets. In
+ * particular, they do not support any Unicode encodings or ISO 2022.
+ *
+ * As a special dispensation, utf8_cstext() and utf8_cstocstext() support
+ * support ISO-2022-JP if EUC-JP can be reverse mapped; and utf8_rmaptext()
+ * will generated ISO-2022-JP using an EUC-JP rmap if flagged to do so.
+ *
+ * No attempt is made to map "equivalent" Unicode characters or Unicode
+ * characters that have the same glyph; nor is there any attempt to handle
+ * combining characters or otherwise do any stringprep. Maybe later.
+ */
+
+
+/* Convert UTF-8 sized text to charset
+ * Accepts: source sized text
+ * destination charset
+ * pointer to returned sized text
+ * substitute character if not in cs, else NIL to return failure
+ * Returns: T if successful, NIL if failure
+ */
+
+
+long utf8_cstext (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,
+ unsigned long errch)
+{
+ short iso2022jp = !compare_cstring (charset,"ISO-2022-JP");
+ unsigned short *rmap = utf8_rmap (iso2022jp ? "EUC-JP" : charset);
+ return rmap ? utf8_rmaptext (text,rmap,ret,errch,iso2022jp) : NIL;
+}
+
+/* Convert charset labelled sized text to another charset
+ * Accepts: source sized text
+ * source charset
+ * pointer to returned sized text
+ * destination charset
+ * substitute character if not in dest cs, else NIL to return failure
+ * Returns: T if successful, NIL if failure
+ *
+ * This routine has the same restricts as utf8_cstext().
+ */
+
+long utf8_cstocstext (SIZEDTEXT *src,char *sc,SIZEDTEXT *dst,char *dc,
+ unsigned long errch)
+{
+ SIZEDTEXT utf8;
+ const CHARSET *scs,*dcs;
+ unsigned short *rmap;
+ long ret = NIL;
+ long iso2022jp;
+ /* lookup charsets and reverse map */
+ if ((dc && (dcs = utf8_charset (dc))) &&
+ (rmap = (iso2022jp = ((dcs->type == CT_2022) &&
+ !compare_cstring (dcs->name,"ISO-2022-JP"))) ?
+ utf8_rmap ("EUC-JP") : utf8_rmap_cs (dcs)) &&
+ (scs = (sc && *sc) ? utf8_charset (sc) : utf8_infercharset (src))) {
+ /* init temporary buffer */
+ memset (&utf8,NIL,sizeof (SIZEDTEXT));
+ /* source cs equivalent to dest cs? */
+ if ((scs->type == dcs->type) && (scs->tab == dcs->tab)) {
+ dst->data = src->data; /* yes, just copy pointers */
+ dst->size = src->size;
+ ret = LONGT;
+ }
+ /* otherwise do the conversion */
+ else ret = (utf8_text_cs (src,scs,&utf8,NIL,NIL) &&
+ utf8_rmaptext (&utf8,rmap,dst,errch,iso2022jp));
+ /* flush temporary buffer */
+ if (utf8.data && (utf8.data != src->data) && (utf8.data != dst->data))
+ fs_give ((void **) &utf8.data);
+ }
+ return ret;
+}
+
+/* Cached rmap */
+
+static const CHARSET *currmapcs = NIL;
+static unsigned short *currmap = NIL;
+
+
+/* Cache and return map for UTF-8 -> character set
+ * Accepts: character set name
+ * Returns: cached map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap (char *charset)
+{
+ return (currmapcs && !compare_cstring (charset,currmapcs->name)) ? currmap :
+ utf8_rmap_cs (utf8_charset (charset));
+}
+
+
+/* Cache and return map for UTF-8 -> character set given CHARSET block
+ * Accepts: CHARSET block
+ * Returns: cached map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap_cs (const CHARSET *cs)
+{
+ unsigned short *ret = NIL;
+ if (!cs); /* have charset? */
+ else if (cs == currmapcs) ret = currmap;
+ else if (ret = utf8_rmap_gen (cs,currmap)) {
+ currmapcs = cs;
+ currmap = ret;
+ }
+ return ret;
+}
+
+/* Return map for UTF-8 -> character set given CHARSET block
+ * Accepts: CHARSET block
+ * old map to recycle
+ * Returns: map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap_gen (const CHARSET *cs,unsigned short *oldmap)
+{
+ unsigned short u,*tab,*rmap;
+ unsigned int i,m,ku,ten;
+ struct utf8_eucparam *param,*p2;
+ switch (cs->type) { /* is a character set? */
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ rmap = oldmap ? oldmap : /* recycle old map if supplied else make new */
+ (unsigned short *) fs_get (65536 * sizeof (unsigned short));
+ /* initialize table for ASCII */
+ for (i = 0; i < 128; i++) rmap[i] = (unsigned short) i;
+ /* populate remainder of table with NOCHAR */
+#define NOCHARBYTE (NOCHAR & 0xff)
+#if NOCHAR - ((NOCHARBYTE << 8) | NOCHARBYTE)
+ while (i < 65536) rmap[i++] = NOCHAR;
+#else
+ memset (rmap + 128,NOCHARBYTE,(65536 - 128) * sizeof (unsigned short));
+#endif
+ break;
+ default: /* unsupported charset type */
+ rmap = NIL; /* no map possible */
+ }
+ if (rmap) { /* have a map? */
+ switch (cs->type) { /* additional reverse map actions */
+ case CT_1BYTE0: /* 1 byte no table */
+ for (i = 128; i < 256; i++) rmap[i] = (unsigned short) i;
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ for (tab = (unsigned short *) cs->tab,i = 128; i < 256; i++)
+ if (tab[i & BITS7] != UBOGON) rmap[tab[i & BITS7]] = (unsigned short)i;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ for (tab = (unsigned short *) cs->tab,i = 0; i < 256; i++)
+ if (tab[i] != UBOGON) rmap[tab[i]] = (unsigned short) i;
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) +
+ (ten + param->base_ten) + 0x8080;
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + param->base_ten);
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ param = (struct utf8_eucparam *) cs->tab;
+ p2 = param + 1; /* plane 2 parameters */
+ /* only ten parameters should differ */
+ if ((param->base_ku != p2->base_ku) || (param->max_ku != p2->max_ku))
+ fatal ("ku definition error for CT_DBYTE2 charset");
+ /* total codepoints in each ku */
+ m = param->max_ten + p2->max_ten;
+ tab = (unsigned short *) param->tab;
+ for (ku = 0; ku < param->max_ku; ku++) {
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * m) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + param->base_ten);
+ for (ten = 0; ten < p2->max_ten; ten++)
+ if ((u = tab[(ku * m) + param->max_ten + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + p2->base_ten);
+ }
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ for (ku = 0; ku < MAX_JIS0208_KU; ku++)
+ for (ten = 0; ten < MAX_JIS0208_TEN; ten++)
+ if ((u = jis0208tab[ku][ten]) != UBOGON) {
+ int sku = ku + BASE_JIS0208_KU;
+ int sten = ten + BASE_JIS0208_TEN;
+ rmap[u] = ((((sku + 1) >> 1) + ((sku < 95) ? 112 : 176)) << 8) +
+ sten + ((sku % 2) ? ((sten > 95) ? 32 : 31) : 126);
+ }
+ /* JIS Roman */
+ rmap[UCS2_YEN] = JISROMAN_YEN;
+ rmap[UCS2_OVERLINE] = JISROMAN_OVERLINE;
+ /* JIS hankaku katakana */
+ for (u = 0; u < (MAX_KANA_8 - MIN_KANA_8); u++)
+ rmap[UCS2_KATAKANA + u] = MIN_KANA_8 + u;
+ break;
+ }
+ /* hack: map NBSP to SP if otherwise no map */
+ if (rmap[0x00a0] == NOCHAR) rmap[0x00a0] = rmap[0x0020];
+ }
+ return rmap; /* return map */
+}
+
+/* Convert UTF-8 sized text to charset using rmap
+ * Accepts: source sized text
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * ISO-2022-JP conversion flag
+ * Returns T if successful, NIL if failure
+ *
+ * This routine doesn't try to convert to all possible charsets; in particular
+ * it doesn't support other Unicode encodings or any ISO 2022 other than
+ * ISO-2022-JP.
+ */
+
+long utf8_rmaptext (SIZEDTEXT *text,unsigned short *rmap,SIZEDTEXT *ret,
+ unsigned long errch,long iso2022jp)
+{
+ unsigned long i,u,c;
+ /* get size of buffer */
+ if (i = utf8_rmapsize (text,rmap,errch,iso2022jp)) {
+ unsigned char *s = text->data;
+ unsigned char *t = ret->data = (unsigned char *) fs_get (i);
+ ret->size = i - 1; /* number of octets in destination buffer */
+ /* start non-zero ISO-2022-JP state at 1 */
+ if (iso2022jp) iso2022jp = 1;
+ /* convert string, ignore BOM */
+ for (i = text->size; i;) if ((u = utf8_get (&s,&i)) != UCS2_BOM) {
+ /* substitute error character for NOCHAR */
+ if ((u & U8GM_NONBMP) || ((c = rmap[u]) == NOCHAR)) c = errch;
+ switch (iso2022jp) { /* depends upon ISO 2022 mode */
+ case 0: /* ISO 2022 not in effect */
+ /* two-byte character */
+ if (c > 0xff) *t++ = (unsigned char) (c >> 8);
+ /* single-byte or low-byte of two-byte */
+ *t++ = (unsigned char) (c & 0xff);
+ break;
+ case 1: /* ISO 2022 Roman */
+ /* <ch> */
+ if (c < 0x80) *t++ = (unsigned char) c;
+ else { /* JIS character */
+ *t++ = I2C_ESC; /* ESC $ B <hi> <lo> */
+ *t++ = I2C_MULTI;
+ *t++ = I2CS_94x94_JIS_NEW;
+ *t++ = (unsigned char) (c >> 8) & 0x7f;
+ *t++ = (unsigned char) c & 0x7f;
+ iso2022jp = 2; /* shift to ISO 2022 JIS */
+ }
+ break;
+ case 2: /* ISO 2022 JIS */
+ if (c > 0x7f) { /* <hi> <lo> */
+ *t++ = (unsigned char) (c >> 8) & 0x7f;
+ *t++ = (unsigned char) c & 0x7f;
+ }
+ else { /* ASCII character */
+ *t++ = I2C_ESC; /* ESC ( J <ch> */
+ *t++ = I2C_G0_94;
+ *t++ = I2CS_94_JIS_ROMAN;
+ *t++ = (unsigned char) c;
+ iso2022jp = 1; /* shift to ISO 2022 Roman */
+ }
+ break;
+ }
+ }
+ if (iso2022jp == 2) { /* ISO-2022-JP string must end in Roman */
+ *t++ = I2C_ESC; /* ESC ( J */
+ *t++ = I2C_G0_94;
+ *t++ = I2CS_94_JIS_ROMAN;
+ }
+ *t++ = NIL; /* tie off returned data */
+ return LONGT; /* return success */
+ }
+ ret->data = NIL;
+ ret->size = 0;
+ return NIL; /* failure */
+}
+
+/* Calculate size of convertsion of UTF-8 sized text to charset using rmap
+ * Accepts: source sized text
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * ISO-2022-JP conversion flag
+ * Returns size+1 if successful, NIL if failure
+ *
+ * This routine doesn't try to handle to all possible charsets; in particular
+ * it doesn't support other Unicode encodings or any ISO 2022 other than
+ * ISO-2022-JP.
+ */
+
+unsigned long utf8_rmapsize (SIZEDTEXT *text,unsigned short *rmap,
+ unsigned long errch,long iso2022jp)
+{
+ unsigned long i,u,c;
+ unsigned long ret = 1; /* terminating NUL */
+ unsigned char *s = text->data;
+ if (iso2022jp) iso2022jp = 1; /* start non-zero ISO-2022-JP state at 1 */
+ for (i = text->size; i;) if ((u = utf8_get (&s,&i)) != UCS2_BOM) {
+ if ((u & U8GM_NONBMP) || (((c = rmap[u]) == NOCHAR) && !(c = errch)))
+ return NIL; /* not in BMP, or NOCHAR and no err char */
+ switch (iso2022jp) { /* depends upon ISO 2022 mode */
+ case 0: /* ISO 2022 not in effect */
+ ret += (c > 0xff) ? 2 : 1;
+ break;
+ case 1: /* ISO 2022 Roman */
+ if (c < 0x80) ret += 1; /* <ch> */
+ else { /* JIS character */
+ ret += 5; /* ESC $ B <hi> <lo> */
+ iso2022jp = 2; /* shift to ISO 2022 JIS */
+ }
+ break;
+ case 2: /* ISO 2022 JIS */
+ if (c > 0x7f) ret += 2; /* <hi> <lo> */
+ else { /* ASCII character */
+ ret += 4; /* ESC ( J <ch> */
+ iso2022jp = 1; /* shift to ISO 2022 Roman */
+ }
+ break;
+ }
+ }
+ if (iso2022jp == 2) { /* ISO-2022-JP string must end in Roman */
+ ret += 3; /* ESC ( J */
+ iso2022jp = 1; /* reset state to Roman */
+ }
+ return ret;
+}
+
+/* Convert UCS-4 to charset using rmap
+ * Accepts: source UCS-4 character(s)
+ * numver of UCS-4 characters
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns T if successful, NIL if failure
+ *
+ * Currently only supports BMP characters, and does not support ISO-2022
+ */
+
+long ucs4_rmaptext (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ SIZEDTEXT *ret,unsigned long errch)
+{
+ long size = ucs4_rmaplen (ucs4,len,rmap,errch);
+ return (size >= 0) ? /* build in newly-created buffer */
+ ucs4_rmapbuf (ret->data = (unsigned char *) fs_get ((ret->size = size) +1),
+ ucs4,len,rmap,errch) : NIL;
+}
+
+/* Return size of UCS-4 string converted to other CS via rmap
+ * Accepts: source UCS-4 character(s)
+ * numver of UCS-4 characters
+ * conversion rmap
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns: length if success, negative if failure (no-convert)
+ */
+
+long ucs4_rmaplen (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ unsigned long errch)
+{
+ long ret;
+ unsigned long i,u,c;
+ /* count non-BOM characters */
+ for (ret = 0,i = 0; i < len; ++i) if ((u = ucs4[i]) != UCS2_BOM) {
+ if ((u & U8GM_NONBMP) || (((c = rmap[u]) == NOCHAR) && !(c = errch)))
+ return -1; /* not in BMP, or NOCHAR and no err char? */
+ ret += (c > 0xff) ? 2 : 1;
+ }
+ return ret;
+}
+
+
+/* Stuff buffer with UCS-4 string converted to other CS via rmap
+ * Accepts: destination buffer
+ * source UCS-4 character(s)
+ * number of UCS-4 characters
+ * conversion rmap
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns: T, always
+ */
+
+long ucs4_rmapbuf (unsigned char *t,unsigned long *ucs4,unsigned long len,
+ unsigned short *rmap,unsigned long errch)
+{
+ unsigned long i,u,c;
+ /* convert non-BOM characters */
+ for (i = 0; i < len; ++i) if ((u = ucs4[i]) != UCS2_BOM) {
+ /* substitute error character for NOCHAR */
+ if ((u & U8GM_NONBMP) || ((c = rmap[u]) == NOCHAR)) c = errch;
+ /* two-byte character? */
+ if (c > 0xff) *t++ = (unsigned char) (c >> 8);
+ /* single-byte or low-byte of two-byte */
+ *t++ = (unsigned char) (c & 0xff);
+ }
+ *t++ = NIL; /* tie off returned data */
+ return LONGT;
+}
+
+/* Return UCS-4 Unicode character from UTF-8 string
+ * Accepts: pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated
+ * or error code with pointer and count unchanged
+ */
+
+unsigned long utf8_get (unsigned char **s,unsigned long *i)
+{
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ /* decode raw UTF-8 string */
+ unsigned long ret = utf8_get_raw (&t,&j);
+ if (ret & U8G_ERROR); /* invalid raw UTF-8 decoding? */
+ /* no, is it surrogate? */
+ else if ((ret >= UTF16_SURR) && (ret <= UTF16_MAXSURR)) ret = U8G_SURROGA;
+ /* or in non-Unicode ISO 10646 space? */
+ else if (ret > UCS4_MAXUNICODE) ret = U8G_NOTUNIC;
+ else {
+ *s = t; /* all is well, update pointer */
+ *i = j; /* and counter */
+ }
+ return ret; /* return value */
+}
+
+/* Return raw (including non-Unicode) UCS-4 character from UTF-8 string
+ * Accepts: pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated
+ * or error code with pointer and count unchanged
+ */
+
+unsigned long utf8_get_raw (unsigned char **s,unsigned long *i)
+{
+ unsigned char c,c1;
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ unsigned long ret = U8G_NOTUTF8;
+ int more = 0;
+ do { /* make sure have source octets available */
+ if (!j--) return more ? U8G_ENDSTRI : U8G_ENDSTRG;
+ /* UTF-8 continuation? */
+ else if (((c = *t++) > 0x7f) && (c < 0xc0)) {
+ /* continuation when not in progress */
+ if (!more) return U8G_BADCONT;
+ --more; /* found a continuation octet */
+ ret <<= 6; /* shift current value by 6 bits */
+ ret |= c & 0x3f; /* merge continuation octet */
+ }
+ /* incomplete UTF-8 character */
+ else if (more) return U8G_INCMPLT;
+ else { /* start of sequence */
+ c1 = j ? *t : 0xbf; /* assume valid continuation if incomplete */
+ if (c < 0x80) ret = c; /* U+0000 - U+007f */
+ else if (c < 0xc2); /* c0 and c1 never valid */
+ else if (c < 0xe0) { /* U+0080 - U+07ff */
+ if (c &= 0x1f) more = 1;
+ }
+ else if (c < 0xf0) { /* U+0800 - U+ffff */
+ if ((c &= 0x0f) || (c1 >= 0xa0)) more = 2;
+ }
+ else if (c < 0xf8) { /* U+10000 - U+10ffff (and 110000 - 1fffff) */
+ if ((c &= 0x07) || (c1 >= 0x90)) more = 3;
+ }
+ else if (c < 0xfc) { /* ISO 10646 200000 - 3ffffff */
+ if ((c &= 0x03) || (c1 >= 0x88)) more = 4;
+ }
+ else if (c < 0xfe) { /* ISO 10646 4000000 - 7fffffff */
+ if ((c &= 0x01) || (c1 >= 0x84)) more = 5;
+ }
+ /* fe and ff never valid */
+ if (more) { /* multi-octet, make sure more to come */
+ if (!j) return U8G_ENDSTRI;
+ ret = c; /* continuation needed, save start bits */
+ }
+ }
+ } while (more);
+ if (!(ret & U8G_ERROR)) { /* success return? */
+ *s = t; /* yes, update pointer */
+ *i = j; /* and counter */
+ }
+ return ret; /* return value */
+}
+
+/* Return UCS-4 character from named charset string
+ * Accepts: charset
+ * pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated, negative if error
+ *
+ * Error codes are the same as utf8_get().
+ */
+
+unsigned long ucs4_cs_get (CHARSET *cs,unsigned char **s,unsigned long *i)
+{
+ unsigned char c,c1,ku,ten;
+ unsigned long ret,d;
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ struct utf8_eucparam *p1,*p2,*p3;
+ if (j--) c = *t++; /* get first octet */
+ else return U8G_ENDSTRG; /* empty string */
+ switch (cs->type) { /* convert if type known */
+ case CT_UTF8: /* variable UTF-8 encoded Unicode no table */
+ return utf8_get (s,i);
+ case CT_ASCII: /* 7-bit ASCII no table */
+ if (c >= 0x80) return U8G_NOTUTF8;
+ case CT_1BYTE0: /* 1 byte no table */
+ ret = c; /* identity */
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ ret = (c > 0x80) ? ((unsigned short *) cs->tab)[c & BITS7] : c;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ ret = ((unsigned short *) cs->tab)[c];
+ break;
+
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ if (c & BIT8) {
+ p1 = (struct utf8_eucparam *) cs->tab;
+ p2 = p1 + 1;
+ p3 = p1 + 2;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (!(c1 & BIT8)) return U8G_NOTUTF8;
+ switch (c) { /* check 8bit code set */
+ case EUC_CS2: /* CS2 */
+ if (p2->base_ku) { /* CS2 set up? */
+ if (p2->base_ten) { /* yes, multibyte? */
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if ((c & BIT8) &&
+ ((ku = (c1 & BITS7) - p2->base_ku) < p2->max_ku) &&
+ ((ten = (c & BITS7) - p2->base_ten) < p2->max_ten)) {
+ ret = ((unsigned short *) p2->tab)[(ku*p2->max_ten) + ten];
+ break;
+ }
+ }
+ else if ((c1 >= p2->base_ku) && (c1 < p2->max_ku)) {
+ ret = c1 + ((unsigned long) p2->tab);
+ break;
+ }
+ }
+ return U8G_NOTUTF8; /* CS2 not set up or bogus */
+ case EUC_CS3: /* CS3 */
+ if (p3->base_ku) { /* CS3 set up? */
+ if (p3->base_ten) { /* yes, multibyte? */
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if ((c & BIT8) &&
+ ((ku = (c1 & BITS7) - p3->base_ku) < p3->max_ku) &&
+ ((ten = (c & BITS7) - p3->base_ten) < p3->max_ten)) {
+ ret = ((unsigned short *) p3->tab)[(ku*p3->max_ten) + ten];
+ break;
+ }
+ }
+ else if ((c1 >= p3->base_ku) && (c1 < p3->max_ku)) {
+ ret = c1 + ((unsigned long) p3->tab);
+ break;
+ }
+ }
+ return U8G_NOTUTF8; /* CS3 not set up or bogus */
+ default:
+ if (((ku = (c & BITS7) - p1->base_ku) >= p1->max_ku) ||
+ ((ten = (c1 & BITS7) - p1->base_ten) >= p1->max_ten))
+ return U8G_NOTUTF8;
+ ret = ((unsigned short *) p1->tab)[(ku*p1->max_ten) + ten];
+ /* special hack for JIS X 0212: merge rows less than 10 */
+ if ((ret == UBOGON) && ku && (ku < 10) && p3->tab && p3->base_ten)
+ ret = ((unsigned short *) p3->tab)
+ [((ku - (p3->base_ku - p1->base_ku))*p3->max_ten) + ten];
+ break;
+ }
+ }
+ else ret = c; /* ASCII character */
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ if (c & BIT8) { /* double-byte character? */
+ p1 = (struct utf8_eucparam *) cs->tab;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten))
+ ret = ((unsigned short *) p1->tab)[(ku*p1->max_ten) + ten];
+ else return U8G_NOTUTF8;
+ }
+ else ret = c; /* ASCII character */
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ if (c & BIT8) { /* double-byte character? */
+ p1 = (struct utf8_eucparam *) cs->tab;
+ p2 = p1 + 1;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (c1 & BIT8) { /* high vs. low plane */
+ if ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten))
+ ret = ((unsigned short *) p1->tab)
+ [(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten];
+ else return U8G_NOTUTF8;
+ }
+ else if ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten))
+ ret = ((unsigned short *) p1->tab)
+ [(ku*(p1->max_ten + p2->max_ten)) + ten];
+ else return U8G_NOTUTF8;
+ }
+ else ret = c; /* ASCII character */
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS encoded JIS no table */
+ /* compromise - do yen sign but not overline */
+ if (!(c & BIT8)) ret = (c == JISROMAN_YEN) ? UCS2_YEN : c;
+ /* half-width katakana? */
+ else if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) ret = c + KANA_8;
+ else { /* Shift-JIS */
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ break;
+
+ case CT_UCS2: /* 2 byte 16-bit Unicode no table */
+ ret = c << 8;
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI; /* empty string */
+ ret |= c;
+ break;
+ case CT_UCS4: /* 4 byte 32-bit Unicode no table */
+ if (c & 0x80) return U8G_NOTUTF8;
+ if (j < 3) return U8G_ENDSTRI;
+ j -= 3; /* count three octets */
+ ret = c << 24;
+ ret |= (*t++) << 16;
+ ret |= (*t++) << 8;
+ ret |= (*t++);
+ break;
+ case CT_UTF16: /* variable UTF-16 encoded Unicode no table */
+ ret = c << 8;
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI; /* empty string */
+ ret |= c;
+ /* surrogate? */
+ if ((ret >= UTF16_SURR) && (ret <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((ret > UTF16_SURRHEND) || (j < 2)) return U8G_NOTUTF8;
+ j -= 2; /* count two octets */
+ d = (*t++) << 8; /* first octet of second surrogate */
+ d |= *t++; /* second octet of second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) return U8G_NOTUTF8;
+ ret = UTF16_BASE + ((ret & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ break;
+ default: /* unknown/unsupported character set type */
+ return U8G_NOTUTF8;
+ }
+ *s = t; /* update pointer and counter */
+ *i = j;
+ return ret;
+}
+
+/* Produce charset validity map for BMP
+ * Accepts: list of charsets to map
+ * Returns: validity map, indexed by BMP codepoint
+ *
+ * Bit 0x1 is the "not-CJK" character bit
+ */
+
+unsigned long *utf8_csvalidmap (char *charsets[])
+{
+ unsigned short u,*tab;
+ unsigned int m,ku,ten;
+ unsigned long i,csi,csb;
+ struct utf8_eucparam *param,*p2;
+ char *s;
+ const CHARSET *cs;
+ unsigned long *ret = (unsigned long *)
+ fs_get (i = 0x10000 * sizeof (unsigned long));
+ memset (ret,0,i); /* zero the entire vector */
+ /* mark all the non-CJK codepoints */
+ /* U+0000 - U+2E7F non-CJK */
+ for (i = 0; i < 0x2E7F; ++i) ret[i] = 0x1;
+ /* U+2E80 - U+2EFF CJK Radicals Supplement
+ * U+2F00 - U+2FDF Kangxi Radicals
+ * U+2FE0 - U+2FEF unassigned
+ * U+2FF0 - U+2FFF Ideographic Description Characters
+ * U+3000 - U+303F CJK Symbols and Punctuation
+ * U+3040 - U+309F Hiragana
+ * U+30A0 - U+30FF Katakana
+ * U+3100 - U+312F BoPoMoFo
+ * U+3130 - U+318F Hangul Compatibility Jamo
+ * U+3190 - U+319F Kanbun
+ * U+31A0 - U+31BF BoPoMoFo Extended
+ * U+31C0 - U+31EF CJK Strokes
+ * U+31F0 - U+31FF Katakana Phonetic Extensions
+ * U+3200 - U+32FF Enclosed CJK Letters and Months
+ * U+3300 - U+33FF CJK Compatibility
+ * U+3400 - U+4DBF CJK Unified Ideographs Extension A
+ * U+4DC0 - U+4DFF Yijing Hexagram Symbols
+ * U+4E00 - U+9FFF CJK Unified Ideographs
+ * U+A000 - U+A48F Yi Syllables
+ * U+A490 - U+A4CF Yi Radicals
+ * U+A700 - U+A71F Modifier Tone Letters
+ */
+ for (i = 0xa720; i < 0xabff; ++i) ret[i] = 0x1;
+ /* U+AC00 - U+D7FF Hangul Syllables */
+ for (i = 0xd800; i < 0xf8ff; ++i) ret[i] = 0x1;
+ /* U+F900 - U+FAFF CJK Compatibility Ideographs */
+ for (i = 0xfb00; i < 0xfe2f; ++i) ret[i] = 0x1;
+ /* U+FE30 - U+FE4F CJK Compatibility Forms
+ * U+FE50 - U+FE6F Small Form Variants (for CNS 11643)
+ */
+ for (i = 0xfe70; i < 0xfeff; ++i) ret[i] = 0x1;
+ /* U+FF00 - U+FFEF CJK Compatibility Ideographs */
+ for (i = 0xfff0; i < 0x10000; ++i) ret[i] = 0x1;
+
+ /* for each supplied charset */
+ for (csi = 1; ret && charsets && (s = charsets[csi - 1]); ++csi) {
+ /* substitute EUC-JP for ISO-2022-JP */
+ if (!compare_cstring (s,"ISO-2022-JP")) s = "EUC-JP";
+ /* look up charset */
+ if (cs = utf8_charset (s)) {
+ csb = 1 << csi; /* charset bit */
+ switch (cs->type) {
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ /* supported charset type, all ASCII is OK */
+ for (i = 0; i < 128; ++i) ret[i] |= csb;
+ break;
+ default: /* unsupported charset type */
+ fs_give ((void **) &ret);
+ break;
+ }
+ /* now do additional operations */
+ if (ret) switch (cs->type) {
+ case CT_1BYTE0: /* 1 byte no table */
+ for (i = 128; i < 256; i++) ret[i] |= csb;
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ for (tab = (unsigned short *) cs->tab,i = 128; i < 256; i++)
+ if (tab[i & BITS7] != UBOGON) ret[tab[i & BITS7]] |= csb;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ for (tab = (unsigned short *) cs->tab,i = 0; i < 256; i++)
+ if (tab[i] != UBOGON) ret[tab[i]] |= csb;
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ ret[u] |= csb;
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ ret[u] |= csb;
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ param = (struct utf8_eucparam *) cs->tab;
+ p2 = param + 1; /* plane 2 parameters */
+ /* only ten parameters should differ */
+ if ((param->base_ku != p2->base_ku) || (param->max_ku != p2->max_ku))
+ fatal ("ku definition error for CT_DBYTE2 charset");
+ /* total codepoints in each ku */
+ m = param->max_ten + p2->max_ten;
+ tab = (unsigned short *) param->tab;
+ for (ku = 0; ku < param->max_ku; ku++) {
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * m) + ten]) != UBOGON)
+ ret[u] |= csb;
+ for (ten = 0; ten < p2->max_ten; ten++)
+ if ((u = tab[(ku * m) + param->max_ten + ten]) != UBOGON)
+ ret[u] |= csb;
+ }
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ for (ku = 0; ku < MAX_JIS0208_KU; ku++)
+ for (ten = 0; ten < MAX_JIS0208_TEN; ten++)
+ if ((u = jis0208tab[ku][ten]) != UBOGON) ret[u] |= csb;
+ /* JIS hankaku katakana */
+ for (u = 0; u < (MAX_KANA_8 - MIN_KANA_8); u++)
+ ret[UCS2_KATAKANA + u] |= csb;
+ break;
+ }
+ }
+ /* invalid charset, punt */
+ else fs_give ((void **) &ret);
+ }
+ return ret;
+}
+
+/* Infer charset from unlabelled sized text
+ * Accepts: sized text
+ * Returns: charset if one inferred, or NIL if unknown
+ */
+
+const CHARSET *utf8_infercharset (SIZEDTEXT *src)
+{
+ long iso2022jp = NIL;
+ long eightbit = NIL;
+ unsigned long i;
+ /* look for ISO 2022 */
+ if (src) for (i = 0; i < src->size; i++) {
+ /* ESC sequence? */
+ if ((src->data[i] == I2C_ESC) && (++i < src->size)) switch (src->data[i]) {
+ case I2C_MULTI: /* yes, multibyte? */
+ if (++i < src->size) switch (src->data[i]) {
+ case I2CS_94x94_JIS_OLD: /* JIS X 0208-1978 */
+ case I2CS_94x94_JIS_NEW: /* JIS X 0208-1983 */
+ case I2CS_94x94_JIS_EXT: /* JIS X 0212-1990 (kludge...) */
+ iso2022jp = T; /* found an ISO-2022-JP sequence */
+ break;
+ default: /* other multibyte */
+ return NIL; /* definitely invalid */
+ }
+ break;
+ case I2C_G0_94: /* single byte */
+ if (++i < src->size) switch (src->data[i]) {
+ case I2CS_94_JIS_BUGROM: /* in case old buggy software */
+ case I2CS_94_JIS_ROMAN: /* JIS X 0201-1976 left half */
+ case I2CS_94_ASCII: /* ASCII */
+ case I2CS_94_BRITISH: /* good enough for gov't work */
+ break;
+ default: /* other 94 single byte */
+ return NIL; /* definitely invalid */
+ }
+ }
+ /* if possible UTF-8 and not ISO-2022-JP */
+ else if (!iso2022jp && (eightbit >= 0) && (src->data[i] & BIT8) &&
+ (eightbit = utf8_validate (src->data + i,src->size - i)) > 0)
+ i += eightbit - 1; /* skip past all but last of UTF-8 char */
+ }
+ /* ISO-2022-JP overrides other guesses */
+ if (iso2022jp) return utf8_charset ("ISO-2022-JP");
+ if (eightbit > 0) return utf8_charset ("UTF-8");
+ return eightbit ? NIL : utf8_charset ("US-ASCII");
+}
+
+
+/* Validate that character at this position is UTF-8
+ * Accepts: string pointer
+ * size of remaining string
+ * Returns: size of UTF-8 character in octets or -1 if not UTF-8
+ */
+
+long utf8_validate (unsigned char *s,unsigned long i)
+{
+ unsigned long j = i;
+ return (utf8_get (&s,&i) & U8G_ERROR) ? -1 : j - i;
+}
+
+/* Convert ISO 8859-1 to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * canonicalization function
+ */
+
+void utf8_text_1byte0 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ for (ret->size = i = 0; i < text->size;) {
+ c = text->data[i++];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ c = text->data[i++];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+
+/* Convert single byte ASCII+8bit character set sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_1byte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ unsigned short *tbl = (unsigned short *) tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert single byte 8bit character set sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_1byte8 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ unsigned short *tbl = (unsigned short *) tab;
+ for (ret->size = i = 0; i < text->size;) {
+ c = tbl[text->data[i++]];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ c = tbl[text->data[i++]];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert EUC sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * EUC parameter table
+ * canonicalization function
+ */
+
+void utf8_text_euc (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int pass,c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ struct utf8_eucparam *p2 = p1 + 1;
+ struct utf8_eucparam *p3 = p1 + 2;
+ unsigned short *t1 = (unsigned short *) p1->tab;
+ unsigned short *t2 = (unsigned short *) p2->tab;
+ unsigned short *t3 = (unsigned short *) p3->tab;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ for (i = 0; i < text->size;) {
+ /* not CS0? */
+ if ((c = text->data[i++]) & BIT8) {
+ /* yes, must have another high byte */
+ if ((i >= text->size) || !((c1 = text->data[i++]) & BIT8))
+ c = UBOGON; /* out of space or bogon */
+ else switch (c) { /* check 8bit code set */
+ case EUC_CS2: /* CS2 */
+ if (p2->base_ku) { /* CS2 set up? */
+ if (p2->base_ten) /* yes, multibyte? */
+ c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
+ ((ku = (c1 & BITS7) - p2->base_ku) < p2->max_ku) &&
+ ((ten = (c & BITS7) - p2->base_ten) < p2->max_ten)) ?
+ t2[(ku*p2->max_ten) + ten] : UBOGON;
+ else c = ((c1 >= p2->base_ku) && (c1 < p2->max_ku)) ?
+ c1 + ((unsigned long) p2->tab) : UBOGON;
+ }
+ else { /* CS2 not set up */
+ c = UBOGON; /* swallow byte, say bogon */
+ if (i < text->size) i++;
+ }
+ break;
+ case EUC_CS3: /* CS3 */
+ if (p3->base_ku) { /* CS3 set up? */
+ if (p3->base_ten) /* yes, multibyte? */
+ c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
+ ((ku = (c1 & BITS7) - p3->base_ku) < p3->max_ku) &&
+ ((ten = (c & BITS7) - p3->base_ten) < p3->max_ten)) ?
+ t3[(ku*p3->max_ten) + ten] : UBOGON;
+ else c = ((c1 >= p3->base_ku) && (c1 < p3->max_ku)) ?
+ c1 + ((unsigned long) p3->tab) : UBOGON;
+ }
+ else { /* CS3 not set up */
+ c = UBOGON; /* swallow byte, say bogon */
+ if (i < text->size) i++;
+ }
+ break;
+
+ default:
+ if (((ku = (c & BITS7) - p1->base_ku) >= p1->max_ku) ||
+ ((ten = (c1 & BITS7) - p1->base_ten) >= p1->max_ten)) c = UBOGON;
+ else if (((c = t1[(ku*p1->max_ten) + ten]) == UBOGON) &&
+ /* special hack for JIS X 0212: merge rows less than 10 */
+ ku && (ku < 10) && t3 && p3->base_ten)
+ c = t3[((ku - (p3->base_ku - p1->base_ku))*p3->max_ten) + ten];
+ }
+ }
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] =NIL;
+ }
+}
+
+
+/* Convert ASCII + double-byte sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_dbyte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ unsigned short *t1 = (unsigned short *) p1->tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* special hack for GBK: 0x80 is Euro */
+ if ((c == 0x80) && (t1 == (unsigned short *) gb2312tab)) c = UCS2_EURO;
+ else c = ((i < text->size) && (c1 = text->data[i++]) &&
+ ((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t1[(ku*p1->max_ten) + ten] : UBOGON;
+ }
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* special hack for GBK: 0x80 is Euro */
+ if ((c == 0x80) && (t1 == (unsigned short *) gb2312tab)) c = UCS2_EURO;
+ else c = ((i < text->size) && (c1 = text->data[i++]) &&
+ ((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t1[(ku*p1->max_ten) + ten] : UBOGON;
+ }
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert ASCII + double byte 2 plane sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_dbyte2 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ struct utf8_eucparam *p2 = p1 + 1;
+ unsigned short *t = (unsigned short *) p1->tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ if ((i >= text->size) || !(c1 = text->data[i++]))
+ c = UBOGON; /* out of space or bogon */
+ else if (c1 & BIT8) /* high vs. low plane */
+ c = ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] :UBOGON;
+ else c = ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + ten] : UBOGON;
+ }
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ if ((i >= text->size) || !(c1 = text->data[i++]))
+ c = UBOGON; /* out of space or bogon */
+ else if (c1 & BIT8) /* high vs. low plane */
+ c = ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] :UBOGON;
+ else c = ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + ten] : UBOGON;
+ }
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+#ifdef JISTOUNICODE /* Japanese */
+/* Convert Shift JIS sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * canonicalization function
+ */
+
+void utf8_text_sjis (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* half-width katakana */
+ if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) c += KANA_8;
+ else if (i >= text->size) c = UBOGON;
+ else { /* Shift-JIS */
+ c1 = text->data[i++];
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ }
+ /* compromise - do yen sign but not overline */
+ else if (c == JISROMAN_YEN) c = UCS2_YEN;
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* half-width katakana */
+ if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) c += KANA_8;
+ else { /* Shift-JIS */
+ c1 = text->data[i++];
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ }
+ /* compromise - do yen sign but not overline */
+ else if (c == JISROMAN_YEN) c = UCS2_YEN;
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+#endif
+
+/* Convert ISO-2022 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_2022 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int pass,state,c,co,gi,gl,gr,g[4],ku,ten;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ gi = 0; /* quell compiler warnings */
+ state = I2S_CHAR; /* initialize engine */
+ g[0]= g[2] = I2CS_ASCII; /* G0 and G2 are ASCII */
+ g[1]= g[3] = I2CS_ISO8859_1;/* G1 and G3 are ISO-8850-1 */
+ gl = I2C_G0; gr = I2C_G1; /* left is G0, right is G1 */
+ for (i = 0; i < text->size;) {
+ c = text->data[i++];
+ switch (state) { /* dispatch based upon engine state */
+ case I2S_ESC: /* ESC seen */
+ switch (c) { /* process intermediate character */
+ case I2C_MULTI: /* multibyte character? */
+ state = I2S_MUL; /* mark multibyte flag seen */
+ break;
+ case I2C_SS2: /* single shift GL to G2 */
+ case I2C_SS2_ALT: /* Taiwan SeedNet */
+ gl |= I2C_SG2;
+ break;
+ case I2C_SS3: /* single shift GL to G3 */
+ case I2C_SS3_ALT: /* Taiwan SeedNet */
+ gl |= I2C_SG3;
+ break;
+ case I2C_LS2: /* shift GL to G2 */
+ gl = I2C_G2;
+ break;
+ case I2C_LS3: /* shift GL to G3 */
+ gl = I2C_G3;
+ break;
+ case I2C_LS1R: /* shift GR to G1 */
+ gr = I2C_G1;
+ break;
+ case I2C_LS2R: /* shift GR to G2 */
+ gr = I2C_G2;
+ break;
+ case I2C_LS3R: /* shift GR to G3 */
+ gr = I2C_G3;
+ break;
+ case I2C_G0_94: case I2C_G1_94: case I2C_G2_94: case I2C_G3_94:
+ g[gi = c - I2C_G0_94] = (state == I2S_MUL) ? I2CS_94x94 : I2CS_94;
+ state = I2S_INT; /* ready for character set */
+ break;
+ case I2C_G0_96: case I2C_G1_96: case I2C_G2_96: case I2C_G3_96:
+ g[gi = c - I2C_G0_96] = (state == I2S_MUL) ? I2CS_96x96 : I2CS_96;
+ state = I2S_INT; /* ready for character set */
+ break;
+ default: /* bogon */
+ if (pass) *s++ = I2C_ESC,*s++ = c;
+ else ret->size += 2;
+ state = I2S_CHAR; /* return to previous state */
+ }
+ break;
+
+ case I2S_MUL: /* ESC $ */
+ switch (c) { /* process multibyte intermediate character */
+ case I2C_G0_94: case I2C_G1_94: case I2C_G2_94: case I2C_G3_94:
+ g[gi = c - I2C_G0_94] = I2CS_94x94;
+ state = I2S_INT; /* ready for character set */
+ break;
+ case I2C_G0_96: case I2C_G1_96: case I2C_G2_96: case I2C_G3_96:
+ g[gi = c - I2C_G0_96] = I2CS_96x96;
+ state = I2S_INT; /* ready for character set */
+ break;
+ default: /* probably omitted I2CS_94x94 */
+ g[gi = I2C_G0] = I2CS_94x94 | c;
+ state = I2S_CHAR; /* return to character state */
+ }
+ break;
+ case I2S_INT:
+ state = I2S_CHAR; /* return to character state */
+ g[gi] |= c; /* set character set */
+ break;
+
+ case I2S_CHAR: /* character data */
+ switch (c) {
+ case I2C_ESC: /* ESC character */
+ state = I2S_ESC; /* see if ISO-2022 prefix */
+ break;
+ case I2C_SI: /* shift GL to G0 */
+ gl = I2C_G0;
+ break;
+ case I2C_SO: /* shift GL to G1 */
+ gl = I2C_G1;
+ break;
+ case I2C_SS2_ALT: /* single shift GL to G2 */
+ case I2C_SS2_ALT_7:
+ gl |= I2C_SG2;
+ break;
+ case I2C_SS3_ALT: /* single shift GL to G3 */
+ case I2C_SS3_ALT_7:
+ gl |= I2C_SG3;
+ break;
+
+ default: /* ordinary character */
+ co = c; /* note original character */
+ if (gl & (3 << 2)) { /* single shifted? */
+ gi = g[gl >> 2]; /* get shifted character set */
+ gl &= 0x3; /* cancel shift */
+ }
+ /* select left or right half */
+ else gi = (c & BIT8) ? g[gr] : g[gl];
+ c &= BITS7; /* make 7-bit */
+ switch (gi) { /* interpret in character set */
+ case I2CS_ASCII: /* ASCII */
+ break; /* easy! */
+ case I2CS_BRITISH: /* British ASCII */
+ /* Pound sterling sign */
+ if (c == BRITISH_POUNDSTERLING) c = UCS2_POUNDSTERLING;
+ break;
+ case I2CS_JIS_ROMAN: /* JIS Roman */
+ case I2CS_JIS_BUGROM: /* old bugs */
+ switch (c) { /* two exceptions to ASCII */
+ case JISROMAN_YEN: /* Yen sign */
+ c = UCS2_YEN;
+ break;
+ /* overline */
+ case JISROMAN_OVERLINE:
+ c = UCS2_OVERLINE;
+ break;
+ }
+ break;
+ case I2CS_JIS_KANA: /* JIS hankaku katakana */
+ if ((c >= MIN_KANA_7) && (c < MAX_KANA_7)) c += KANA_7;
+ break;
+
+ case I2CS_ISO8859_1: /* Latin-1 (West European) */
+ c |= BIT8; /* just turn on high bit */
+ break;
+ case I2CS_ISO8859_2: /* Latin-2 (Czech, Slovak) */
+ c = iso8859_2tab[c];
+ break;
+ case I2CS_ISO8859_3: /* Latin-3 (Dutch, Turkish) */
+ c = iso8859_3tab[c];
+ break;
+ case I2CS_ISO8859_4: /* Latin-4 (Scandinavian) */
+ c = iso8859_4tab[c];
+ break;
+ case I2CS_ISO8859_5: /* Cyrillic */
+ c = iso8859_5tab[c];
+ break;
+ case I2CS_ISO8859_6: /* Arabic */
+ c = iso8859_6tab[c];
+ break;
+ case I2CS_ISO8859_7: /* Greek */
+ c = iso8859_7tab[c];
+ break;
+ case I2CS_ISO8859_8: /* Hebrew */
+ c = iso8859_8tab[c];
+ break;
+ case I2CS_ISO8859_9: /* Latin-5 (Finnish, Portuguese) */
+ c = iso8859_9tab[c];
+ break;
+ case I2CS_TIS620: /* Thai */
+ c = tis620tab[c];
+ break;
+ case I2CS_ISO8859_10: /* Latin-6 (Northern Europe) */
+ c = iso8859_10tab[c];
+ break;
+ case I2CS_ISO8859_13: /* Latin-7 (Baltic) */
+ c = iso8859_13tab[c];
+ break;
+ case I2CS_VSCII: /* Vietnamese */
+ c = visciitab[c];
+ break;
+ case I2CS_ISO8859_14: /* Latin-8 (Celtic) */
+ c = iso8859_14tab[c];
+ break;
+ case I2CS_ISO8859_15: /* Latin-9 (Euro) */
+ c = iso8859_15tab[c];
+ break;
+ case I2CS_ISO8859_16: /* Latin-10 (Baltic) */
+ c = iso8859_16tab[c];
+ break;
+
+ default: /* all other character sets */
+ /* multibyte character set */
+ if ((gi & I2CS_MUL) && !(c & BIT8) && isgraph (c)) {
+ c = (i < text->size) ? text->data[i++] : 0;
+ switch (gi) {
+#ifdef GBTOUNICODE
+ case I2CS_GB: /* GB 2312 */
+ co |= BIT8; /* make into EUC */
+ c |= BIT8;
+ c = GBTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef JISTOUNICODE
+ case I2CS_JIS_OLD:/* JIS X 0208-1978 */
+ case I2CS_JIS_NEW:/* JIS X 0208-1983 */
+ c = JISTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef JIS0212TOUNICODE
+ case I2CS_JIS_EXT:/* JIS X 0212-1990 */
+ c = JIS0212TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef KSCTOUNICODE
+ case I2CS_KSC: /* KSC 5601 */
+ co |= BIT8; /* make into EUC */
+ c |= BIT8;
+ c = KSCTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS1TOUNICODE
+ case I2CS_CNS1: /* CNS 11643 plane 1 */
+ c = CNS1TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS2TOUNICODE
+ case I2CS_CNS2: /* CNS 11643 plane 2 */
+ c = CNS2TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS3TOUNICODE
+ case I2CS_CNS3: /* CNS 11643 plane 3 */
+ c = CNS3TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS4TOUNICODE
+ case I2CS_CNS4: /* CNS 11643 plane 4 */
+ c = CNS4TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS5TOUNICODE
+ case I2CS_CNS5: /* CNS 11643 plane 5 */
+ c = CNS5TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS6TOUNICODE
+ case I2CS_CNS6: /* CNS 11643 plane 6 */
+ c = CNS6TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS7TOUNICODE
+ case I2CS_CNS7: /* CNS 11643 plane 7 */
+ c = CNS7TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+ default: /* unknown multibyte, treat as UCS-2 */
+ c |= (co << 8); /* wrong, but nothing else to do */
+ break;
+ }
+ }
+ else c = co; /* unknown single byte, treat as 8859-1 */
+ }
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ }
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] = NIL;
+ else if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("ISO-2022 to UTF-8 botch");
+ }
+}
+
+/* Convert UTF-7 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf7 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,d,uc,pass,e,e1,state,surrh;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ c1 = d = uc = e = e1 = 0;
+ for (i = 0,state = NIL; i < text->size;) {
+ c = text->data[i++]; /* get next byte */
+ switch (state) {
+ case U7_PLUS: /* previous character was + */
+ if (c == '-') { /* +- means textual + */
+ c = '+';
+ state = U7_ASCII; /* revert to ASCII */
+ break;
+ }
+ state = U7_UNICODE; /* enter Unicode state */
+ e = e1 = 0; /* initialize Unicode quantum position */
+ case U7_UNICODE: /* Unicode state */
+ if (c == '-') state = U7_MINUS;
+ else { /* decode Unicode */
+ /* don't use isupper/islower since this is ASCII only */
+ if ((c >= 'A') && (c <= 'Z')) c -= 'A';
+ else if ((c >= 'a') && (c <= 'z')) c -= 'a' - 26;
+ else if (isdigit (c)) c -= '0' - 52;
+ else if (c == '+') c = 62;
+ else if (c == '/') c = 63;
+ else state = U7_ASCII;/* end of modified BASE64 */
+ }
+ break;
+ case U7_MINUS: /* previous character was absorbed - */
+ state = U7_ASCII; /* revert to ASCII */
+ case U7_ASCII: /* ASCII state */
+ if (c == '+') state = U7_PLUS;
+ break;
+ }
+
+ switch (state) { /* store character if in character mode */
+ case U7_UNICODE: /* Unicode */
+ switch (e++) { /* install based on BASE64 state */
+ case 0:
+ c1 = c << 2; /* byte 1: high 6 bits */
+ break;
+ case 1:
+ d = c1 | (c >> 4); /* byte 1: low 2 bits */
+ c1 = c << 4; /* byte 2: high 4 bits */
+ break;
+ case 2:
+ d = c1 | (c >> 2); /* byte 2: low 4 bits */
+ c1 = c << 6; /* byte 3: high 2 bits */
+ break;
+ case 3:
+ d = c | c1; /* byte 3: low 6 bits */
+ e = 0; /* reinitialize mechanism */
+ break;
+ }
+ if (e == 1) break; /* done if first BASE64 state */
+ if (!e1) { /* first byte of UCS-2 character */
+ uc = (d & 0xff) << 8; /* note first byte */
+ e1 = T; /* enter second UCS-2 state */
+ break; /* done */
+ }
+ c = uc | (d & 0xff); /* build UCS-2 character */
+ e1 = NIL; /* back to first UCS-2 state, drop in */
+ /* surrogate pair? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* save high surrogate for later */
+ if (c < UTF16_SURRL) surrh = c;
+ else c = UTF16_BASE + ((surrh & UTF16_MASK) << UTF16_SHIFT) +
+ (c & UTF16_MASK);
+ break; /* either way with surrogates, we're done */
+ }
+ case U7_ASCII: /* just install if ASCII */
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] = NIL;
+ else if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-7 to UTF-8 botch");
+ }
+}
+
+
+/* Convert UTF-8 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf8 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i,c;
+ unsigned char *s,*t;
+ for (ret->size = 0, t = text->data, i = text->size; i;) {
+ if ((c = utf8_get (&t,&i)) & U8G_ERROR) {
+ ret->data = text->data; /* conversion failed */
+ ret->size = text->size;
+ return;
+ }
+ UTF8_COUNT (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (t = text->data, i = text->size; i;) {
+ c = utf8_get (&t,&i);
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-8 to UTF-8 botch");
+}
+
+/* Convert UCS-2 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_ucs2 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned int c;
+ for (ret->size = 0, t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UCS-2 to UTF-8 botch");
+}
+
+
+/* Convert UCS-4 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_ucs4 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned long c;
+ for (ret->size = 0, t = text->data, i = text->size / 4; i; --i) {
+ c = *t++ << 24; c |= *t++ << 16; c |= *t++ << 8; c |= *t++;
+ UTF8_COUNT (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 24; c |= *t++ << 16; c |= *t++ << 8; c |= *t++;
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UCS-4 to UTF-8 botch");
+}
+
+/* Convert UTF-16 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf16 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned long c,d;
+ for (ret->size = 0, t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ /* possible surrogate? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((c > UTF16_SURRHEND) || !i) c = UBOGON;
+ else { /* get second surrogate */
+ d = *t++ << 8;
+ d |= *t++;
+ --i; /* swallowed another 16-bits */
+ /* invalid second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) c = UBOGON;
+ else c = UTF16_BASE + ((c & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ }
+ UTF8_COUNT (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ /* possible surrogate? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((c > UTF16_SURRHEND) || !i) c = UBOGON;
+ else { /* get second surrogate */
+ d = *t++ << 8;
+ d |= *t++;
+ --i; /* swallowed another 16-bits */
+ /* invalid second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) c = UBOGON;
+ else c = UTF16_BASE + ((c & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ }
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-16 to UTF-8 botch");
+}
+
+/* Size of UCS-4 character, possibly not in BMP, as UTF-8 octets
+ * Accepts: character
+ * Returns: size (0 means bogon)
+ *
+ * Use UTF8_SIZE macro if known to be in the BMP
+ */
+
+unsigned long utf8_size (unsigned long c)
+{
+ if (c < 0x80) return 1;
+ else if (c < 0x800) return 2;
+ else if (c < 0x10000) return 3;
+ else if (c < 0x200000) return 4;
+ else if (c < 0x4000000) return 5;
+ else if (c < 0x80000000) return 6;
+ return 0;
+}
+
+
+/* Put UCS-4 character, possibly not in BMP, as UTF-8 octets
+ * Accepts: destination string pointer
+ * character
+ * Returns: updated destination pointer
+ *
+ * Use UTF8_PUT_BMP macro if known to be in the BMP
+ */
+
+unsigned char *utf8_put (unsigned char *s,unsigned long c)
+{
+ unsigned char mark[6] = {0x00,0xc0,0xe0,0xf0,0xf8,0xfc};
+ unsigned long size = utf8_size (c);
+ switch (size) {
+ case 6:
+ s[5] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 5:
+ s[4] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 4:
+ s[3] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 3:
+ s[2] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 2:
+ s[1] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 1:
+ *s = mark[size-1] | (unsigned char) (c & 0x7f);
+ break;
+ }
+ return s + size;
+}
+
+/* Return title case of a fixed-width UCS-4 character
+ * Accepts: character
+ * Returns: title case of character
+ */
+
+unsigned long ucs4_titlecase (unsigned long c)
+{
+ if (c <= UCS4_TMAPMAX) return ucs4_tmaptab[c];
+ if (c < UCS4_TMAPHIMIN) return c;
+ if (c <= UCS4_TMAPHIMAX) return c - UCS4_TMAPHIMAP;
+ if (c < UCS4_TMAPDESERETMIN) return c;
+ if (c <= UCS4_TMAPDESERETMAX) return c - UCS4_TMAPDESERETMAP;
+ return c;
+}
+
+
+/* Return width of a fixed-width UCS-4 character in planes 0-2
+ * Accepts: character
+ * Returns: width (0, 1, 2) or negative error condition if not valid
+ */
+
+long ucs4_width (unsigned long c)
+{
+ long ret;
+ /* out of range, not-a-char, or surrogates */
+ if ((c > UCS4_MAXUNICODE) || ((c & 0xfffe) == 0xfffe) ||
+ ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR))) ret = U4W_NOTUNCD;
+ /* private-use */
+ else if (c >= UCS4_PVTBASE) ret = U4W_PRIVATE;
+ /* SSP are not printing characters */
+ else if (c >= UCS4_SSPBASE) ret = U4W_SSPCHAR;
+ /* unassigned planes */
+ else if (c >= UCS4_UNABASE) ret = U4W_UNASSGN;
+ /* SIP and reserved plane 3 are wide */
+ else if (c >= UCS4_SIPBASE) ret = 2;
+#if (UCS4_WIDLEN != UCS4_SIPBASE)
+#error "UCS4_WIDLEN != UCS4_SIPBASE"
+#endif
+ /* C0/C1 controls */
+ else if ((c <= UCS2_C0CONTROLEND) ||
+ ((c >= UCS2_C1CONTROL) && (c <= UCS2_C1CONTROLEND)))
+ ret = U4W_CONTROL;
+ /* BMP and SMP get value from table */
+ else switch (ret = (ucs4_widthtab[(c >> 2)] >> ((3 - (c & 0x3)) << 1)) &0x3){
+ case 0: /* zero-width */
+ if (c == 0x00ad) ret = 1; /* force U+00ad (SOFT HYPHEN) to width 1 */
+ case 1: /* single-width */
+ case 2: /* double-width */
+ break;
+ case 3: /* ambiguous width */
+ ret = (c >= 0x2100) ? 2 : 1;/* need to do something better than this */
+ break;
+ }
+ return ret;
+}
+
+/* Return screen width of UTF-8 string
+ * Accepts: string
+ * Returns: width or negative if not valid UTF-8
+ */
+
+long utf8_strwidth (unsigned char *s)
+{
+ unsigned long c,i,ret;
+ /* go through string */
+ for (ret = 0; *s; ret += ucs4_width (c)) {
+ /* It's alright to give a fake value for the byte count to utf8_get()
+ * since the null of a null-terminated string will stop processing anyway.
+ */
+ i = 6; /* fake value */
+ if ((c = utf8_get (&s,&i)) & U8G_ERROR) return -1;
+ }
+ return ret;
+}
+
+
+/* Return screen width of UTF-8 text
+ * Accepts: SIZEDTEXT to string
+ * Returns: width or negative if not valid UTF-8
+ */
+
+long utf8_textwidth (SIZEDTEXT *utf8)
+{
+ unsigned long c;
+ unsigned char *s = utf8->data;
+ unsigned long i = utf8->size;
+ unsigned long ret = 0;
+ while (i) { /* while there's a string to process */
+ if ((c = utf8_get (&s,&i)) & U8G_ERROR) return -1;
+ ret += ucs4_width (c);
+ }
+ return ret;
+}
+
+/* Decomposition (phew!) */
+
+#define MORESINGLE 1 /* single UCS-4 tail value */
+#define MOREMULTIPLE 2 /* multiple UCS-2 tail values */
+
+struct decomposemore {
+ short type; /* type of more */
+ union {
+ unsigned long single; /* single decomposed value */
+ struct { /* multiple BMP values */
+ unsigned short *next;
+ unsigned long count;
+ } multiple;
+ } data;
+};
+
+#define RECURSIVEMORE struct recursivemore
+
+RECURSIVEMORE {
+ struct decomposemore *more;
+ RECURSIVEMORE *next;
+};
+
+
+/* Return decomposition of a UCS-4 character
+ * Accepts: character or U8G_ERROR to return next from "more"
+ * pointer to returned more
+ * Returns: [next] decomposed value, more set if still more decomposition
+ */
+
+unsigned long ucs4_decompose (unsigned long c,void **more)
+{
+ unsigned long i,ix,ret;
+ struct decomposemore *m;
+ if (c & U8G_ERROR) { /* want to chase more? */
+ /* do sanity check */
+ if (m = (struct decomposemore *) *more) switch (m->type) {
+ case MORESINGLE: /* single value */
+ ret = m->data.single;
+ fs_give (more); /* no more decomposition */
+ break;
+ case MOREMULTIPLE: /* multiple value */
+ ret = *m->data.multiple.next++;
+ if (!--m->data.multiple.count) fs_give (more);
+ break;
+ default: /* uh-oh */
+ fatal ("invalid more block argument to ucs4_decompose!");
+ }
+ else fatal ("no more block provided to ucs4_decompose!");
+ }
+
+ else { /* start decomposition */
+ *more = NIL; /* initially set no more */
+ /* BMP low decompositions */
+ if (c < UCS4_BMPLOMIN) ret = c;
+ /* fix this someday */
+ else if (c == UCS4_BMPLOMIN) ret = ucs4_dbmplotab[0];
+ else if (c <= UCS4_BMPLOMAX) {
+ /* within range - have a decomposition? */
+ if (i = ucs4_dbmploixtab[c - UCS4_BMPLOMIN]) {
+ /* get first value of decomposition */
+ ret = ucs4_dbmplotab[ix = i & UCS4_BMPLOIXMASK];
+ /* has continuation? */
+ if (i & UCS4_BMPLOSIZEMASK) {
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MOREMULTIPLE;
+ m->data.multiple.next = &ucs4_dbmplotab[++ix];
+ m->data.multiple.count = i >> UCS4_BMPLOSIZESHIFT;
+ }
+ }
+ else ret = c; /* in range but doesn't decompose */
+ }
+ /* BMP CJK compatibility */
+ else if (c < UCS4_BMPCJKMIN) ret = c;
+ else if (c <= UCS4_BMPCJKMAX) {
+ if (!(ret = ucs4_bmpcjk1decomptab[c - UCS4_BMPCJKMIN])) ret = c;
+ }
+ /* BMP CJK compatibility - some not in BMP */
+#if UCS4_BMPCJK2MIN - (UCS4_BMPCJKMAX + 1)
+ else if (c < UCS4_BMPCJK2MIN) ret = c;
+#endif
+ else if (c <= UCS4_BMPCJK2MAX)
+ ret = ucs4_bmpcjk2decomptab[c - UCS4_BMPCJK2MIN];
+ /* BMP high decompositions */
+ else if (c < UCS4_BMPHIMIN) ret = c;
+ else if (c <= UCS4_BMPHIMAX) {
+ /* within range - have a decomposition? */
+ if (i = ucs4_dbmphiixtab[c - UCS4_BMPHIMIN]) {
+ /* get first value of decomposition */
+ ret = ucs4_dbmphitab[ix = i & UCS4_BMPHIIXMASK];
+ /* has continuation? */
+ if (i & UCS4_BMPHISIZEMASK) {
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MOREMULTIPLE;
+ m->data.multiple.next = &ucs4_dbmphitab[++ix];
+ m->data.multiple.count = i >> UCS4_BMPHISIZESHIFT;
+ }
+ }
+ else ret = c; /* in range but doesn't decompose */
+ }
+
+ /* BMP half and full width forms */
+ else if (c < UCS4_BMPHALFFULLMIN) ret = c;
+ else if (c <= UCS4_BMPHALFFULLMAX) {
+ if (!(ret = ucs4_bmphalffulldecomptab[c - UCS4_BMPHALFFULLMIN])) ret = c;
+ }
+ /* SMP music */
+ else if (c < UCS4_SMPMUSIC1MIN) ret = c;
+ else if (c <= UCS4_SMPMUSIC1MAX) {
+ ret = ucs4_smpmusic1decomptab[c -= UCS4_SMPMUSIC1MIN][0];
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MORESINGLE;
+ m->data.single = ucs4_smpmusic1decomptab[c][1];
+ }
+ else if (c < UCS4_SMPMUSIC2MIN) ret = c;
+ else if (c <= UCS4_SMPMUSIC2MAX) {
+ ret = ucs4_smpmusic2decomptab[c -= UCS4_SMPMUSIC2MIN][0];
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MORESINGLE;
+ m->data.single = ucs4_smpmusic2decomptab[c][1];
+ }
+ /* SMP mathematical forms */
+ else if (c < UCS4_SMPMATHMIN) ret = c;
+ else if (c <= UCS4_SMPMATHMAX) {
+ if (!(ret = ucs4_smpmathdecomptab[c - UCS4_SMPMATHMIN])) ret = c;
+ }
+ /* CJK compatibility ideographs in SIP */
+ else if (!(ret = ((c >= UCS4_SIPMIN) && (c <= UCS4_SIPMAX)) ?
+ ucs4_sipdecomptab[c - UCS4_SIPMIN] : c)) ret = c;
+ }
+ return ret;
+}
+
+/* Return recursive decomposition of a UCS-4 character
+ * Accepts: character or U8G_ERROR to return next from "more"
+ * pointer to returned more
+ * Returns: [next] decomposed value, more set if still more decomposition
+ */
+
+unsigned long ucs4_decompose_recursive (unsigned long c,void **more)
+{
+ unsigned long c1;
+ void *m,*mn;
+ RECURSIVEMORE *mr;
+ if (c & U8G_ERROR) { /* want to chase more? */
+ mn = NIL;
+ if (mr = (RECURSIVEMORE *) *more) switch (mr->more->type) {
+ case MORESINGLE: /* decompose single value */
+ c = ucs4_decompose_recursive (mr->more->data.single,&mn);
+ *more = mr->next; /* done with this more, remove it */
+ fs_give ((void **) &mr->more);
+ fs_give ((void **) &mr);
+ break;
+ case MOREMULTIPLE: /* decompose current value in multiple */
+ c = ucs4_decompose_recursive (*mr->more->data.multiple.next++,&mn);
+ /* if done with this multiple decomposition */
+ if (!--mr->more->data.multiple.count) {
+ *more = mr->next; /* done with this more, remove it */
+ fs_give ((void **) &mr->more);
+ fs_give ((void **) &mr);
+ }
+ break;
+ default: /* uh-oh */
+ fatal ("invalid more block argument to ucs4_decompose_recursive!");
+ }
+ else fatal ("no more block provided to ucs4_decompose_recursive!");
+ if (mr = mn) { /* did this value recurse on us? */
+ mr->next = *more; /* yes, insert new more at head */
+ *more = mr;
+ }
+ }
+ else { /* start decomposition */
+ *more = NIL; /* initially set no more */
+ mr = NIL;
+ do { /* repeatedly decompose this codepoint */
+ c = ucs4_decompose (c1 = c,&m);
+ if (m) { /* multi-byte decomposition */
+ if (c1 == c) fatal ("endless multiple decomposition!");
+ /* create a block to stash this more */
+ mr = memset (fs_get (sizeof (RECURSIVEMORE)),0,sizeof (RECURSIVEMORE));
+ mr->more = m; /* note the expansion */
+ mr->next = *more; /* old list is the tail */
+ *more = mr; /* and this is the new head */
+ }
+ } while (c1 != c); /* until nothing more to decompose */
+ }
+ return c;
+}
diff --git a/imap/src/c-client/utf8.h b/imap/src/c-client/utf8.h
new file mode 100644
index 00000000..105f856d
--- /dev/null
+++ b/imap/src/c-client/utf8.h
@@ -0,0 +1,584 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 17 January 2008
+ */
+
+/* UTF-8 size and conversion routines from UCS-2 values (thus in the BMP).
+ * Don't use these if UTF-16 data (surrogate pairs) are an issue.
+ * For UCS-4 values, use the utf8_size() and utf8_put() functions.
+ */
+
+#define UTF8_SIZE_BMP(c) ((c & 0xff80) ? ((c & 0xf800) ? 3 : 2) : 1)
+
+#define UTF8_PUT_BMP(b,c) { \
+ if (c & 0xff80) { /* non-ASCII? */ \
+ if (c & 0xf800) { /* three byte code */ \
+ *b++ = 0xe0 | (c >> 12); \
+ *b++ = 0x80 | ((c >> 6) & 0x3f); \
+ } \
+ else *b++ = 0xc0 | ((c >> 6) & 0x3f); \
+ *b++ = 0x80 | (c & 0x3f); \
+ } \
+ else *b++ = c; \
+}
+
+/* utf8_text() flag values */
+
+#define U8T_CASECANON 2 /* canonicalize case */
+#define U8T_DECOMPOSE 4 /* decompose */
+ /* full canonicalization */
+#define U8T_CANONICAL (U8T_CASECANON | U8T_DECOMPOSE)
+
+
+/* utf8_get() return values */
+
+ /* 0x0000 - 0xffff BMP plane */
+#define U8GM_NONBMP 0xffff0000 /* mask for non-BMP values */
+ /* 0x10000 - 0x10ffff extended planes */
+ /* 0x110000 - 0x7ffffff non-Unicode */
+#define U8G_ERROR 0x80000000 /* error flag */
+#define U8G_BADCONT U8G_ERROR+1 /* continuation when not in progress */
+#define U8G_INCMPLT U8G_ERROR+2 /* incomplete UTF-8 character */
+#define U8G_NOTUTF8 U8G_ERROR+3 /* not a valid UTF-8 octet */
+#define U8G_ENDSTRG U8G_ERROR+4 /* end of string */
+#define U8G_ENDSTRI U8G_ERROR+5 /* end of string w/ incomplete UTF-8 char */
+#define U8G_SURROGA U8G_ERROR+6 /* surrogate codepoint */
+#define U8G_NOTUNIC U8G_ERROR+7 /* non-Unicode codepoint */
+
+
+/* ucs4_width() return values */
+
+#define U4W_ERROR 0x80000000 /* error flags */
+#define U4W_NOTUNCD U4W_ERROR+1 /* not a Unicode char */
+#define U4W_PRIVATE U4W_ERROR+2 /* private-space plane */
+#define U4W_SSPCHAR U4W_ERROR+3 /* Supplementary Special-purpose Plane */
+#define U4W_UNASSGN U4W_ERROR+4 /* unassigned space plane */
+#define U4W_CONTROL U4W_ERROR+5 /* C0/C1 control */
+#define U4W_CTLSRGT U4W_CONTROL /* in case legacy code references this */
+
+/* ISO-2022 engine states */
+
+#define I2S_CHAR 0 /* character */
+#define I2S_ESC 1 /* previous character was ESC */
+#define I2S_MUL 2 /* previous character was multi-byte code */
+#define I2S_INT 3 /* previous character was intermediate */
+
+
+/* ISO-2022 Gn selections */
+
+#define I2C_G0 0 /* G0 */
+#define I2C_G1 1 /* G1 */
+#define I2C_G2 2 /* G2 */
+#define I2C_G3 3 /* G3 */
+#define I2C_SG2 (2 << 2) /* single shift G2 */
+#define I2C_SG3 (3 << 2) /* single shift G2 */
+
+
+/* ISO-2022 octet definitions */
+
+#define I2C_ESC 0x1b /* ESCape */
+
+ /* Intermediate character */
+#define I2C_STRUCTURE 0x20 /* announce code structure */
+#define I2C_C0 0x21 /* C0 */
+#define I2C_C1 0x22 /* C1 */
+#define I2C_CONTROL 0x23 /* single control function */
+#define I2C_MULTI 0x24 /* multi-byte character set */
+#define I2C_OTHER 0x25 /* other coding system */
+#define I2C_REVISED 0x26 /* revised registration */
+#define I2C_G0_94 0x28 /* G0 94-character set */
+#define I2C_G1_94 0x29 /* G1 94-character set */
+#define I2C_G2_94 0x2A /* G2 94-character set */
+#define I2C_G3_94 0x2B /* G3 94-character set */
+#define I2C_G0_96 0x2C /* (not in ISO-2022) G0 96-character set */
+#define I2C_G1_96 0x2D /* G1 96-character set */
+#define I2C_G2_96 0x2E /* G2 96-character set */
+#define I2C_G3_96 0x2F /* G3 96-character set */
+
+ /* Locking shifts */
+#define I2C_SI 0x0f /* lock shift to G0 (Shift In) */
+#define I2C_SO 0x0e /* lock shift to G1 (Shift Out) */
+ /* prefixed by ESC */
+#define I2C_LS2 0x6e /* lock shift to G2 */
+#define I2C_LS3 0x6f /* lock shift to G3 */
+#define I2C_LS1R 0x7e /* lock shift GR to G1 */
+#define I2C_LS2R 0x7d /* lock shift GR to G2 */
+#define I2C_LS3R 0x7c /* lock shift GR to G3 */
+
+ /* Single shifts */
+#define I2C_SS2_ALT 0x8e /* single shift to G2 (SS2) */
+#define I2C_SS3_ALT 0x8f /* single shift to G3 (SS3) */
+#define I2C_SS2_ALT_7 0x19 /* single shift to G2 (SS2) */
+#define I2C_SS3_ALT_7 0x1d /* single shift to G3 (SS3) */
+ /* prefixed by ESC */
+#define I2C_SS2 0x4e /* single shift to G2 (SS2) */
+#define I2C_SS3 0x4f /* single shift to G3 (SS3) */
+
+/* 94 character sets */
+
+ /* 4/0 ISO 646 IRV */
+#define I2CS_94_BRITISH 0x41 /* 4/1 ISO 646 British */
+#define I2CS_94_ASCII 0x42 /* 4/2 ISO 646 USA (ASCII) */
+ /* 4/3 NATS Finland/Sweden (primary) */
+ /* 4/4 NATS Finland/Sweden (secondary) */
+ /* 4/5 NATS Denmark/Norway (primary) */
+ /* 4/6 NATS Denmark/Norway (secondary) */
+ /* 4/7 ISO 646 Swedish SEN 850200 */
+ /* 4/8 ISO 646 Swedish names */
+#define I2CS_94_JIS_BUGROM 0x48 /* 4/8 some buggy software does this */
+#define I2CS_94_JIS_KANA 0x49 /* 4/9 JIS X 0201-1976 right half */
+#define I2CS_94_JIS_ROMAN 0x4a /* 4/a JIS X 0201-1976 left half */
+ /* 4/b ISO 646 German */
+ /* 4/c ISO 646 Portuguese (Olivetti) */
+ /* 4/d ISO 6438 African */
+ /* 4/e ISO 5427 Cyrillic (Honeywell-Bull) */
+ /* 4/f DIN 31624 extended bibliography */
+ /* 5/0 ISO 5426-1980 Bibliography */
+ /* 5/1 ISO 5427-1981 Cyrillic*/
+ /* 5/2 ISO 646 French (withdrawn) */
+ /* 5/3 ISO 5428-1980 Greek bibliography */
+ /* 5/4 GB 1988-80 Chinese */
+ /* 5/5 Latin-Greek (Honeywell-Bull) */
+ /* 5/6 UK Viewdata/Teletext */
+ /* 5/7 INIS (IRV subset) */
+ /* 5/8 ISO 5428 Greek Bibliography */
+ /* 5/9 ISO 646 Italian (Olivetti) */
+ /* 5/a ISO 646 Spanish (Olivetti) */
+ /* 5/b Greek (Olivetti) */
+ /* 5/c Latin-Greek (Olivetti) */
+ /* 5/d INIS non-standard extension */
+ /* 5/e INIS Cyrillic extension */
+ /* 5/f Arabic CODAR-U IERA */
+ /* 6/0 ISO 646 Norwegian */
+ /* 6/1 Norwegian version 2 (withdrawn) */
+ /* 6/2 Videotex supplementary */
+ /* 6/3 Videotex supplementary #2 */
+ /* 6/4 Videotex supplementary #3 */
+ /* 6/5 APL */
+ /* 6/6 ISO 646 French */
+ /* 6/7 ISO 646 Portuguese (IBM) */
+ /* 6/8 ISO 646 Spanish (IBM) */
+ /* 6/9 ISO 646 Hungarian */
+ /* 6/a Greek ELOT (withdrawn) */
+ /* 6/b ISO 9036 Arabic 7-bit */
+ /* 6/c ISO 646 IRV supplementary set */
+ /* 6/d JIS C6229-1984 OCR-A */
+ /* 6/e JIS C6229-1984 OCR-B */
+ /* 6/f JIS C6229-1984 OCR-B additional */
+ /* 7/0 JIS C6229-1984 hand-printed */
+ /* 7/1 JIS C6229-1984 additional hand-printd */
+ /* 7/2 JIS C6229-1984 katakana hand-printed */
+ /* 7/3 E13B Japanese graphic */
+ /* 7/4 Supplementary Videotex (withdrawn) */
+ /* 7/5 Teletex primary CCITT T.61 */
+ /* 7/6 Teletex secondary CCITT T.61 */
+ /* 7/7 CSA Z 243.4-1985 Alternate primary #1 */
+ /* 7/8 CSA Z 243.4-1985 Alternate primary #2 */
+ /* 7/9 Mosaic CCITT T.101 */
+ /* 7/a Serbocroatian/Slovenian Latin */
+ /* 7/b Serbocroatian Cyrillic */
+ /* 7/c Supplementary CCITT T.101 */
+ /* 7/d Macedonian Cyrillic */
+
+/* 94 character sets - second intermediate byte */
+
+ /* 4/0 Greek primary CCITT */
+ /* 4/1 Cuba */
+ /* 4/2 ISO/IEC 646 invariant */
+ /* 4/3 Irish Gaelic 7-bit */
+ /* 4/4 Turkmen */
+
+
+/* 94x94 character sets */
+
+#define I2CS_94x94_JIS_OLD 0x40 /* 4/0 JIS X 0208-1978 */
+#define I2CS_94x94_GB 0x41 /* 4/1 GB 2312 */
+#define I2CS_94x94_JIS_NEW 0x42 /* 4/2 JIS X 0208-1983 */
+#define I2CS_94x94_KSC 0x43 /* 4/3 KSC 5601 */
+#define I2CS_94x94_JIS_EXT 0x44 /* 4/4 JIS X 0212-1990 */
+ /* 4/5 CCITT Chinese */
+ /* 4/6 Blisssymbol Graphic */
+#define I2CS_94x94_CNS1 0x47 /* 4/7 CNS 11643 plane 1 */
+#define I2CS_94x94_CNS2 0x48 /* 4/8 CNS 11643 plane 2 */
+#define I2CS_94x94_CNS3 0x49 /* 4/9 CNS 11643 plane 3 */
+#define I2CS_94x94_CNS4 0x4a /* 4/a CNS 11643 plane 4 */
+#define I2CS_94x94_CNS5 0x4b /* 4/b CNS 11643 plane 5 */
+#define I2CS_94x94_CNS6 0x4c /* 4/c CNS 11643 plane 6 */
+#define I2CS_94x94_CNS7 0x4d /* 4/d CNS 11643 plane 7 */
+ /* 4/e DPRK (North Korea) KGCII */
+ /* 4/f JGCII plane 1 */
+ /* 5/0 JGCII plane 2 */
+
+/* 96 character sets */
+
+#define I2CS_96_ISO8859_1 0x41 /* 4/1 Latin-1 (Western Europe) */
+#define I2CS_96_ISO8859_2 0x42 /* 4/2 Latin-2 (Czech, Slovak) */
+#define I2CS_96_ISO8859_3 0x43 /* 4/3 Latin-3 (Dutch, Turkish) */
+#define I2CS_96_ISO8859_4 0x44 /* 4/4 Latin-4 (Scandinavian) */
+ /* 4/5 CSA Z 243.4-1985 */
+#define I2CS_96_ISO8859_7 0x46 /* 4/6 Greek */
+#define I2CS_96_ISO8859_6 0x47 /* 4/7 Arabic */
+#define I2CS_96_ISO8859_8 0x48 /* 4/8 Hebrew */
+ /* 4/9 Czechoslovak CSN 369103 */
+ /* 4/a Supplementary Latin and non-alpha */
+ /* 4/b Technical */
+#define I2CS_96_ISO8859_5 0x4c /* 4/c Cyrillic */
+#define I2CS_96_ISO8859_9 0x4d /* 4/d Latin-5 (Finnish, Portuguese) */
+ /* 4/e ISO 6937-2 residual */
+ /* 4/f Basic Cyrillic */
+ /* 5/0 Supplementary Latin 1, 2 and 5 */
+ /* 5/1 Basic Box */
+ /* 5/2 Supplementary ISO/IEC 6937 : 1992 */
+ /* 5/3 CCITT Hebrew supplementary */
+#define I2CS_96_TIS620 0x54 /* 5/4 TIS 620 */
+ /* 5/5 Arabic/French/German */
+#define I2CS_96_ISO8859_10 0x56 /* 5/6 Latin-6 (Northern Europe) */
+ /* 5/7 ??? */
+ /* 5/8 Sami (Lappish) supplementary */
+#define I2CS_96_ISO8859_13 0x59 /* 5/9 Latin-7 (Baltic) */
+#define I2CS_96_VSCII 0x5a /* 5/a Vietnamese */
+ /* 5/b Technical #1 IEC 1289 */
+#define I2CS_96_ISO8859_14 0x5c /* 5/c Latin-8 (Celtic) */
+ /* 5/d Sami supplementary Latin */
+ /* 5/e Latin/Hebrew */
+ /* 5/f Celtic supplementary Latin */
+ /* 6/0 Uralic supplementary Cyrillic */
+ /* 6/1 Volgaic supplementary Cyrillic */
+#define I2CS_96_ISO8859_15 0x62 /* 6/2 Latin-9 (Euro) */
+ /* 6/3 Latin-1 with Euro */
+ /* 6/4 Latin-4 with Euro */
+ /* 6/5 Latin-7 with Euro */
+#define I2CS_96_ISO8859_16 0x66 /* 6/6 Latin-10 (Balkan) */
+ /* 6/7 Ogham */
+ /* 6/8 Sami supplementary Latin #2 */
+ /* 7/d Supplementary Mosaic for CCITT 101 */
+
+/* 96x96 character sets */
+
+/* Types of character sets */
+
+#define I2CS_94 0x000 /* 94 character set */
+#define I2CS_96 0x100 /* 96 character set */
+#define I2CS_MUL 0x200 /* multi-byte */
+#define I2CS_94x94 (I2CS_MUL | I2CS_94)
+#define I2CS_96x96 (I2CS_MUL | I2CS_96)
+
+
+/* Character set identifiers stored in Gn */
+
+#define I2CS_BRITISH (I2CS_94 | I2CS_94_BRITISH)
+#define I2CS_ASCII (I2CS_94 | I2CS_94_ASCII)
+#define I2CS_JIS_BUGROM (I2CS_94 | I2CS_94_JIS_BUGROM)
+#define I2CS_JIS_KANA (I2CS_94 | I2CS_94_JIS_KANA)
+#define I2CS_JIS_ROMAN (I2CS_94 | I2CS_94_JIS_ROMAN)
+#define I2CS_JIS_OLD (I2CS_94x94 | I2CS_94x94_JIS_OLD)
+#define I2CS_GB (I2CS_94x94 | I2CS_94x94_GB)
+#define I2CS_JIS_NEW (I2CS_94x94 | I2CS_94x94_JIS_NEW)
+#define I2CS_KSC (I2CS_94x94 | I2CS_94x94_KSC)
+#define I2CS_JIS_EXT (I2CS_94x94 | I2CS_94x94_JIS_EXT)
+#define I2CS_CNS1 (I2CS_94x94 | I2CS_94x94_CNS1)
+#define I2CS_CNS2 (I2CS_94x94 | I2CS_94x94_CNS2)
+#define I2CS_CNS3 (I2CS_94x94 | I2CS_94x94_CNS3)
+#define I2CS_CNS4 (I2CS_94x94 | I2CS_94x94_CNS4)
+#define I2CS_CNS5 (I2CS_94x94 | I2CS_94x94_CNS5)
+#define I2CS_CNS6 (I2CS_94x94 | I2CS_94x94_CNS6)
+#define I2CS_CNS7 (I2CS_94x94 | I2CS_94x94_CNS7)
+#define I2CS_ISO8859_1 (I2CS_96 | I2CS_96_ISO8859_1)
+#define I2CS_ISO8859_2 (I2CS_96 | I2CS_96_ISO8859_2)
+#define I2CS_ISO8859_3 (I2CS_96 | I2CS_96_ISO8859_3)
+#define I2CS_ISO8859_4 (I2CS_96 | I2CS_96_ISO8859_4)
+#define I2CS_ISO8859_7 (I2CS_96 | I2CS_96_ISO8859_7)
+#define I2CS_ISO8859_6 (I2CS_96 | I2CS_96_ISO8859_6)
+#define I2CS_ISO8859_8 (I2CS_96 | I2CS_96_ISO8859_8)
+#define I2CS_ISO8859_5 (I2CS_96 | I2CS_96_ISO8859_5)
+#define I2CS_ISO8859_9 (I2CS_96 | I2CS_96_ISO8859_9)
+#define I2CS_TIS620 (I2CS_96 | I2CS_96_TIS620)
+#define I2CS_ISO8859_10 (I2CS_96 | I2CS_96_ISO8859_10)
+#define I2CS_ISO8859_13 (I2CS_96 | I2CS_96_ISO8859_13)
+#define I2CS_VSCII (I2CS_96 | I2CS_96_VSCII)
+#define I2CS_ISO8859_14 (I2CS_96 | I2CS_96_ISO8859_14)
+#define I2CS_ISO8859_15 (I2CS_96 | I2CS_96_ISO8859_15)
+#define I2CS_ISO8859_16 (I2CS_96 | I2CS_96_ISO8859_16)
+
+
+/* Miscellaneous ISO 2022 definitions */
+
+#define EUC_CS2 0x8e /* single shift CS2 */
+#define EUC_CS3 0x8f /* single shift CS3 */
+
+#define BITS7 0x7f /* 7-bit value mask */
+#define BIT8 0x80 /* 8th bit mask */
+
+/* The following saves us from having to have yet more charset tables */
+
+/* Unicode codepoints */
+
+#define UCS2_C0CONTROL 0x00 /* first C0 control */
+#define UCS2_C0CONTROLEND 0x1F /* last C0 control */
+#define UCS2_C1CONTROL 0x80 /* first C1 control */
+#define UCS2_C1CONTROLEND 0x9F /* last C1 control */
+
+ /* ISO 646 substituted Unicode codepoints */
+#define UCS2_POUNDSTERLING 0x00a3
+#define UCS2_YEN 0x00a5
+#define UCS2_OVERLINE 0x203e
+#define UCS2_EURO 0x20ac
+#define UCS2_KATAKANA 0xff61 /* first katakana codepoint */
+#define UCS2_BOM 0xfeff /* byte order mark */
+#define UCS2_BOGON 0xfffd /* replacement character */
+ /* next two codepoints are not Unicode chars */
+#define UCS2_BOMCHECK 0xfffe /* used to check byte order with UCS2_BOM */
+#define UCS2_NOTCHAR 0xffff /* not a character */
+
+#define UCS4_BMPBASE 0x0000 /* Basic Multilingual Plane */
+#define UCS4_SMPBASE 0x10000 /* Supplementary Multilinugual Plane */
+#define UCS4_SIPBASE 0x20000 /* Supplementary Ideographic Plane */
+ /* EastAsianWidth says plane 3 is wide */
+#define UCS4_UNABASE 0x40000 /* unassigned space */
+#define UCS4_SSPBASE 0xe0000 /* Supplementary Special-purpose Plane */
+#define UCS4_PVTBASE 0xf0000 /* private-space (two planes) */
+#define UCS4_MAXUNICODE 0x10ffff/* highest Unicode codepoint */
+
+#define UTF16_BASE 0x10000 /* base of codepoints needing surrogates */
+#define UTF16_SHIFT 10 /* surrogate shift */
+#define UTF16_MASK 0x3ff /* surrogate mask */
+#define UTF16_SURR 0xd800 /* UTF-16 surrogate area */
+#define UTF16_SURRH 0xd800 /* UTF-16 first high surrogate */
+#define UTF16_SURRHEND 0xdbff /* UTF-16 last high surrogate */
+#define UTF16_SURRL 0xdc00 /* UTF-16 first low surrogate */
+#define UTF16_SURRLEND 0xdfff /* UTF-16 last low surrogate */
+#define UTF16_MAXSURR 0xdfff /* end of UTF-16 surrogates */
+
+
+/* UBOGON is used to represent a codepoint in a character set which does not
+ * map to Unicode. It is also used for mapping failures, e.g. incomplete
+ * shift sequences. This name has the same text width as 0x????, for
+ * convenience in the mapping tables.
+ *
+ * NOCHAR is used to represent a codepoint in Unicode which does not map to
+ * the target character set in a reverse mapping table. This name has the
+ * same text width as 0x???? in case we ever add static reverse mapping tables.
+ */
+
+#define UBOGON UCS2_BOGON
+#define NOCHAR UCS2_NOTCHAR
+
+/* Codepoints in non-Unicode character sets */
+
+/* Codepoints in ISO 646 character sets */
+
+/* British ASCII codepoints */
+
+#define BRITISH_POUNDSTERLING 0x23
+
+/* JIS Roman codepoints */
+
+#define JISROMAN_YEN 0x5c
+#define JISROMAN_OVERLINE 0x7e
+
+
+/* Hankaku katakana codepoints & parameters
+ *
+ * In earlier versions, MAX_KANA_7 and MAX_KANA_8 were the maximum codepoint
+ * values. Although this made sense, it was confusing with the "max ku" and
+ * "max ten" values used in the double-byte tables; there are 1-origin, but
+ * the calculated values used for "ku" and "ten" are 0-origin (derived by
+ * substracting the "base"). What this all meant is that for double byte
+ * characters the limit test is of the form (value < max_ku), but for single
+ * byte characters (which used the same cell to hold the max ku) the limit
+ * test was (value <= max_ku).
+ *
+ * By making MAX_KANA_[78] be maximum+1, the same (value < max_ku) limit test
+ * is used throughout. - 6/15/2006
+ */
+
+#define MIN_KANA_7 0x21
+#define MAX_KANA_7 0x60 /* maximum value + 1 */
+#define KANA_7 (UCS2_KATAKANA - MIN_KANA_7)
+#define MIN_KANA_8 (MIN_KANA_7 | BIT8)
+#define MAX_KANA_8 (MAX_KANA_7 | BIT8)
+#define KANA_8 (UCS2_KATAKANA - MIN_KANA_8)
+
+/* Charset scripts */
+
+/* The term "script" is used here in a very loose sense, enough to make
+ * purists cringe. Basically, the idea is to give the main program some
+ * idea of how it should treat the characters of text in a charset with
+ * respect to font, drawing routines, etc.
+ *
+ * In some cases, "script" is associated with a charset; in other cases,
+ * it's more closely tied to a language.
+ */
+
+#define SC_UNICODE 0x1 /* Unicode */
+#define SC_LATIN_1 0x10 /* Western Europe */
+#define SC_LATIN_2 0x20 /* Eastern Europe */
+#define SC_LATIN_3 0x40 /* Southern Europe */
+#define SC_LATIN_4 0x80 /* Northern Europe */
+#define SC_LATIN_5 0x100 /* Turkish */
+#define SC_LATIN_6 0x200 /* Nordic */
+#define SC_LATIN_7 0x400 /* Baltic */
+#define SC_LATIN_8 0x800 /* Celtic */
+#define SC_LATIN_9 0x1000 /* Euro */
+#define SC_LATIN_0 SC_LATIN_9 /* colloquial name for Latin-9 */
+#define SC_ARABIC 0x2000
+#define SC_CYRILLIC 0x4000
+#define SC_GREEK 0x8000
+#define SC_HEBREW 0x10000
+#define SC_THAI 0x20000
+#define SC_UKRANIAN 0x40000
+#define SC_LATIN_10 0x80000 /* Balkan */
+#define SC_VIETNAMESE 0x100000
+#define SC_CHINESE_SIMPLIFIED 0x1000000
+#define SC_CHINESE_TRADITIONAL 0x2000000
+#define SC_JAPANESE 0x4000000
+#define SC_KOREAN 0x8000000
+
+
+/* Script table */
+
+typedef struct utf8_scent {
+ char *name; /* script name */
+ char *description; /* script description */
+ unsigned long script; /* script bitmask */
+} SCRIPT;
+
+/* Character set table support */
+
+typedef struct utf8_csent {
+ char *name; /* charset name */
+ unsigned short type; /* type of charset */
+ unsigned short flags; /* charset flags */
+ void *tab; /* additional data */
+ unsigned long script; /* script(s) implemented by this charset */
+ char *preferred; /* preferred charset over this one */
+} CHARSET;
+
+
+struct utf8_eucparam {
+ unsigned int base_ku : 8; /* base row */
+ unsigned int base_ten : 8; /* base column */
+ unsigned int max_ku : 8; /* maximum row */
+ unsigned int max_ten : 8; /* maximum column */
+ void *tab; /* conversion table */
+};
+
+
+/* Charset types */
+
+#define CT_UNKNOWN 0 /* unknown 8-bit */
+#define CT_ASCII 1 /* 7-bit ASCII no table */
+#define CT_UCS2 2 /* 2 byte 16-bit Unicode no table */
+#define CT_UCS4 3 /* 4 byte 32-bit Unicode no table */
+#define CT_1BYTE0 10 /* 1 byte ISO 8859-1 no table */
+#define CT_1BYTE 11 /* 1 byte ASCII + table 0x80-0xff */
+#define CT_1BYTE8 12 /* 1 byte table 0x00 - 0xff */
+#define CT_EUC 100 /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+#define CT_DBYTE 101 /* 2 byte ASCII + utf8_eucparam */
+#define CT_DBYTE2 102 /* 2 byte ASCII + utf8_eucparam plane1/2 */
+#define CT_UTF16 1000 /* variable UTF-16 encoded Unicode no table */
+#define CT_UTF8 1001 /* variable UTF-8 encoded Unicode no table */
+#define CT_UTF7 1002 /* variable UTF-7 encoded Unicode no table */
+#define CT_2022 10000 /* variable ISO-2022 encoded no table */
+#define CT_SJIS 10001 /* 2 byte Shift-JIS encoded JIS no table */
+
+
+/* Character set flags */
+
+#define CF_PRIMARY 0x1 /* primary name for this charset */
+#define CF_DISPLAY 0x2 /* charset used in displays */
+#define CF_POSTING 0x4 /* charset used in email posting */
+#define CF_UNSUPRT 0x8 /* charset unsupported (can't convert to it) */
+#define CF_NOEMAIL 0x10 /* charset not used in email */
+
+
+/* UTF-7 engine states */
+
+#define U7_ASCII 0 /* ASCII character */
+#define U7_PLUS 1 /* plus seen */
+#define U7_UNICODE 2 /* Unicode characters */
+#define U7_MINUS 3 /* absorbed minus seen */
+
+/* Function prototypes */
+
+typedef unsigned long (*ucs4cn_t) (unsigned long c);
+typedef unsigned long (*ucs4de_t) (unsigned long c,void **more);
+
+SCRIPT *utf8_script (char *script);
+const CHARSET *utf8_charset (char *charset);
+char *utf8_badcharset (char *charset);
+long utf8_text (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,long flags);
+long utf8_text_cs (SIZEDTEXT *text,const CHARSET *cs,SIZEDTEXT *ret,
+ ucs4cn_t cv,ucs4de_t de);
+long utf8_cstext (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,
+ unsigned long errch);
+long utf8_cstocstext (SIZEDTEXT *text,char *sc,SIZEDTEXT *ret,char *dc,
+ unsigned long errch);
+unsigned short *utf8_rmap (char *charset);
+unsigned short *utf8_rmap_cs (const CHARSET *cs);
+unsigned short *utf8_rmap_gen (const CHARSET *cs,unsigned short *oldmap);
+long utf8_rmaptext (SIZEDTEXT *text,unsigned short *rmap,SIZEDTEXT *ret,
+ unsigned long errch,long iso2022jp);
+unsigned long utf8_rmapsize (SIZEDTEXT *text,unsigned short *rmap,
+ unsigned long errch,long iso2022jp);
+long ucs4_rmaptext (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ SIZEDTEXT *ret,unsigned long errch);
+long ucs4_rmaplen (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ unsigned long errch);
+long ucs4_rmapbuf (unsigned char *t,unsigned long *ucs4,unsigned long len,
+ unsigned short *rmap,unsigned long errch);
+unsigned long utf8_get (unsigned char **s,unsigned long *i);
+unsigned long utf8_get_raw (unsigned char **s,unsigned long *i);
+unsigned long ucs4_cs_get (CHARSET *cs,unsigned char **s,unsigned long *i);
+unsigned long *utf8_csvalidmap (char *charsets[]);
+const CHARSET *utf8_infercharset (SIZEDTEXT *src);
+long utf8_validate (unsigned char *s,unsigned long i);
+void utf8_text_1byte0 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_1byte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_1byte8 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_euc (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_dbyte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_dbyte2 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_sjis (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_2022 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf7 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf8 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_ucs2 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_ucs4 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf16 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+unsigned long utf8_size (unsigned long c);
+unsigned char *utf8_put (unsigned char *s,unsigned long c);
+unsigned long ucs4_titlecase (unsigned long c);
+long ucs4_width (unsigned long c);
+long utf8_strwidth (unsigned char *s);
+long utf8_textwidth (SIZEDTEXT *utf8);
+unsigned long ucs4_decompose (unsigned long c,void **more);
+unsigned long ucs4_decompose_recursive (unsigned long c,void **more);
diff --git a/imap/src/c-client/utf8aux.c b/imap/src/c-client/utf8aux.c
new file mode 100644
index 00000000..5138987b
--- /dev/null
+++ b/imap/src/c-client/utf8aux.c
@@ -0,0 +1,449 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 auxillary routines (c-client and MIME2 support)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 12 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* Convert charset labelled stringlist to UTF-8 in place
+ * Accepts: string list
+ * charset
+ */
+
+static void utf8_stringlist (STRINGLIST *st,char *charset)
+{
+ SIZEDTEXT txt;
+ /* convert entire stringstruct */
+ if (st) do if (utf8_text (&st->text,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &st->text.data);
+ st->text.data = txt.data; /* transfer this text */
+ st->text.size = txt.size;
+ } while (st = st->next);
+}
+
+
+/* Convert charset labelled searchpgm to UTF-8 in place
+ * Accepts: search program
+ * charset
+ */
+
+void utf8_searchpgm (SEARCHPGM *pgm,char *charset)
+{
+ SIZEDTEXT txt;
+ SEARCHHEADER *hl;
+ SEARCHOR *ol;
+ SEARCHPGMLIST *pl;
+ if (pgm) { /* must have a search program */
+ utf8_stringlist (pgm->bcc,charset);
+ utf8_stringlist (pgm->cc,charset);
+ utf8_stringlist (pgm->from,charset);
+ utf8_stringlist (pgm->to,charset);
+ utf8_stringlist (pgm->subject,charset);
+ for (hl = pgm->header; hl; hl = hl->next) {
+ if (utf8_text (&hl->line,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &hl->line.data);
+ hl->line.data = txt.data;
+ hl->line.size = txt.size;
+ }
+ if (utf8_text (&hl->text,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &hl->text.data);
+ hl->text.data = txt.data;
+ hl->text.size = txt.size;
+ }
+ }
+ utf8_stringlist (pgm->body,charset);
+ utf8_stringlist (pgm->text,charset);
+ for (ol = pgm->or; ol; ol = ol->next) {
+ utf8_searchpgm (ol->first,charset);
+ utf8_searchpgm (ol->second,charset);
+ }
+ for (pl = pgm->not; pl; pl = pl->next) utf8_searchpgm (pl->pgm,charset);
+ utf8_stringlist (pgm->return_path,charset);
+ utf8_stringlist (pgm->sender,charset);
+ utf8_stringlist (pgm->reply_to,charset);
+ utf8_stringlist (pgm->in_reply_to,charset);
+ utf8_stringlist (pgm->message_id,charset);
+ utf8_stringlist (pgm->newsgroups,charset);
+ utf8_stringlist (pgm->followup_to,charset);
+ utf8_stringlist (pgm->references,charset);
+ }
+}
+
+/* Convert MIME-2 sized text to UTF-8
+ * Accepts: source sized text
+ * charset
+ * flags (same as utf8_text())
+ * Returns: T if successful, NIL if failure
+ */
+
+#define MINENCWORD 9
+#define MAXENCWORD 75
+
+/* This resizing algorithm is stupid, but hopefully it should never be triggered
+ * except for a pathological header. The main concern is that we don't get a
+ * buffer overflow.
+ */
+
+#define DSIZE 65536 /* real headers should never be this big */
+#define FUZZ 10 /* paranoia fuzz */
+
+long utf8_mime2text (SIZEDTEXT *src,SIZEDTEXT *dst,long flags)
+{
+ unsigned char *s,*se,*e,*ee,*t,*te;
+ char *cs,*ce,*ls;
+ SIZEDTEXT txt,rtxt;
+ unsigned long i;
+ size_t dsize = min (DSIZE,((src->size / 4) + 1) * 9);
+ /* always create buffer if canonicalizing */
+ dst->data = (flags & U8T_CANONICAL) ?
+ (unsigned char *) fs_get ((size_t) dsize) : NIL;
+ dst->size = 0; /* nothing written yet */
+ /* look for encoded words */
+ for (s = src->data, se = src->data + src->size; s < se; s++) {
+ if (((se - s) > MINENCWORD) && (*s == '=') && (s[1] == '?') &&
+ (cs = (char *) mime2_token (s+2,se,(unsigned char **) &ce)) &&
+ (e = mime2_token ((unsigned char *) ce+1,se,&ee)) &&
+ (te = mime2_text (t = e+2,se)) && (ee == e + 1) &&
+ ((te - s) < MAXENCWORD)) {
+ if (mime2_decode (e,t,te,&txt)) {
+ *ce = '\0'; /* temporarily tie off charset */
+ if (ls = strchr (cs,'*')) *ls = '\0';
+ /* convert to UTF-8 as best we can */
+ if (!utf8_text (&txt,cs,&rtxt,flags)) utf8_text (&txt,NIL,&rtxt,flags);
+ if (dst->data) { /* make sure existing buffer fits */
+ while (dsize <= (dst->size + rtxt.size + FUZZ)) {
+ dsize += DSIZE; /* kick it up */
+ fs_resize ((void **) &dst->data,dsize);
+ }
+ }
+ else { /* make a new buffer */
+ while (dsize <= (dst->size + rtxt.size)) dsize += DSIZE;
+ memcpy (dst->data = (unsigned char *) fs_get (dsize),src->data,
+ dst->size = s - src->data);
+ }
+ for (i = 0; i < rtxt.size; i++) dst->data[dst->size++] = rtxt.data[i];
+
+ /* all done with converted text */
+ if (rtxt.data != txt.data) fs_give ((void **) &rtxt.data);
+ if (ls) *ls = '*'; /* restore language tag delimiter */
+ *ce = '?'; /* restore charset delimiter */
+ /* all done with decoded text */
+ fs_give ((void **) &txt.data);
+ s = te+1; /* continue scan after encoded word */
+ /* skip leading whitespace */
+ for (t = s + 1; (t < se) && ((*t == ' ') || (*t == '\t')); t++);
+ /* see if likely continuation encoded word */
+ if (t < (se - MINENCWORD)) switch (*t) {
+ case '=': /* possible encoded word? */
+ if (t[1] == '?') s = t - 1;
+ break;
+ case '\015': /* CR, eat a following LF */
+ if (t[1] == '\012') t++;
+ case '\012': /* possible end of logical line */
+ if ((t[1] == ' ') || (t[1] == '\t')) {
+ do t++;
+ while ((t < (se - MINENCWORD)) && ((t[1] == ' ')||(t[1] == '\t')));
+ if ((t < (se - MINENCWORD)) && (t[1] == '=') && (t[2] == '?'))
+ s = t; /* definitely looks like continuation */
+ }
+ }
+ }
+ else { /* restore original text */
+ if (dst->data) fs_give ((void **) &dst->data);
+ dst->data = src->data;
+ dst->size = src->size;
+ return NIL; /* syntax error: MIME-2 decoding failure */
+ }
+ }
+ else do if (dst->data) { /* stash ASCII characters until LWSP */
+ if (dsize < (dst->size + FUZZ)) {
+ dsize += DSIZE; /* kick it up */
+ fs_resize ((void **) &dst->data,dsize);
+ }
+ /* kludge: assumes ASCII doesn't decompose and titlecases to one byte */
+ dst->data[dst->size++] = (flags & U8T_CASECANON) ?
+ (unsigned char) ucs4_titlecase (*s) : *s;
+ }
+ while ((*s != ' ') && (*s != '\t') && (*s != '\015') && (*s != '\012') &&
+ (++s < se));
+ }
+ if (dst->data) dst->data[dst->size] = '\0';
+ else { /* nothing converted, return identity */
+ dst->data = src->data;
+ dst->size = src->size;
+ }
+ return T; /* success */
+}
+
+/* Decode MIME-2 text
+ * Accepts: Encoding
+ * text
+ * text end
+ * destination sized text
+ * Returns: T if successful, else NIL
+ */
+
+long mime2_decode (unsigned char *e,unsigned char *t,unsigned char *te,
+ SIZEDTEXT *txt)
+{
+ unsigned char *q;
+ txt->data = NIL; /* initially no returned data */
+ switch (*e) { /* dispatch based upon encoding */
+ case 'Q': case 'q': /* sort-of QUOTED-PRINTABLE */
+ txt->data = (unsigned char *) fs_get ((size_t) (te - t) + 1);
+ for (q = t,txt->size = 0; q < te; q++) switch (*q) {
+ case '=': /* quoted character */
+ /* both must be hex */
+ if (!isxdigit (q[1]) || !isxdigit (q[2])) {
+ fs_give ((void **) &txt->data);
+ return NIL; /* syntax error: bad quoted character */
+ }
+ /* assemble character */
+ txt->data[txt->size++] = hex2byte (q[1],q[2]);
+ q += 2; /* advance past quoted character */
+ break;
+ case '_': /* convert to space */
+ txt->data[txt->size++] = ' ';
+ break;
+ default: /* ordinary character */
+ txt->data[txt->size++] = *q;
+ break;
+ }
+ txt->data[txt->size] = '\0';
+ break;
+ case 'B': case 'b': /* BASE64 */
+ if (txt->data = (unsigned char *) rfc822_base64 (t,te - t,&txt->size))
+ break;
+ default: /* any other encoding is unknown */
+ return NIL; /* syntax error: unknown encoding */
+ }
+ return T;
+}
+
+/* Get MIME-2 token from encoded word
+ * Accepts: current text pointer
+ * text limit pointer
+ * pointer to returned end pointer
+ * Returns: current text pointer & end pointer if success, else NIL
+ */
+
+unsigned char *mime2_token (unsigned char *s,unsigned char *se,
+ unsigned char **t)
+{
+ for (*t = s; **t != '?'; ++*t) {
+ if ((*t < se) && isgraph (**t)) switch (**t) {
+ case '(': case ')': case '<': case '>': case '@': case ',': case ';':
+ case ':': case '\\': case '"': case '/': case '[': case ']': case '.':
+ case '=':
+ return NIL; /* none of these are valid in tokens */
+ }
+ else return NIL; /* out of text or CTL or space */
+ }
+ return s;
+}
+
+
+/* Get MIME-2 text from encoded word
+ * Accepts: current text pointer
+ * text limit pointer
+ * pointer to returned end pointer
+ * Returns: end pointer if success, else NIL
+ */
+
+unsigned char *mime2_text (unsigned char *s,unsigned char *se)
+{
+ unsigned char *t = se - 1;
+ /* search for closing ?, make sure valid */
+ while ((s < t) && (*s != '?') && isgraph (*s++));
+ return ((s < t) && (*s == '?') && (s[1] == '=') &&
+ ((se == (s + 2)) || (s[2] == ' ') || (s[2] == '\t') ||
+ (s[2] == '\015') || (s[2] == '\012'))) ? s : NIL;
+}
+
+/* Convert UTF-16 string to Modified Base64
+ * Accepts: destination pointer
+ * source string
+ * source length in octets
+ * Returns: updated destination pointer
+ */
+
+static unsigned char *utf16_to_mbase64 (unsigned char *t,unsigned char *s,
+ size_t i)
+{
+ char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+ *t++ = '&'; /* write shift-in */
+ while (i >= 3) { /* process tuplets */
+ *t++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *t++ = v[((s[0] << 4) + (s[1] >> 4)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *t++ = v[((s[1] << 2) + (s[2] >> 6)) & 0x3f];
+ *t++ = v[s[2] & 0x3f]; /* byte 4: low 6 bits (3) */
+ s += 3;
+ i -= 3;
+ }
+ if (i) {
+ *t++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *t++ = v[((s[0] << 4) + (--i ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2) */
+ if (i) *t++ = v[(s[1] << 2) & 0x3f];
+ }
+ *t++ = '-'; /* write shift-out */
+ return t;
+}
+
+
+/* Poot a UTF-16 value to a buffer
+ * Accepts: buffer pointer
+ * value
+ * Returns: updated pointer
+ */
+
+static unsigned char *utf16_poot (unsigned char *s,unsigned long c)
+{
+ *s++ = (unsigned char) (c >> 8);
+ *s++ = (unsigned char) (c & 0xff);
+ return s;
+}
+
+/* Convert UTF-8 to Modified UTF-7
+ * Accepts: UTF-8 string
+ * Returns: Modified UTF-7 string on success, NIL if invalid UTF-8
+ */
+
+#define MAXUNIUTF8 4 /* maximum length of Unicode UTF-8 sequence */
+
+unsigned char *utf8_to_mutf7 (unsigned char *src)
+{
+ unsigned char *u16buf,*utf16;
+ unsigned char *ret,*t;
+ unsigned long j,c;
+ unsigned char *s = src;
+ unsigned long i = 0;
+ int nonascii = 0;
+ while (*s) { /* pass one: count destination octets */
+ if (*s & 0x80) { /* non-ASCII character? */
+ j = MAXUNIUTF8; /* get single UCS-4 codepoint */
+ if ((c = utf8_get (&s,&j)) & U8G_ERROR) return NIL;
+ /* tally number of UTF-16 octets */
+ nonascii += (c & U8GM_NONBMP) ? 4 : 2;
+ }
+ else { /* ASCII character */
+ if (nonascii) { /* add pending Modified BASE64 size + shifts */
+ i += ((nonascii / 3) * 4) + ((j = nonascii % 3) ? j + 1 : 0) + 2;
+ nonascii = 0; /* back to ASCII */
+ }
+ if (*s == '&') i += 2; /* two octets if the escape */
+ else ++i; /* otherwise just count another octet */
+ ++s; /* advance to next source octet */
+ }
+ }
+ if (nonascii) /* add pending Modified BASE64 size + shifts */
+ i += ((nonascii / 3) * 4) + ((j = nonascii % 3) ? j + 1 : 0) + 2;
+
+ /* create return buffer */
+ t = ret = (unsigned char *) fs_get (i + 1);
+ /* and scratch buffer */
+ utf16 = u16buf = (unsigned char *) fs_get (i + 1);
+ for (s = src; *s;) { /* pass two: copy destination octets */
+ if (*s & 0x80) { /* non-ASCII character? */
+ j = MAXUNIUTF8; /* get single UCS-4 codepoint */
+ if ((c = utf8_get (&s,&j)) & U8G_ERROR) return NIL;
+ if (c & U8GM_NONBMP) { /* non-BMP? */
+ c -= UTF16_BASE; /* yes, convert to surrogate */
+ utf16 = utf16_poot (utf16_poot (utf16,(c >> UTF16_SHIFT)+UTF16_SURRH),
+ (c & UTF16_MASK) + UTF16_SURRL);
+ }
+ else utf16 = utf16_poot (utf16,c);
+ }
+ else { /* ASCII character */
+ if (utf16 != u16buf) { /* add pending Modified BASE64 size + shifts */
+ t = utf16_to_mbase64 (t,u16buf,utf16 - u16buf);
+ utf16 = u16buf; /* reset buffer */
+ }
+ *t++ = *s; /* copy the character */
+ if (*s == '&') *t++ = '-';/* special sequence if the escape */
+ ++s; /* advance to next source octet */
+ }
+ }
+ /* add pending Modified BASE64 size + shifts */
+ if (utf16 != u16buf) t = utf16_to_mbase64 (t,u16buf,utf16 - u16buf);
+ *t = '\0'; /* tie off destination */
+ if (i != (t - ret)) fatal ("utf8_to_mutf7 botch");
+ fs_give ((void **) &u16buf);
+ return ret;
+}
+
+/* Convert Modified UTF-7 to UTF-8
+ * Accepts: Modified UTF-7 string
+ * Returns: UTF-8 string on success, NIL if invalid Modified UTF-7
+ */
+
+unsigned char *utf8_from_mutf7 (unsigned char *src)
+{
+ SIZEDTEXT utf8,utf7;
+ unsigned char *s;
+ int mbase64 = 0;
+ /* disallow bogus strings */
+ if (mail_utf7_valid (src)) return NIL;
+ /* initialize SIZEDTEXTs */
+ memset (&utf7,0,sizeof (SIZEDTEXT));
+ memset (&utf8,0,sizeof (SIZEDTEXT));
+ /* make copy of source */
+ for (s = cpytxt (&utf7,src,strlen (src)); *s; ++s) switch (*s) {
+ case '&': /* Modified UTF-7 uses & instead of + */
+ *s = '+';
+ mbase64 = T; /* note that we are in Modified BASE64 */
+ break;
+ case '+': /* temporarily swap text + to & */
+ if (!mbase64) *s = '&';
+ break;
+ case '-': /* shift back to ASCII */
+ mbase64 = NIL;
+ break;
+ case ',': /* Modified UTF-7 uses , instead of / ... */
+ if (mbase64) *s = '/'; /* ...in Modified BASE64 */
+ break;
+ }
+ /* do the conversion */
+ utf8_text_utf7 (&utf7,&utf8,NIL,NIL);
+ /* no longer need copy of source */
+ fs_give ((void **) &utf7.data);
+ /* post-process: switch & and + */
+ for (s = utf8.data; *s; ++s) switch (*s) {
+ case '&':
+ *s = '+';
+ break;
+ case '+':
+ *s = '&';
+ break;
+ }
+ return utf8.data;
+}
diff --git a/imap/src/c-client/utf8aux.h b/imap/src/c-client/utf8aux.h
new file mode 100644
index 00000000..beb55057
--- /dev/null
+++ b/imap/src/c-client/utf8aux.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 auxillary routines (c-client and MIME2 support)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 9 October 2007
+ */
+
+
+/* Following routines are in utf8aux.c as these depend upon c-client.
+ * Splitting these routines out makes it possible for pico to link with utf8.o
+ * and a few rump routines (e.g., fs_get()) but not all the rest of c-client
+ * (which pico does not need).
+ */
+
+void utf8_searchpgm (SEARCHPGM *pgm,char *charset);
+long utf8_mime2text (SIZEDTEXT *src,SIZEDTEXT *dst,long flags);
+unsigned char *mime2_token (unsigned char *s,unsigned char *se,
+ unsigned char **t);
+unsigned char *mime2_text (unsigned char *s,unsigned char *se);
+long mime2_decode (unsigned char *e,unsigned char *t,unsigned char *te,
+ SIZEDTEXT *txt);
+unsigned char *utf8_to_mutf7 (unsigned char *src);
+unsigned char *utf8_from_mutf7 (unsigned char *src);
diff --git a/imap/src/charset/big5.c b/imap/src/charset/big5.c
new file mode 100644
index 00000000..2bd4a35e
--- /dev/null
+++ b/imap/src/charset/big5.c
@@ -0,0 +1,2016 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BIG-5 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Big-5 is the de-facto industrial standard of the Republic of China
+ * (Taiwan), and is commonly used in the Hong Kong Special Administrative
+ * Region of the People's Republic of China (mainland China).
+ */
+
+#define BASE_BIG5_KU 0xa1
+#define BASE_BIG5_TEN_0 0x40
+#define BASE_BIG5_TEN_1 0xa1
+#define MAX_BIG5_KU 89
+#define MAX_BIG5_TEN_0 63
+#define MAX_BIG5_TEN_1 94
+
+
+#define BIG5TOUNICODE(c,c1,ku,ten) \
+ (((ku = c - BASE_BIG5_KU) < MAX_BIG5_KU) ? \
+ ((c1 & 0x80) ? \
+ (((ten = c1 - BASE_BIG5_TEN_1) < MAX_BIG5_TEN_1) ? \
+ big5tab[ku].plane1[ten] : UBOGON) : \
+ (((ten = c1 - BASE_BIG5_TEN_0) < MAX_BIG5_TEN_0) ? \
+ big5tab[ku].plane0[ten] : UBOGON)) : UBOGON)
+
+
+typedef struct big5_ku {
+ unsigned short plane0[MAX_BIG5_TEN_0];
+ unsigned short plane1[MAX_BIG5_TEN_1];
+} BIG5;
+
+
+static const BIG5 big5tab[MAX_BIG5_KU] = {
+ { /* ku 01 */
+ {
+ 0x3000,0xff0c,0x3001,0x3002,0xff0e,0x2022,0xff1b,0xff1a,0xff1f,0xff01,
+ 0xfe30,0x2026,0x2025,0xfe50,0xff64,0xfe52,0x00b7,0xfe54,0xfe55,0xfe56,
+ 0xfe57,0xff5c,0x2013,0xfe31,0x2014,0xfe33,0xfffd,0xfe34,0xfe4f,0xff08,
+ 0xff09,0xfe35,0xfe36,0xff5b,0xff5d,0xfe37,0xfe38,0x3014,0x3015,0xfe39,
+ 0xfe3a,0x3010,0x3011,0xfe3b,0xfe3c,0x300a,0x300b,0xfe3d,0xfe3e,0x3008,
+ 0x3009,0xfe3f,0xfe40,0x300c,0x300d,0xfe41,0xfe42,0x300e,0x300f,0xfe43,
+ 0xfe44,0xfe59,0xfe5a
+ },{
+ 0xfe5b,0xfe5c,0xfe5d,0xfe5e,0x2018,0x2019,0x201c,0x201d,0x301d,0x301e,
+ 0x2035,0x2032,0xff03,0xff06,0xff0a,0x203b,0x00a7,0x3003,0x25cb,0x25cf,
+ 0x25b3,0x25b2,0x25ce,0x2606,0x2605,0x25c7,0x25c6,0x25a1,0x25a0,0x25bd,
+ 0x25bc,0x32a3,0x2105,0x203e,0xfffd,0xff3f,0xfffd,0xfe49,0xfe4a,0xfe4d,
+ 0xfe4e,0xfe4b,0xfe4c,0xfe5f,0xfe60,0xfe61,0xff0b,0xff0d,0x00d7,0x00f7,
+ 0x00b1,0x221a,0xff1c,0xff1e,0xff1d,0x2266,0x2267,0x2260,0x221e,0x2252,
+ 0x2261,0xfe62,0xfe63,0xfe64,0xfe65,0xfe66,0x223c,0x2229,0x222a,0x22a5,
+ 0x2220,0x221f,0x22bf,0x33d2,0x33d1,0x222b,0x222e,0x2235,0x2234,0x2640,
+ 0x2642,0x2641,0x2609,0x2191,0x2193,0x2190,0x2192,0x2196,0x2197,0x2199,
+ 0x2198,0x2225,0x2223,0xfffd
+ }
+ },
+ { /* ku 02 */
+ {
+ 0xfffd,0xff0f,0xff3c,0xff04,0x00a5,0x3012,0x00a2,0x00a3,0xff05,0xff20,
+ 0x2103,0x2109,0xfe69,0xfe6a,0xfe6b,0x33d5,0x339c,0x339d,0x339e,0x33ce,
+ 0x33a1,0x338e,0x338f,0x33c4,0x00b0,0x5159,0x515b,0x515e,0x515d,0x5161,
+ 0x5163,0x55e7,0x74e9,0x7cce,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,
+ 0x2587,0x2588,0x258f,0x258e,0x258d,0x258c,0x258b,0x258a,0x2589,0x253c,
+ 0x2534,0x252c,0x2524,0x251c,0x2594,0x2500,0x2502,0x2595,0x250c,0x2510,
+ 0x2514,0x2518,0x256d
+ },{
+ 0x256e,0x2570,0x256f,0x2550,0x255e,0x256a,0x2561,0x25e2,0x25e3,0x25e5,
+ 0x25e4,0x2571,0x2572,0x2573,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,
+ 0xff16,0xff17,0xff18,0xff19,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,
+ 0x2166,0x2167,0x2168,0x2169,0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,
+ 0x3027,0x3028,0x3029,0xfffd,0x5344,0xfffd,0xff21,0xff22,0xff23,0xff24,
+ 0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,
+ 0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,
+ 0xff39,0xff3a,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,
+ 0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,
+ 0xff53,0xff54,0xff55,0xff56
+ }
+ },
+ { /* ku 03 */
+ {
+ 0xff57,0xff58,0xff59,0xff5a,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,
+ 0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,
+ 0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x3105,0x3106,0x3107,0x3108,0x3109,0x310a,0x310b,0x310c,
+ 0x310d,0x310e,0x310f
+ },{
+ 0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,
+ 0x311a,0x311b,0x311c,0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,
+ 0x3124,0x3125,0x3126,0x3127,0x3128,0x3129,0x02d9,0x02c9,0x02ca,0x02c7,
+ 0x02cb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ },
+ { /* ku 04 */
+ {
+ 0x4e00,0x4e59,0x4e01,0x4e03,0x4e43,0x4e5d,0x4e86,0x4e8c,0x4eba,0x513f,
+ 0x5165,0x516b,0x51e0,0x5200,0x5201,0x529b,0x5315,0x5341,0x535c,0x53c8,
+ 0x4e09,0x4e0b,0x4e08,0x4e0a,0x4e2b,0x4e38,0x51e1,0x4e45,0x4e48,0x4e5f,
+ 0x4e5e,0x4e8e,0x4ea1,0x5140,0x5203,0x52fa,0x5343,0x53c9,0x53e3,0x571f,
+ 0x58eb,0x5915,0x5927,0x5973,0x5b50,0x5b51,0x5b53,0x5bf8,0x5c0f,0x5c22,
+ 0x5c38,0x5c71,0x5ddd,0x5de5,0x5df1,0x5df2,0x5df3,0x5dfe,0x5e72,0x5efe,
+ 0x5f0b,0x5f13,0x624d
+ },{
+ 0x4e11,0x4e10,0x4e0d,0x4e2d,0x4e30,0x4e39,0x4e4b,0x5c39,0x4e88,0x4e91,
+ 0x4e95,0x4e92,0x4e94,0x4ea2,0x4ec1,0x4ec0,0x4ec3,0x4ec6,0x4ec7,0x4ecd,
+ 0x4eca,0x4ecb,0x4ec4,0x5143,0x5141,0x5167,0x516d,0x516e,0x516c,0x5197,
+ 0x51f6,0x5206,0x5207,0x5208,0x52fb,0x52fe,0x52ff,0x5316,0x5339,0x5348,
+ 0x5347,0x5345,0x535e,0x5384,0x53cb,0x53ca,0x53cd,0x58ec,0x5929,0x592b,
+ 0x592a,0x592d,0x5b54,0x5c11,0x5c24,0x5c3a,0x5c6f,0x5df4,0x5e7b,0x5eff,
+ 0x5f14,0x5f15,0x5fc3,0x6208,0x6236,0x624b,0x624e,0x652f,0x6587,0x6597,
+ 0x65a4,0x65b9,0x65e5,0x66f0,0x6708,0x6728,0x6b20,0x6b62,0x6b79,0x6bcb,
+ 0x6bd4,0x6bdb,0x6c0f,0x6c34,0x706b,0x722a,0x7236,0x723b,0x7247,0x7259,
+ 0x725b,0x72ac,0x738b,0x4e19
+ }
+ },
+ { /* ku 05 */
+ {
+ 0x4e16,0x4e15,0x4e14,0x4e18,0x4e3b,0x4e4d,0x4e4f,0x4e4e,0x4ee5,0x4ed8,
+ 0x4ed4,0x4ed5,0x4ed6,0x4ed7,0x4ee3,0x4ee4,0x4ed9,0x4ede,0x5145,0x5144,
+ 0x5189,0x518a,0x51ac,0x51f9,0x51fa,0x51f8,0x520a,0x52a0,0x529f,0x5305,
+ 0x5306,0x5317,0x531d,0x4edf,0x534a,0x5349,0x5361,0x5360,0x536f,0x536e,
+ 0x53bb,0x53ef,0x53e4,0x53f3,0x53ec,0x53ee,0x53e9,0x53e8,0x53fc,0x53f8,
+ 0x53f5,0x53eb,0x53e6,0x53ea,0x53f2,0x53f1,0x53f0,0x53e5,0x53ed,0x53fb,
+ 0x56db,0x56da,0x5916
+ },{
+ 0x592e,0x5931,0x5974,0x5976,0x5b55,0x5b83,0x5c3c,0x5de8,0x5de7,0x5de6,
+ 0x5e02,0x5e03,0x5e73,0x5e7c,0x5f01,0x5f18,0x5f17,0x5fc5,0x620a,0x6253,
+ 0x6254,0x6252,0x6251,0x65a5,0x65e6,0x672e,0x672c,0x672a,0x672b,0x672d,
+ 0x6b63,0x6bcd,0x6c11,0x6c10,0x6c38,0x6c41,0x6c40,0x6c3e,0x72af,0x7384,
+ 0x7389,0x74dc,0x74e6,0x7518,0x751f,0x7528,0x7529,0x7530,0x7531,0x7532,
+ 0x7533,0x758b,0x767d,0x76ae,0x76bf,0x76ee,0x77db,0x77e2,0x77f3,0x793a,
+ 0x79be,0x7a74,0x7acb,0x4e1e,0x4e1f,0x4e52,0x4e53,0x4e69,0x4e99,0x4ea4,
+ 0x4ea6,0x4ea5,0x4eff,0x4f09,0x4f19,0x4f0a,0x4f15,0x4f0d,0x4f10,0x4f11,
+ 0x4f0f,0x4ef2,0x4ef6,0x4efb,0x4ef0,0x4ef3,0x4efd,0x4f01,0x4f0b,0x5149,
+ 0x5147,0x5146,0x5148,0x5168
+ }
+ },
+ { /* ku 06 */
+ {
+ 0x5171,0x518d,0x51b0,0x5217,0x5211,0x5212,0x520e,0x5216,0x52a3,0x5308,
+ 0x5321,0x5320,0x5370,0x5371,0x5409,0x540f,0x540c,0x540a,0x5410,0x5401,
+ 0x540b,0x5404,0x5411,0x540d,0x5408,0x5403,0x540e,0x5406,0x5412,0x56e0,
+ 0x56de,0x56dd,0x5733,0x5730,0x5728,0x572d,0x572c,0x572f,0x5729,0x5919,
+ 0x591a,0x5937,0x5938,0x5984,0x5978,0x5983,0x597d,0x5979,0x5982,0x5981,
+ 0x5b57,0x5b58,0x5b87,0x5b88,0x5b85,0x5b89,0x5bfa,0x5c16,0x5c79,0x5dde,
+ 0x5e06,0x5e76,0x5e74
+ },{
+ 0x5f0f,0x5f1b,0x5fd9,0x5fd6,0x620e,0x620c,0x620d,0x6210,0x6263,0x625b,
+ 0x6258,0x6536,0x65e9,0x65e8,0x65ec,0x65ed,0x66f2,0x66f3,0x6709,0x673d,
+ 0x6734,0x6731,0x6735,0x6b21,0x6b64,0x6b7b,0x6c16,0x6c5d,0x6c57,0x6c59,
+ 0x6c5f,0x6c60,0x6c50,0x6c55,0x6c61,0x6c5b,0x6c4d,0x6c4e,0x7070,0x725f,
+ 0x725d,0x767e,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f8a,0x7fbd,0x8001,0x8003,
+ 0x800c,0x8012,0x8033,0x807f,0x8089,0x808b,0x808c,0x81e3,0x81ea,0x81f3,
+ 0x81fc,0x820c,0x821b,0x821f,0x826e,0x8272,0x827e,0x866b,0x8840,0x884c,
+ 0x8863,0x897f,0x9621,0x4e32,0x4ea8,0x4f4d,0x4f4f,0x4f47,0x4f57,0x4f5e,
+ 0x4f34,0x4f5b,0x4f55,0x4f30,0x4f50,0x4f51,0x4f3d,0x4f3a,0x4f38,0x4f43,
+ 0x4f54,0x4f3c,0x4f46,0x4f63
+ }
+ },
+ { /* ku 07 */
+ {
+ 0x4f5c,0x4f60,0x4f2f,0x4f4e,0x4f36,0x4f59,0x4f5d,0x4f48,0x4f5a,0x514c,
+ 0x514b,0x514d,0x5175,0x51b6,0x51b7,0x5225,0x5224,0x5229,0x522a,0x5228,
+ 0x52ab,0x52a9,0x52aa,0x52ac,0x5323,0x5373,0x5375,0x541d,0x542d,0x541e,
+ 0x543e,0x5426,0x544e,0x5427,0x5446,0x5443,0x5433,0x5448,0x5442,0x541b,
+ 0x5429,0x544a,0x5439,0x543b,0x5438,0x542e,0x5435,0x5436,0x5420,0x543c,
+ 0x5440,0x5431,0x542b,0x541f,0x542c,0x56ea,0x56f0,0x56e4,0x56eb,0x574a,
+ 0x5751,0x5740,0x574d
+ },{
+ 0x5747,0x574e,0x573e,0x5750,0x574f,0x573b,0x58ef,0x593e,0x599d,0x5992,
+ 0x59a8,0x599e,0x59a3,0x5999,0x5996,0x598d,0x59a4,0x5993,0x598a,0x59a5,
+ 0x5b5d,0x5b5c,0x5b5a,0x5b5b,0x5b8c,0x5b8b,0x5b8f,0x5c2c,0x5c40,0x5c41,
+ 0x5c3f,0x5c3e,0x5c90,0x5c91,0x5c94,0x5c8c,0x5deb,0x5e0c,0x5e8f,0x5e87,
+ 0x5e8a,0x5ef7,0x5f04,0x5f1f,0x5f64,0x5f62,0x5f77,0x5f79,0x5fd8,0x5fcc,
+ 0x5fd7,0x5fcd,0x5ff1,0x5feb,0x5ff8,0x5fea,0x6212,0x6211,0x6284,0x6297,
+ 0x6296,0x6280,0x6276,0x6289,0x626d,0x628a,0x627c,0x627e,0x6279,0x6273,
+ 0x6292,0x626f,0x6298,0x626e,0x6295,0x6293,0x6291,0x6286,0x6539,0x653b,
+ 0x6538,0x65f1,0x66f4,0x675f,0x674e,0x674f,0x6750,0x6751,0x675c,0x6756,
+ 0x675e,0x6749,0x6746,0x6760
+ }
+ },
+ { /* ku 08 */
+ {
+ 0x6753,0x6757,0x6b65,0x6bcf,0x6c42,0x6c5e,0x6c99,0x6c81,0x6c88,0x6c89,
+ 0x6c85,0x6c9b,0x6c6a,0x6c7a,0x6c90,0x6c70,0x6c8c,0x6c68,0x6c96,0x6c92,
+ 0x6c7d,0x6c83,0x6c72,0x6c7e,0x6c74,0x6c86,0x6c76,0x6c8d,0x6c94,0x6c98,
+ 0x6c82,0x7076,0x707c,0x707d,0x7078,0x7262,0x7261,0x7260,0x72c4,0x72c2,
+ 0x7396,0x752c,0x752b,0x7537,0x7538,0x7682,0x76ef,0x77e3,0x79c1,0x79c0,
+ 0x79bf,0x7a76,0x7cfb,0x7f55,0x8096,0x8093,0x809d,0x8098,0x809b,0x809a,
+ 0x80b2,0x826f,0x8292
+ },{
+ 0x828b,0x828d,0x898b,0x89d2,0x8a00,0x8c37,0x8c46,0x8c55,0x8c9d,0x8d64,
+ 0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fc2,0x8fc6,0x8fc5,0x8fc4,
+ 0x5de1,0x9091,0x90a2,0x90aa,0x90a6,0x90a3,0x9149,0x91c6,0x91cc,0x9632,
+ 0x962e,0x9631,0x962a,0x962c,0x4e26,0x4e56,0x4e73,0x4e8b,0x4e9b,0x4e9e,
+ 0x4eab,0x4eac,0x4f6f,0x4f9d,0x4f8d,0x4f73,0x4f7f,0x4f6c,0x4f9b,0x4f8b,
+ 0x4f86,0x4f83,0x4f70,0x4f75,0x4f88,0x4f69,0x4f7b,0x4f96,0x4f7e,0x4f8f,
+ 0x4f91,0x4f7a,0x5154,0x5152,0x5155,0x5169,0x5177,0x5176,0x5178,0x51bd,
+ 0x51fd,0x523b,0x5238,0x5237,0x523a,0x5230,0x522e,0x5236,0x5241,0x52be,
+ 0x52bb,0x5352,0x5354,0x5353,0x5351,0x5366,0x5377,0x5378,0x5379,0x53d6,
+ 0x53d4,0x53d7,0x5473,0x5475
+ }
+ },
+ { /* ku 09 */
+ {
+ 0x5496,0x5478,0x5495,0x5480,0x547b,0x5477,0x5484,0x5492,0x5486,0x547c,
+ 0x5490,0x5471,0x5476,0x548c,0x549a,0x5462,0x5468,0x548b,0x547d,0x548e,
+ 0x56fa,0x5783,0x5777,0x576a,0x5769,0x5761,0x5766,0x5764,0x577c,0x591c,
+ 0x5949,0x5947,0x5948,0x5944,0x5954,0x59be,0x59bb,0x59d4,0x59b9,0x59ae,
+ 0x59d1,0x59c6,0x59d0,0x59cd,0x59cb,0x59d3,0x59ca,0x59af,0x59b3,0x59d2,
+ 0x59c5,0x5b5f,0x5b64,0x5b63,0x5b97,0x5b9a,0x5b98,0x5b9c,0x5b99,0x5b9b,
+ 0x5c1a,0x5c48,0x5c45
+ },{
+ 0x5c46,0x5cb7,0x5ca1,0x5cb8,0x5ca9,0x5cab,0x5cb1,0x5cb3,0x5e18,0x5e1a,
+ 0x5e16,0x5e15,0x5e1b,0x5e11,0x5e78,0x5e9a,0x5e97,0x5e9c,0x5e95,0x5e96,
+ 0x5ef6,0x5f26,0x5f27,0x5f29,0x5f80,0x5f81,0x5f7f,0x5f7c,0x5fdd,0x5fe0,
+ 0x5ffd,0x5ff5,0x5fff,0x600f,0x6014,0x602f,0x6035,0x6016,0x602a,0x6015,
+ 0x6021,0x6027,0x6029,0x602b,0x601b,0x6216,0x6215,0x623f,0x623e,0x6240,
+ 0x627f,0x62c9,0x62cc,0x62c4,0x62bf,0x62c2,0x62b9,0x62d2,0x62db,0x62ab,
+ 0x62d3,0x62d4,0x62cb,0x62c8,0x62a8,0x62bd,0x62bc,0x62d0,0x62d9,0x62c7,
+ 0x62cd,0x62b5,0x62da,0x62b1,0x62d8,0x62d6,0x62d7,0x62c6,0x62ac,0x62ce,
+ 0x653e,0x65a7,0x65bc,0x65fa,0x6614,0x6613,0x660c,0x6606,0x6602,0x660e,
+ 0x6600,0x660f,0x6615,0x660a
+ }
+ },
+ { /* ku 0a */
+ {
+ 0x6607,0x670d,0x670b,0x676d,0x678b,0x6795,0x6771,0x679c,0x6773,0x6777,
+ 0x6787,0x679d,0x6797,0x676f,0x6770,0x677f,0x6789,0x677e,0x6790,0x6775,
+ 0x679a,0x6793,0x677c,0x676a,0x6772,0x6b23,0x6b66,0x6b67,0x6b7f,0x6c13,
+ 0x6c1b,0x6ce3,0x6ce8,0x6cf3,0x6cb1,0x6ccc,0x6ce5,0x6cb3,0x6cbd,0x6cbe,
+ 0x6cbc,0x6ce2,0x6cab,0x6cd5,0x6cd3,0x6cb8,0x6cc4,0x6cb9,0x6cc1,0x6cae,
+ 0x6cd7,0x6cc5,0x6cf1,0x6cbf,0x6cbb,0x6ce1,0x6cdb,0x6cca,0x6cac,0x6cef,
+ 0x6cdc,0x6cd6,0x6ce0
+ },{
+ 0x7095,0x708e,0x7092,0x708a,0x7099,0x722c,0x722d,0x7238,0x7248,0x7267,
+ 0x7269,0x72c0,0x72ce,0x72d9,0x72d7,0x72d0,0x73a9,0x73a8,0x739f,0x73ab,
+ 0x73a5,0x753d,0x759d,0x7599,0x759a,0x7684,0x76c2,0x76f2,0x76f4,0x77e5,
+ 0x77fd,0x793e,0x7940,0x7941,0x79c9,0x79c8,0x7a7a,0x7a79,0x7afa,0x7cfe,
+ 0x7f54,0x7f8c,0x7f8b,0x8005,0x80ba,0x80a5,0x80a2,0x80b1,0x80a1,0x80ab,
+ 0x80a9,0x80b4,0x80aa,0x80af,0x81e5,0x81fe,0x820d,0x82b3,0x829d,0x8299,
+ 0x82ad,0x82bd,0x829f,0x82b9,0x82b1,0x82ac,0x82a5,0x82af,0x82b8,0x82a3,
+ 0x82b0,0x82be,0x82b7,0x864e,0x8671,0x521d,0x8868,0x8ecb,0x8fce,0x8fd4,
+ 0x8fd1,0x90b5,0x90b8,0x90b1,0x90b6,0x91c7,0x91d1,0x9577,0x9580,0x961c,
+ 0x9640,0x963f,0x963b,0x9644
+ }
+ },
+ { /* ku 0b */
+ {
+ 0x9642,0x96b9,0x96e8,0x9752,0x975e,0x4e9f,0x4ead,0x4eae,0x4fe1,0x4fb5,
+ 0x4faf,0x4fbf,0x4fe0,0x4fd1,0x4fcf,0x4fdd,0x4fc3,0x4fb6,0x4fd8,0x4fdf,
+ 0x4fca,0x4fd7,0x4fae,0x4fd0,0x4fc4,0x4fc2,0x4fda,0x4fce,0x4fde,0x4fb7,
+ 0x5157,0x5192,0x5191,0x51a0,0x524e,0x5243,0x524a,0x524d,0x524c,0x524b,
+ 0x5247,0x52c7,0x52c9,0x52c3,0x52c1,0x530d,0x5357,0x537b,0x539a,0x53db,
+ 0x54ac,0x54c0,0x54a8,0x54ce,0x54c9,0x54b8,0x54a6,0x54b3,0x54c7,0x54c2,
+ 0x54bd,0x54aa,0x54c1
+ },{
+ 0x54c4,0x54c8,0x54af,0x54ab,0x54b1,0x54bb,0x54a9,0x54a7,0x54bf,0x56ff,
+ 0x5782,0x578b,0x57a0,0x57a3,0x57a2,0x57ce,0x57ae,0x5793,0x5955,0x5951,
+ 0x594f,0x594e,0x5950,0x59dc,0x59d8,0x59ff,0x59e3,0x59e8,0x5a03,0x59e5,
+ 0x59ea,0x59da,0x59e6,0x5a01,0x59fb,0x5b69,0x5ba3,0x5ba6,0x5ba4,0x5ba2,
+ 0x5ba5,0x5c01,0x5c4e,0x5c4f,0x5c4d,0x5c4b,0x5cd9,0x5cd2,0x5df7,0x5e1d,
+ 0x5e25,0x5e1f,0x5e7d,0x5ea0,0x5ea6,0x5efa,0x5f08,0x5f2d,0x5f65,0x5f88,
+ 0x5f85,0x5f8a,0x5f8b,0x5f87,0x5f8c,0x5f89,0x6012,0x601d,0x6020,0x6025,
+ 0x600e,0x6028,0x604d,0x6070,0x6068,0x6062,0x6046,0x6043,0x606c,0x606b,
+ 0x606a,0x6064,0x6241,0x62dc,0x6316,0x6309,0x62fc,0x62ed,0x6301,0x62ee,
+ 0x62fd,0x6307,0x62f1,0x62f7
+ }
+ },
+ { /* ku 0c */
+ {
+ 0x62ef,0x62ec,0x62fe,0x62f4,0x6311,0x6302,0x653f,0x6545,0x65ab,0x65bd,
+ 0x65e2,0x6625,0x662d,0x6620,0x6627,0x662f,0x661f,0x6628,0x6631,0x6624,
+ 0x66f7,0x67ff,0x67d3,0x67f1,0x67d4,0x67d0,0x67ec,0x67b6,0x67af,0x67f5,
+ 0x67e9,0x67ef,0x67c4,0x67d1,0x67b4,0x67da,0x67e5,0x67b8,0x67cf,0x67de,
+ 0x67f3,0x67b0,0x67d9,0x67e2,0x67dd,0x67d2,0x6b6a,0x6b83,0x6b86,0x6bb5,
+ 0x6bd2,0x6bd7,0x6c1f,0x6cc9,0x6d0b,0x6d32,0x6d2a,0x6d41,0x6d25,0x6d0c,
+ 0x6d31,0x6d1e,0x6d17
+ },{
+ 0x6d3b,0x6d3d,0x6d3e,0x6d36,0x6d1b,0x6cf5,0x6d39,0x6d27,0x6d38,0x6d29,
+ 0x6d2e,0x6d35,0x6d0e,0x6d2b,0x70ab,0x70ba,0x70b3,0x70ac,0x70af,0x70ad,
+ 0x70b8,0x70ae,0x70a4,0x7230,0x7272,0x726f,0x7274,0x72e9,0x72e0,0x72e1,
+ 0x73b7,0x73ca,0x73bb,0x73b2,0x73cd,0x73c0,0x73b3,0x751a,0x752d,0x754f,
+ 0x754c,0x754e,0x754b,0x75ab,0x75a4,0x75a5,0x75a2,0x75a3,0x7678,0x7686,
+ 0x7687,0x7688,0x76c8,0x76c6,0x76c3,0x76c5,0x7701,0x76f9,0x76f8,0x7709,
+ 0x770b,0x76fe,0x76fc,0x7707,0x77dc,0x7802,0x7814,0x780c,0x780d,0x7946,
+ 0x7949,0x7948,0x7947,0x79b9,0x79ba,0x79d1,0x79d2,0x79cb,0x7a7f,0x7a81,
+ 0x7aff,0x7afd,0x7c7d,0x7d02,0x7d05,0x7d00,0x7d09,0x7d07,0x7d04,0x7d06,
+ 0x7f38,0x7f8e,0x7fbf,0x8004
+ }
+ },
+ { /* ku 0d */
+ {
+ 0x8010,0x800d,0x8011,0x8036,0x80d6,0x80e5,0x80da,0x80c3,0x80c4,0x80cc,
+ 0x80e1,0x80db,0x80ce,0x80de,0x80e4,0x80dd,0x81f4,0x8222,0x82e7,0x8303,
+ 0x8305,0x82e3,0x82db,0x82e6,0x8304,0x82e5,0x8302,0x8309,0x82d2,0x82d7,
+ 0x82f1,0x8301,0x82dc,0x82d4,0x82d1,0x82de,0x82d3,0x82df,0x82ef,0x8306,
+ 0x8650,0x8679,0x867b,0x867a,0x884d,0x886b,0x8981,0x89d4,0x8a08,0x8a02,
+ 0x8a03,0x8c9e,0x8ca0,0x8d74,0x8d73,0x8db4,0x8ecd,0x8ecc,0x8ff0,0x8fe6,
+ 0x8fe2,0x8fea,0x8fe5
+ },{
+ 0x8fed,0x8feb,0x8fe4,0x8fe8,0x90ca,0x90ce,0x90c1,0x90c3,0x914b,0x914a,
+ 0x91cd,0x9582,0x9650,0x964b,0x964c,0x964d,0x9762,0x9769,0x97cb,0x97ed,
+ 0x97f3,0x9801,0x98a8,0x98db,0x98df,0x9996,0x9999,0x4e58,0x4eb3,0x500c,
+ 0x500d,0x5023,0x4fef,0x5026,0x5025,0x4ff8,0x5029,0x5016,0x5006,0x503c,
+ 0x501f,0x501a,0x5012,0x5011,0x4ffa,0x5000,0x5014,0x5028,0x4ff1,0x5021,
+ 0x500b,0x5019,0x5018,0x4ff3,0x4fee,0x502d,0x502a,0x4ffe,0x502b,0x5009,
+ 0x517c,0x51a4,0x51a5,0x51a2,0x51cd,0x51cc,0x51c6,0x51cb,0x5256,0x525c,
+ 0x5254,0x525b,0x525d,0x532a,0x537f,0x539f,0x539d,0x53df,0x54e8,0x5510,
+ 0x5501,0x5537,0x54fc,0x54e5,0x54f2,0x5506,0x54fa,0x5514,0x54e9,0x54ed,
+ 0x54e1,0x5509,0x54ee,0x54ea
+ }
+ },
+ { /* ku 0e */
+ {
+ 0x54e6,0x5527,0x5507,0x54fd,0x550f,0x5703,0x5704,0x57c2,0x57d4,0x57cb,
+ 0x57c3,0x5809,0x590f,0x5957,0x5958,0x595a,0x5a11,0x5a18,0x5a1c,0x5a1f,
+ 0x5a1b,0x5a13,0x59ec,0x5a20,0x5a23,0x5a29,0x5a25,0x5a0c,0x5a09,0x5b6b,
+ 0x5c58,0x5bb0,0x5bb3,0x5bb6,0x5bb4,0x5bae,0x5bb5,0x5bb9,0x5bb8,0x5c04,
+ 0x5c51,0x5c55,0x5c50,0x5ced,0x5cfd,0x5cfb,0x5cea,0x5ce8,0x5cf0,0x5cf6,
+ 0x5d01,0x5cf4,0x5dee,0x5e2d,0x5e2b,0x5eab,0x5ead,0x5ea7,0x5f31,0x5f92,
+ 0x5f91,0x5f90,0x6059
+ },{
+ 0x6063,0x6065,0x6050,0x6055,0x606d,0x6069,0x606f,0x6084,0x609f,0x609a,
+ 0x608d,0x6094,0x608c,0x6085,0x6096,0x6247,0x62f3,0x6308,0x62ff,0x634e,
+ 0x633e,0x632f,0x6355,0x6342,0x6346,0x634f,0x6349,0x633a,0x6350,0x633d,
+ 0x632a,0x632b,0x6328,0x634d,0x634c,0x6548,0x6549,0x6599,0x65c1,0x65c5,
+ 0x6642,0x6649,0x664f,0x6643,0x6652,0x664c,0x6645,0x6641,0x66f8,0x6714,
+ 0x6715,0x6717,0x6821,0x6838,0x6848,0x6846,0x6853,0x6839,0x6842,0x6854,
+ 0x6829,0x68b3,0x6817,0x684c,0x6851,0x683d,0x67f4,0x6850,0x6840,0x683c,
+ 0x6843,0x682a,0x6845,0x6813,0x6818,0x6841,0x6b8a,0x6b89,0x6bb7,0x6c23,
+ 0x6c27,0x6c28,0x6c26,0x6c24,0x6cf0,0x6d6a,0x6d95,0x6d88,0x6d87,0x6d66,
+ 0x6d78,0x6d77,0x6d59,0x6d93
+ }
+ },
+ { /* ku 0f */
+ {
+ 0x6d6c,0x6d89,0x6d6e,0x6d5a,0x6d74,0x6d69,0x6d8c,0x6d8a,0x6d79,0x6d85,
+ 0x6d65,0x6d94,0x70ca,0x70d8,0x70e4,0x70d9,0x70c8,0x70cf,0x7239,0x7279,
+ 0x72fc,0x72f9,0x72fd,0x72f8,0x72f7,0x7386,0x73ed,0x7409,0x73ee,0x73e0,
+ 0x73ea,0x73de,0x7554,0x755d,0x755c,0x755a,0x7559,0x75be,0x75c5,0x75c7,
+ 0x75b2,0x75b3,0x75bd,0x75bc,0x75b9,0x75c2,0x75b8,0x768b,0x76b0,0x76ca,
+ 0x76cd,0x76ce,0x7729,0x771f,0x7720,0x7728,0x77e9,0x7830,0x7827,0x7838,
+ 0x781d,0x7834,0x7837
+ },{
+ 0x7825,0x782d,0x7820,0x781f,0x7832,0x7955,0x7950,0x7960,0x795f,0x7956,
+ 0x795e,0x795d,0x7957,0x795a,0x79e4,0x79e3,0x79e7,0x79df,0x79e6,0x79e9,
+ 0x79d8,0x7a84,0x7a88,0x7ad9,0x7b06,0x7b11,0x7c89,0x7d21,0x7d17,0x7d0b,
+ 0x7d0a,0x7d20,0x7d22,0x7d14,0x7d10,0x7d15,0x7d1a,0x7d1c,0x7d0d,0x7d19,
+ 0x7d1b,0x7f3a,0x7f5f,0x7f94,0x7fc5,0x7fc1,0x8006,0x8018,0x8015,0x8019,
+ 0x8017,0x803d,0x803f,0x80f1,0x8102,0x80f0,0x8105,0x80ed,0x80f4,0x8106,
+ 0x80f8,0x80f3,0x8108,0x80fd,0x810a,0x80fc,0x80ef,0x81ed,0x81ec,0x8200,
+ 0x8210,0x822a,0x822b,0x8228,0x822c,0x82bb,0x832b,0x8352,0x8354,0x834a,
+ 0x8338,0x8350,0x8349,0x8335,0x8334,0x834f,0x8332,0x8339,0x8336,0x8317,
+ 0x8340,0x8331,0x8328,0x8343
+ }
+ },
+ { /* ku 10 */
+ {
+ 0x8654,0x868a,0x86aa,0x8693,0x86a4,0x86a9,0x868c,0x86a3,0x869c,0x8870,
+ 0x8877,0x8881,0x8882,0x887d,0x8879,0x8a18,0x8a10,0x8a0e,0x8a0c,0x8a15,
+ 0x8a0a,0x8a17,0x8a13,0x8a16,0x8a0f,0x8a11,0x8c48,0x8c7a,0x8c79,0x8ca1,
+ 0x8ca2,0x8d77,0x8eac,0x8ed2,0x8ed4,0x8ecf,0x8fb1,0x9001,0x9006,0x8ff7,
+ 0x9000,0x8ffa,0x8ff4,0x9003,0x8ffd,0x9005,0x8ff8,0x9095,0x90e1,0x90dd,
+ 0x90e2,0x9152,0x914d,0x914c,0x91d8,0x91dd,0x91d7,0x91dc,0x91d9,0x9583,
+ 0x9662,0x9663,0x9661
+ },{
+ 0x965b,0x965d,0x9664,0x9658,0x965e,0x96bb,0x98e2,0x99ac,0x9aa8,0x9ad8,
+ 0x9b25,0x9b32,0x9b3c,0x4e7e,0x507a,0x507d,0x505c,0x5047,0x5043,0x504c,
+ 0x505a,0x5049,0x5065,0x5076,0x504e,0x5055,0x5075,0x5074,0x5077,0x504f,
+ 0x500f,0x506f,0x506d,0x515c,0x5195,0x51f0,0x526a,0x526f,0x52d2,0x52d9,
+ 0x52d8,0x52d5,0x5310,0x530f,0x5319,0x533f,0x5340,0x533e,0x53c3,0x66fc,
+ 0x5546,0x556a,0x5566,0x5544,0x555e,0x5561,0x5543,0x554a,0x5531,0x5556,
+ 0x554f,0x5555,0x552f,0x5564,0x5538,0x552e,0x555c,0x552c,0x5563,0x5533,
+ 0x5541,0x5557,0x5708,0x570b,0x5709,0x57df,0x5805,0x580a,0x5806,0x57e0,
+ 0x57e4,0x57fa,0x5802,0x5835,0x57f7,0x57f9,0x5920,0x5962,0x5a36,0x5a41,
+ 0x5a49,0x5a66,0x5a6a,0x5a40
+ }
+ },
+ { /* ku 11 */
+ {
+ 0x5a3c,0x5a62,0x5a5a,0x5a46,0x5a4a,0x5b70,0x5bc7,0x5bc5,0x5bc4,0x5bc2,
+ 0x5bbf,0x5bc6,0x5c09,0x5c08,0x5c07,0x5c60,0x5c5c,0x5c5d,0x5d07,0x5d06,
+ 0x5d0e,0x5d1b,0x5d16,0x5d22,0x5d11,0x5d29,0x5d14,0x5d19,0x5d24,0x5d27,
+ 0x5d17,0x5de2,0x5e38,0x5e36,0x5e33,0x5e37,0x5eb7,0x5eb8,0x5eb6,0x5eb5,
+ 0x5ebe,0x5f35,0x5f37,0x5f57,0x5f6c,0x5f69,0x5f6b,0x5f97,0x5f99,0x5f9e,
+ 0x5f98,0x5fa1,0x5fa0,0x5f9c,0x607f,0x60a3,0x6089,0x60a0,0x60a8,0x60cb,
+ 0x60b4,0x60e6,0x60bd
+ },{
+ 0x60c5,0x60bb,0x60b5,0x60dc,0x60bc,0x60d8,0x60d5,0x60c6,0x60df,0x60b8,
+ 0x60da,0x60c7,0x621a,0x621b,0x6248,0x63a0,0x63a7,0x6372,0x6396,0x63a2,
+ 0x63a5,0x6377,0x6367,0x6398,0x63aa,0x6371,0x63a9,0x6389,0x6383,0x639b,
+ 0x636b,0x63a8,0x6384,0x6388,0x6399,0x63a1,0x63ac,0x6392,0x638f,0x6380,
+ 0x637b,0x6369,0x6368,0x637a,0x655d,0x6556,0x6551,0x6559,0x6557,0x555f,
+ 0x654f,0x6558,0x6555,0x6554,0x659c,0x659b,0x65ac,0x65cf,0x65cb,0x65cc,
+ 0x65ce,0x665d,0x665a,0x6664,0x6668,0x6666,0x665e,0x66f9,0x52d7,0x671b,
+ 0x6881,0x68af,0x68a2,0x6893,0x68b5,0x687f,0x6876,0x68b1,0x68a7,0x6897,
+ 0x68b0,0x6883,0x68c4,0x68ad,0x6886,0x6885,0x6894,0x689d,0x68a8,0x689f,
+ 0x68a1,0x6882,0x6b32,0x6bba
+ }
+ },
+ { /* ku 12 */
+ {
+ 0x6beb,0x6bec,0x6c2b,0x6d8e,0x6dbc,0x6df3,0x6dd9,0x6db2,0x6de1,0x6dcc,
+ 0x6de4,0x6dfb,0x6dfa,0x6e05,0x6dc7,0x6dcb,0x6daf,0x6dd1,0x6dae,0x6dde,
+ 0x6df9,0x6db8,0x6df7,0x6df5,0x6dc5,0x6dd2,0x6e1a,0x6db5,0x6dda,0x6deb,
+ 0x6dd8,0x6dea,0x6df1,0x6dee,0x6de8,0x6dc6,0x6dc4,0x6daa,0x6dec,0x6dbf,
+ 0x6de6,0x70f9,0x7109,0x710a,0x70fd,0x70ef,0x723d,0x727d,0x7281,0x731c,
+ 0x731b,0x7316,0x7313,0x7319,0x7387,0x7405,0x740a,0x7403,0x7406,0x73fe,
+ 0x740d,0x74e0,0x74f6
+ },{
+ 0x74f7,0x751c,0x7522,0x7565,0x7566,0x7562,0x7570,0x758f,0x75d4,0x75d5,
+ 0x75b5,0x75ca,0x75cd,0x768e,0x76d4,0x76d2,0x76db,0x7737,0x773e,0x773c,
+ 0x7736,0x7738,0x773a,0x786b,0x7843,0x784e,0x7965,0x7968,0x796d,0x79fb,
+ 0x7a92,0x7a95,0x7b20,0x7b28,0x7b1b,0x7b2c,0x7b26,0x7b19,0x7b1e,0x7b2e,
+ 0x7c92,0x7c97,0x7c95,0x7d46,0x7d43,0x7d71,0x7d2e,0x7d39,0x7d3c,0x7d40,
+ 0x7d30,0x7d33,0x7d44,0x7d2f,0x7d42,0x7d32,0x7d31,0x7f3d,0x7f9e,0x7f9a,
+ 0x7fcc,0x7fce,0x7fd2,0x801c,0x804a,0x8046,0x812f,0x8116,0x8123,0x812b,
+ 0x8129,0x8130,0x8124,0x8202,0x8235,0x8237,0x8236,0x8239,0x838e,0x839e,
+ 0x8398,0x8378,0x83a2,0x8396,0x83bd,0x83ab,0x8392,0x838a,0x8393,0x8389,
+ 0x83a0,0x8377,0x837b,0x837c
+ }
+ },
+ { /* ku 13 */
+ {
+ 0x8386,0x83a7,0x8655,0x5f6a,0x86c7,0x86c0,0x86b6,0x86c4,0x86b5,0x86c6,
+ 0x86cb,0x86b1,0x86af,0x86c9,0x8853,0x889e,0x8888,0x88ab,0x8892,0x8896,
+ 0x888d,0x888b,0x8993,0x898f,0x8a2a,0x8a1d,0x8a23,0x8a25,0x8a31,0x8a2d,
+ 0x8a1f,0x8a1b,0x8a22,0x8c49,0x8c5a,0x8ca9,0x8cac,0x8cab,0x8ca8,0x8caa,
+ 0x8ca7,0x8d67,0x8d66,0x8dbe,0x8dba,0x8edb,0x8edf,0x9019,0x900d,0x901a,
+ 0x9017,0x9023,0x901f,0x901d,0x9010,0x9015,0x901e,0x9020,0x900f,0x9022,
+ 0x9016,0x901b,0x9014
+ },{
+ 0x90e8,0x90ed,0x90fd,0x9157,0x91ce,0x91f5,0x91e6,0x91e3,0x91e7,0x91ed,
+ 0x91e9,0x9589,0x966a,0x9675,0x9673,0x9678,0x9670,0x9674,0x9676,0x9677,
+ 0x966c,0x96c0,0x96ea,0x96e9,0x7ae0,0x7adf,0x9802,0x9803,0x9b5a,0x9ce5,
+ 0x9e75,0x9e7f,0x9ea5,0x9ebb,0x50a2,0x508d,0x5085,0x5099,0x5091,0x5080,
+ 0x5096,0x5098,0x509a,0x6700,0x51f1,0x5272,0x5274,0x5275,0x5269,0x52de,
+ 0x52dd,0x52db,0x535a,0x53a5,0x557b,0x5580,0x55a7,0x557c,0x558a,0x559d,
+ 0x5598,0x5582,0x559c,0x55aa,0x5594,0x5587,0x558b,0x5583,0x55b3,0x55ae,
+ 0x559f,0x553e,0x55b2,0x559a,0x55bb,0x55ac,0x55b1,0x557e,0x5589,0x55ab,
+ 0x5599,0x570d,0x582f,0x582a,0x5834,0x5824,0x5830,0x5831,0x5821,0x581d,
+ 0x5820,0x58f9,0x58fa,0x5960
+ }
+ },
+ { /* ku 14 */
+ {
+ 0x5a77,0x5a9a,0x5a7f,0x5a92,0x5a9b,0x5aa7,0x5b73,0x5b71,0x5bd2,0x5bcc,
+ 0x5bd3,0x5bd0,0x5c0a,0x5c0b,0x5c31,0x5d4c,0x5d50,0x5d34,0x5d47,0x5dfd,
+ 0x5e45,0x5e3d,0x5e40,0x5e43,0x5e7e,0x5eca,0x5ec1,0x5ec2,0x5ec4,0x5f3c,
+ 0x5f6d,0x5fa9,0x5faa,0x5fa8,0x60d1,0x60e1,0x60b2,0x60b6,0x60e0,0x611c,
+ 0x6123,0x60fa,0x6115,0x60f0,0x60fb,0x60f4,0x6168,0x60f1,0x610e,0x60f6,
+ 0x6109,0x6100,0x6112,0x621f,0x6249,0x63a3,0x638c,0x63cf,0x63c0,0x63e9,
+ 0x63c9,0x63c6,0x63cd
+ },{
+ 0x63d2,0x63e3,0x63d0,0x63e1,0x63d6,0x63ed,0x63ee,0x6376,0x63f4,0x63ea,
+ 0x63db,0x6452,0x63da,0x63f9,0x655e,0x6566,0x6562,0x6563,0x6591,0x6590,
+ 0x65af,0x666e,0x6670,0x6674,0x6676,0x666f,0x6691,0x667a,0x667e,0x6677,
+ 0x66fe,0x66ff,0x671f,0x671d,0x68fa,0x68d5,0x68e0,0x68d8,0x68d7,0x6905,
+ 0x68df,0x68f5,0x68ee,0x68e7,0x68f9,0x68d2,0x68f2,0x68e3,0x68cb,0x68cd,
+ 0x690d,0x6912,0x690e,0x68c9,0x68da,0x696e,0x68fb,0x6b3e,0x6b3a,0x6b3d,
+ 0x6b98,0x6b96,0x6bbc,0x6bef,0x6c2e,0x6c2f,0x6c2c,0x6e2f,0x6e38,0x6e54,
+ 0x6e21,0x6e32,0x6e67,0x6e4a,0x6e20,0x6e25,0x6e23,0x6e1b,0x6e5b,0x6e58,
+ 0x6e24,0x6e56,0x6e6e,0x6e2d,0x6e26,0x6e6f,0x6e34,0x6e4d,0x6e3a,0x6e2c,
+ 0x6e43,0x6e1d,0x6e3e,0x6ecb
+ }
+ },
+ { /* ku 15 */
+ {
+ 0x6e89,0x6e19,0x6e4e,0x6e63,0x6e44,0x6e72,0x6e69,0x6e5f,0x7119,0x711a,
+ 0x7126,0x7130,0x7121,0x7136,0x716e,0x711c,0x724c,0x7284,0x7280,0x7336,
+ 0x7325,0x7334,0x7329,0x743a,0x742a,0x7433,0x7422,0x7425,0x7435,0x7436,
+ 0x7434,0x742f,0x741b,0x7426,0x7428,0x7525,0x7526,0x756b,0x756a,0x75e2,
+ 0x75db,0x75e3,0x75d9,0x75d8,0x75de,0x75e0,0x767b,0x767c,0x7696,0x7693,
+ 0x76b4,0x76dc,0x774f,0x77ed,0x785d,0x786c,0x786f,0x7a0d,0x7a08,0x7a0b,
+ 0x7a05,0x7a00,0x7a98
+ },{
+ 0x7a97,0x7a96,0x7ae5,0x7ae3,0x7b49,0x7b56,0x7b46,0x7b50,0x7b52,0x7b54,
+ 0x7b4d,0x7b4b,0x7b4f,0x7b51,0x7c9f,0x7ca5,0x7d5e,0x7d50,0x7d68,0x7d55,
+ 0x7d2b,0x7d6e,0x7d72,0x7d61,0x7d66,0x7d62,0x7d70,0x7d73,0x5584,0x7fd4,
+ 0x7fd5,0x800b,0x8052,0x8085,0x8155,0x8154,0x814b,0x8151,0x814e,0x8139,
+ 0x8146,0x813e,0x814c,0x8153,0x8174,0x8212,0x821c,0x83e9,0x8403,0x83f8,
+ 0x840d,0x83e0,0x83c5,0x840b,0x83c1,0x83ef,0x83f1,0x83f4,0x8457,0x840a,
+ 0x83f0,0x840c,0x83cc,0x83fd,0x83f2,0x83ca,0x8438,0x840e,0x8404,0x83dc,
+ 0x8407,0x83d4,0x83df,0x865b,0x86df,0x86d9,0x86ed,0x86d4,0x86db,0x86e4,
+ 0x86d0,0x86de,0x8857,0x88c1,0x88c2,0x88b1,0x8983,0x8996,0x8a3b,0x8a60,
+ 0x8a55,0x8a5e,0x8a3c,0x8a41
+ }
+ },
+ { /* ku 16 */
+ {
+ 0x8a54,0x8a5b,0x8a50,0x8a46,0x8a34,0x8a3a,0x8a36,0x8a56,0x8c61,0x8c82,
+ 0x8caf,0x8cbc,0x8cb3,0x8cbd,0x8cc1,0x8cbb,0x8cc0,0x8cb4,0x8cb7,0x8cb6,
+ 0x8cbf,0x8cb8,0x8d8a,0x8d85,0x8d81,0x8dce,0x8ddd,0x8dcb,0x8dda,0x8dd1,
+ 0x8dcc,0x8ddb,0x8dc6,0x8efb,0x8ef8,0x8efc,0x8f9c,0x902e,0x9035,0x9031,
+ 0x9038,0x9032,0x9036,0x9102,0x90f5,0x9109,0x90fe,0x9163,0x9165,0x91cf,
+ 0x9214,0x9215,0x9223,0x9209,0x921e,0x920d,0x9210,0x9207,0x9211,0x9594,
+ 0x958f,0x958b,0x9591
+ },{
+ 0x9593,0x9592,0x958e,0x968a,0x968e,0x968b,0x967d,0x9685,0x9686,0x968d,
+ 0x9672,0x9684,0x96c1,0x96c5,0x96c4,0x96c6,0x96c7,0x96ef,0x96f2,0x97cc,
+ 0x9805,0x9806,0x9808,0x98e7,0x98ea,0x98ef,0x98e9,0x98f2,0x98ed,0x99ae,
+ 0x99ad,0x9ec3,0x9ecd,0x9ed1,0x4e82,0x50ad,0x50b5,0x50b2,0x50b3,0x50c5,
+ 0x50be,0x50ac,0x50b7,0x50bb,0x50af,0x50c7,0x527f,0x5277,0x527d,0x52df,
+ 0x52e6,0x52e4,0x52e2,0x52e3,0x532f,0x55df,0x55e8,0x55d3,0x55e6,0x55ce,
+ 0x55dc,0x55c7,0x55d1,0x55e3,0x55e4,0x55ef,0x55da,0x55e1,0x55c5,0x55c6,
+ 0x55e5,0x55c9,0x5712,0x5713,0x585e,0x5851,0x5858,0x5857,0x585a,0x5854,
+ 0x586b,0x584c,0x586d,0x584a,0x5862,0x5852,0x584b,0x5967,0x5ac1,0x5ac9,
+ 0x5acc,0x5abe,0x5abd,0x5abc
+ }
+ },
+ { /* ku 17 */
+ {
+ 0x5ab3,0x5ac2,0x5ab2,0x5d69,0x5d6f,0x5e4c,0x5e79,0x5ec9,0x5ec8,0x5f12,
+ 0x5f59,0x5fac,0x5fae,0x611a,0x610f,0x6148,0x611f,0x60f3,0x611b,0x60f9,
+ 0x6101,0x6108,0x614e,0x614c,0x6144,0x614d,0x613e,0x6134,0x6127,0x610d,
+ 0x6106,0x6137,0x6221,0x6222,0x6413,0x643e,0x641e,0x642a,0x642d,0x643d,
+ 0x642c,0x640f,0x641c,0x6414,0x640d,0x6436,0x6416,0x6417,0x6406,0x656c,
+ 0x659f,0x65b0,0x6697,0x6689,0x6687,0x6688,0x6696,0x6684,0x6698,0x668d,
+ 0x6703,0x6994,0x696d
+ },{
+ 0x695a,0x6977,0x6960,0x6954,0x6975,0x6930,0x6982,0x694a,0x6968,0x696b,
+ 0x695e,0x6953,0x6979,0x6986,0x695d,0x6963,0x695b,0x6b47,0x6b72,0x6bc0,
+ 0x6bbf,0x6bd3,0x6bfd,0x6ea2,0x6eaf,0x6ed3,0x6eb6,0x6ec2,0x6e90,0x6e9d,
+ 0x6ec7,0x6ec5,0x6ea5,0x6e98,0x6ebc,0x6eba,0x6eab,0x6ed1,0x6e96,0x6e9c,
+ 0x6ec4,0x6ed4,0x6eaa,0x6ea7,0x6eb4,0x714e,0x7159,0x7169,0x7164,0x7149,
+ 0x7167,0x715c,0x716c,0x7166,0x714c,0x7165,0x715e,0x7146,0x7168,0x7156,
+ 0x723a,0x7252,0x7337,0x7345,0x733f,0x733e,0x746f,0x745a,0x7455,0x745f,
+ 0x745e,0x7441,0x743f,0x7459,0x745b,0x745c,0x7576,0x7578,0x7600,0x75f0,
+ 0x7601,0x75f2,0x75f1,0x75fa,0x75ff,0x75f4,0x75f3,0x76de,0x76df,0x775b,
+ 0x776b,0x7766,0x775e,0x7763
+ }
+ },
+ { /* ku 18 */
+ {
+ 0x7779,0x776a,0x776c,0x775c,0x7765,0x7768,0x7762,0x77ee,0x788e,0x78b0,
+ 0x7897,0x7898,0x788c,0x7889,0x787c,0x7891,0x7893,0x787f,0x797a,0x797f,
+ 0x7981,0x842c,0x79bd,0x7a1c,0x7a1a,0x7a20,0x7a14,0x7a1f,0x7a1e,0x7a9f,
+ 0x7aa0,0x7b77,0x7bc0,0x7b60,0x7b6e,0x7b67,0x7cb1,0x7cb3,0x7cb5,0x7d93,
+ 0x7d79,0x7d91,0x7d81,0x7d8f,0x7d5b,0x7f6e,0x7f69,0x7f6a,0x7f72,0x7fa9,
+ 0x7fa8,0x7fa4,0x8056,0x8058,0x8086,0x8084,0x8171,0x8170,0x8178,0x8165,
+ 0x816e,0x8173,0x816b
+ },{
+ 0x8179,0x817a,0x8166,0x8205,0x8247,0x8482,0x8477,0x843d,0x8431,0x8475,
+ 0x8466,0x846b,0x8449,0x846c,0x845b,0x843c,0x8435,0x8461,0x8463,0x8469,
+ 0x846d,0x8446,0x865e,0x865c,0x865f,0x86f9,0x8713,0x8708,0x8707,0x8700,
+ 0x86fe,0x86fb,0x8702,0x8703,0x8706,0x870a,0x8859,0x88df,0x88d4,0x88d9,
+ 0x88dc,0x88d8,0x88dd,0x88e1,0x88ca,0x88d5,0x88d2,0x899c,0x89e3,0x8a6b,
+ 0x8a72,0x8a73,0x8a66,0x8a69,0x8a70,0x8a87,0x8a7c,0x8a63,0x8aa0,0x8a71,
+ 0x8a85,0x8a6d,0x8a62,0x8a6e,0x8a6c,0x8a79,0x8a7b,0x8a3e,0x8a68,0x8c62,
+ 0x8c8a,0x8c89,0x8cca,0x8cc7,0x8cc8,0x8cc4,0x8cb2,0x8cc3,0x8cc2,0x8cc5,
+ 0x8de1,0x8ddf,0x8de8,0x8def,0x8df3,0x8dfa,0x8dea,0x8de4,0x8de6,0x8eb2,
+ 0x8f03,0x8f09,0x8efe,0x8f0a
+ }
+ },
+ { /* ku 19 */
+ {
+ 0x8f9f,0x8fb2,0x904b,0x904a,0x9053,0x9042,0x9054,0x903c,0x9055,0x9050,
+ 0x9047,0x904f,0x904e,0x904d,0x9051,0x903e,0x9041,0x9112,0x9117,0x916c,
+ 0x916a,0x9169,0x91c9,0x9237,0x9257,0x9238,0x923d,0x9240,0x923e,0x925b,
+ 0x924b,0x9264,0x9251,0x9234,0x9249,0x924d,0x9245,0x9239,0x923f,0x925a,
+ 0x9598,0x9698,0x9694,0x9695,0x96cd,0x96cb,0x96c9,0x96ca,0x96f7,0x96fb,
+ 0x96f9,0x96f6,0x9756,0x9774,0x9776,0x9810,0x9811,0x9813,0x980a,0x9812,
+ 0x980c,0x98fc,0x98f4
+ },{
+ 0x98fd,0x98fe,0x99b3,0x99b1,0x99b4,0x9ae1,0x9ce9,0x9e82,0x9f0e,0x9f13,
+ 0x9f20,0x50e7,0x50ee,0x50e5,0x50d6,0x50ed,0x50da,0x50d5,0x50cf,0x50d1,
+ 0x50f1,0x50ce,0x50e9,0x5162,0x51f3,0x5283,0x5282,0x5331,0x53ad,0x55fe,
+ 0x5600,0x561b,0x5617,0x55fd,0x5614,0x5606,0x5609,0x560d,0x560e,0x55f7,
+ 0x5616,0x561f,0x5608,0x5610,0x55f6,0x5718,0x5716,0x5875,0x587e,0x5883,
+ 0x5893,0x588a,0x5879,0x5885,0x587d,0x58fd,0x5925,0x5922,0x5924,0x596a,
+ 0x5969,0x5ae1,0x5ae6,0x5ae9,0x5ad7,0x5ad6,0x5ad8,0x5ae3,0x5b75,0x5bde,
+ 0x5be7,0x5be1,0x5be5,0x5be6,0x5be8,0x5be2,0x5be4,0x5bdf,0x5c0d,0x5c62,
+ 0x5d84,0x5d87,0x5e5b,0x5e63,0x5e55,0x5e57,0x5e54,0x5ed3,0x5ed6,0x5f0a,
+ 0x5f46,0x5f70,0x5fb9,0x6147
+ }
+ },
+ { /* ku 1a */
+ {
+ 0x613f,0x614b,0x6177,0x6162,0x6163,0x615f,0x615a,0x6158,0x6175,0x622a,
+ 0x6487,0x6458,0x6454,0x64a4,0x6478,0x645f,0x647a,0x6451,0x6467,0x6434,
+ 0x646d,0x647b,0x6572,0x65a1,0x65d7,0x65d6,0x66a2,0x66a8,0x669d,0x699c,
+ 0x69a8,0x6995,0x69c1,0x69ae,0x69d3,0x69cb,0x699b,0x69b7,0x69bb,0x69ab,
+ 0x69b4,0x69d0,0x69cd,0x69ad,0x69cc,0x69a6,0x69c3,0x69a3,0x6b49,0x6b4c,
+ 0x6c33,0x6f33,0x6f14,0x6efe,0x6f13,0x6ef4,0x6f29,0x6f3e,0x6f20,0x6f2c,
+ 0x6f0f,0x6f02,0x6f22
+ },{
+ 0x6eff,0x6eef,0x6f06,0x6f31,0x6f38,0x6f32,0x6f23,0x6f15,0x6f2b,0x6f2f,
+ 0x6f88,0x6f2a,0x6eec,0x6f01,0x6ef2,0x6ecc,0x6ef7,0x7194,0x7199,0x717d,
+ 0x718a,0x7184,0x7192,0x723e,0x7292,0x7296,0x7344,0x7350,0x7464,0x7463,
+ 0x746a,0x7470,0x746d,0x7504,0x7591,0x7627,0x760d,0x760b,0x7609,0x7613,
+ 0x76e1,0x76e3,0x7784,0x777d,0x777f,0x7761,0x78c1,0x789f,0x78a7,0x78b3,
+ 0x78a9,0x78a3,0x798e,0x798f,0x798d,0x7a2e,0x7a31,0x7aaa,0x7aa9,0x7aed,
+ 0x7aef,0x7ba1,0x7b95,0x7b8b,0x7b75,0x7b97,0x7b9d,0x7b94,0x7b8f,0x7bb8,
+ 0x7b87,0x7b84,0x7cb9,0x7cbd,0x7cbe,0x7dbb,0x7db0,0x7d9c,0x7dbd,0x7dbe,
+ 0x7da0,0x7dca,0x7db4,0x7db2,0x7db1,0x7dba,0x7da2,0x7dbf,0x7db5,0x7db8,
+ 0x7dad,0x7dd2,0x7dc7,0x7dac
+ }
+ },
+ { /* ku 1b */
+ {
+ 0x7f70,0x7fe0,0x7fe1,0x7fdf,0x805e,0x805a,0x8087,0x8150,0x8180,0x818f,
+ 0x8188,0x818a,0x817f,0x8182,0x81e7,0x81fa,0x8207,0x8214,0x821e,0x824b,
+ 0x84c9,0x84bf,0x84c6,0x84c4,0x8499,0x849e,0x84b2,0x849c,0x84cb,0x84b8,
+ 0x84c0,0x84d3,0x8490,0x84bc,0x84d1,0x84ca,0x873f,0x871c,0x873b,0x8722,
+ 0x8725,0x8734,0x8718,0x8755,0x8737,0x8729,0x88f3,0x8902,0x88f4,0x88f9,
+ 0x88f8,0x88fd,0x88e8,0x891a,0x88ef,0x8aa6,0x8a8c,0x8a9e,0x8aa3,0x8a8d,
+ 0x8aa1,0x8a93,0x8aa4
+ },{
+ 0x8aaa,0x8aa5,0x8aa8,0x8a98,0x8a91,0x8a9a,0x8aa7,0x8c6a,0x8c8d,0x8c8c,
+ 0x8cd3,0x8cd1,0x8cd2,0x8d6b,0x8d99,0x8d95,0x8dfc,0x8f14,0x8f12,0x8f15,
+ 0x8f13,0x8fa3,0x9060,0x9058,0x905c,0x9063,0x9059,0x905e,0x9062,0x905d,
+ 0x905b,0x9119,0x9118,0x911e,0x9175,0x9178,0x9177,0x9174,0x9278,0x9280,
+ 0x9285,0x9298,0x9296,0x927b,0x9293,0x929c,0x92a8,0x927c,0x9291,0x95a1,
+ 0x95a8,0x95a9,0x95a3,0x95a5,0x95a4,0x9699,0x969c,0x969b,0x96cc,0x96d2,
+ 0x9700,0x977c,0x9785,0x97f6,0x9817,0x9818,0x98af,0x98b1,0x9903,0x9905,
+ 0x990c,0x9909,0x99c1,0x9aaf,0x9ab0,0x9ae6,0x9b41,0x9b42,0x9cf4,0x9cf6,
+ 0x9cf3,0x9ebc,0x9f3b,0x9f4a,0x5104,0x5100,0x50fb,0x50f5,0x50f9,0x5102,
+ 0x5108,0x5109,0x5105,0x51dc
+ }
+ },
+ { /* ku 1c */
+ {
+ 0x5287,0x5288,0x5289,0x528d,0x528a,0x52f0,0x53b2,0x562e,0x563b,0x5639,
+ 0x5632,0x563f,0x5634,0x5629,0x5653,0x564e,0x5657,0x5674,0x5636,0x562f,
+ 0x5630,0x5880,0x589f,0x589e,0x58b3,0x589c,0x58ae,0x58a9,0x58a6,0x596d,
+ 0x5b09,0x5afb,0x5b0b,0x5af5,0x5b0c,0x5b08,0x5bee,0x5bec,0x5be9,0x5beb,
+ 0x5c64,0x5c65,0x5d9d,0x5d94,0x5e62,0x5e5f,0x5e61,0x5ee2,0x5eda,0x5edf,
+ 0x5edd,0x5ee3,0x5ee0,0x5f48,0x5f71,0x5fb7,0x5fb5,0x6176,0x6167,0x616e,
+ 0x615d,0x6155,0x6182
+ },{
+ 0x617c,0x6170,0x616b,0x617e,0x61a7,0x6190,0x61ab,0x618e,0x61ac,0x619a,
+ 0x61a4,0x6194,0x61ae,0x622e,0x6469,0x646f,0x6479,0x649e,0x64b2,0x6488,
+ 0x6490,0x64b0,0x64a5,0x6493,0x6495,0x64a9,0x6492,0x64ae,0x64ad,0x64ab,
+ 0x649a,0x64ac,0x6499,0x64a2,0x64b3,0x6575,0x6577,0x6578,0x66ae,0x66ab,
+ 0x66b4,0x66b1,0x6a23,0x6a1f,0x69e8,0x6a01,0x6a1e,0x6a19,0x69fd,0x6a21,
+ 0x6a13,0x6a0a,0x69f3,0x6a02,0x6a05,0x69ed,0x6a11,0x6b50,0x6b4e,0x6ba4,
+ 0x6bc5,0x6bc6,0x6f3f,0x6f7c,0x6f84,0x6f51,0x6f66,0x6f54,0x6f86,0x6f6d,
+ 0x6f5b,0x6f78,0x6f6e,0x6f8e,0x6f7a,0x6f70,0x6f64,0x6f97,0x6f58,0x6ed5,
+ 0x6f6f,0x6f60,0x6f5f,0x719f,0x71ac,0x71b1,0x71a8,0x7256,0x729b,0x734e,
+ 0x7357,0x7469,0x748b,0x7483
+ }
+ },
+ { /* ku 1d */
+ {
+ 0x747e,0x7480,0x757f,0x7620,0x7629,0x761f,0x7624,0x7626,0x7621,0x7622,
+ 0x769a,0x76ba,0x76e4,0x778e,0x7787,0x778c,0x7791,0x778b,0x78cb,0x78c5,
+ 0x78ba,0x78ca,0x78be,0x78d5,0x78bc,0x78d0,0x7a3f,0x7a3c,0x7a40,0x7a3d,
+ 0x7a37,0x7a3b,0x7aaf,0x7aae,0x7bad,0x7bb1,0x7bc4,0x7bb4,0x7bc6,0x7bc7,
+ 0x7bc1,0x7ba0,0x7bcc,0x7cca,0x7de0,0x7df4,0x7def,0x7dfb,0x7dd8,0x7dec,
+ 0x7ddd,0x7de8,0x7de3,0x7dda,0x7dde,0x7de9,0x7d9e,0x7dd9,0x7df2,0x7df9,
+ 0x7f75,0x7f77,0x7faf
+ },{
+ 0x7fe9,0x8026,0x819b,0x819c,0x819d,0x81a0,0x819a,0x8198,0x8517,0x853d,
+ 0x851a,0x84ee,0x852c,0x852d,0x8513,0x8511,0x8523,0x8521,0x8514,0x84ec,
+ 0x8525,0x84ff,0x8506,0x8782,0x8774,0x8776,0x8760,0x8766,0x8778,0x8768,
+ 0x8759,0x8757,0x874c,0x8753,0x885b,0x885d,0x8910,0x8907,0x8912,0x8913,
+ 0x8915,0x890a,0x8abc,0x8ad2,0x8ac7,0x8ac4,0x8a95,0x8acb,0x8af8,0x8ab2,
+ 0x8ac9,0x8ac2,0x8abf,0x8ab0,0x8ad6,0x8acd,0x8ab6,0x8ab9,0x8adb,0x8c4c,
+ 0x8c4e,0x8c6c,0x8ce0,0x8cde,0x8ce6,0x8ce4,0x8cec,0x8ced,0x8ce2,0x8ce3,
+ 0x8cdc,0x8cea,0x8ce1,0x8d6d,0x8d9f,0x8da3,0x8e2b,0x8e10,0x8e1d,0x8e22,
+ 0x8e0f,0x8e29,0x8e1f,0x8e21,0x8e1e,0x8eba,0x8f1d,0x8f1b,0x8f1f,0x8f29,
+ 0x8f26,0x8f2a,0x8f1c,0x8f1e
+ }
+ },
+ { /* ku 1e */
+ {
+ 0x8f25,0x9069,0x906e,0x9068,0x906d,0x9077,0x9130,0x912d,0x9127,0x9131,
+ 0x9187,0x9189,0x918b,0x9183,0x92c5,0x92bb,0x92b7,0x92ea,0x92ac,0x92e4,
+ 0x92c1,0x92b3,0x92bc,0x92d2,0x92c7,0x92f0,0x92b2,0x95ad,0x95b1,0x9704,
+ 0x9706,0x9707,0x9709,0x9760,0x978d,0x978b,0x978f,0x9821,0x982b,0x981c,
+ 0x98b3,0x990a,0x9913,0x9912,0x9918,0x99dd,0x99d0,0x99df,0x99db,0x99d1,
+ 0x99d5,0x99d2,0x99d9,0x9ab7,0x9aee,0x9aef,0x9b27,0x9b45,0x9b44,0x9b77,
+ 0x9b6f,0x9d06,0x9d09
+ },{
+ 0x9d03,0x9ea9,0x9ebe,0x9ece,0x58a8,0x9f52,0x5112,0x5118,0x5114,0x5110,
+ 0x5115,0x5180,0x51aa,0x51dd,0x5291,0x5293,0x52f3,0x5659,0x566b,0x5679,
+ 0x5669,0x5664,0x5678,0x566a,0x5668,0x5665,0x5671,0x566f,0x566c,0x5662,
+ 0x5676,0x58c1,0x58be,0x58c7,0x58c5,0x596e,0x5b1d,0x5b34,0x5b78,0x5bf0,
+ 0x5c0e,0x5f4a,0x61b2,0x6191,0x61a9,0x618a,0x61cd,0x61b6,0x61be,0x61ca,
+ 0x61c8,0x6230,0x64c5,0x64c1,0x64cb,0x64bb,0x64bc,0x64da,0x64c4,0x64c7,
+ 0x64c2,0x64cd,0x64bf,0x64d2,0x64d4,0x64be,0x6574,0x66c6,0x66c9,0x66b9,
+ 0x66c4,0x66c7,0x66b8,0x6a3d,0x6a38,0x6a3a,0x6a59,0x6a6b,0x6a58,0x6a39,
+ 0x6a44,0x6a62,0x6a61,0x6a4b,0x6a47,0x6a35,0x6a5f,0x6a48,0x6b59,0x6b77,
+ 0x6c05,0x6fc2,0x6fb1,0x6fa1
+ }
+ },
+ { /* ku 1f */
+ {
+ 0x6fc3,0x6fa4,0x6fc1,0x6fa7,0x6fb3,0x6fc0,0x6fb9,0x6fb6,0x6fa6,0x6fa0,
+ 0x6fb4,0x71be,0x71c9,0x71d0,0x71d2,0x71c8,0x71d5,0x71b9,0x71ce,0x71d9,
+ 0x71dc,0x71c3,0x71c4,0x7368,0x749c,0x74a3,0x7498,0x749f,0x749e,0x74e2,
+ 0x750c,0x750d,0x7634,0x7638,0x763a,0x76e7,0x76e5,0x77a0,0x779e,0x779f,
+ 0x77a5,0x78e8,0x78da,0x78ec,0x78e7,0x79a6,0x7a4d,0x7a4e,0x7a46,0x7a4c,
+ 0x7a4b,0x7aba,0x7bd9,0x7c11,0x7bc9,0x7be4,0x7bdb,0x7be1,0x7be9,0x7be6,
+ 0x7cd5,0x7cd6,0x7e0a
+ },{
+ 0x7e11,0x7e08,0x7e1b,0x7e23,0x7e1e,0x7e1d,0x7e09,0x7e10,0x7f79,0x7fb2,
+ 0x7ff0,0x7ff1,0x7fee,0x8028,0x81b3,0x81a9,0x81a8,0x81fb,0x8208,0x8258,
+ 0x8259,0x854a,0x8559,0x8548,0x8568,0x8569,0x8543,0x8549,0x856d,0x856a,
+ 0x855e,0x8783,0x879f,0x879e,0x87a2,0x878d,0x8861,0x892a,0x8932,0x8925,
+ 0x892b,0x8921,0x89aa,0x89a6,0x8ae6,0x8afa,0x8aeb,0x8af1,0x8b00,0x8adc,
+ 0x8ae7,0x8aee,0x8afe,0x8b01,0x8b02,0x8af7,0x8aed,0x8af3,0x8af6,0x8afc,
+ 0x8c6b,0x8c6d,0x8c93,0x8cf4,0x8e44,0x8e31,0x8e34,0x8e42,0x8e39,0x8e35,
+ 0x8f3b,0x8f2f,0x8f38,0x8f33,0x8fa8,0x8fa6,0x9075,0x9074,0x9078,0x9072,
+ 0x907c,0x907a,0x9134,0x9192,0x9320,0x9336,0x92f8,0x9333,0x932f,0x9322,
+ 0x92fc,0x932b,0x9304,0x931a
+ }
+ },
+ { /* ku 20 */
+ {
+ 0x9310,0x9326,0x9321,0x9315,0x932e,0x9319,0x95bb,0x96a7,0x96a8,0x96aa,
+ 0x96d5,0x970e,0x9711,0x9716,0x970d,0x9713,0x970f,0x975b,0x975c,0x9766,
+ 0x9798,0x9830,0x9838,0x983b,0x9837,0x982d,0x9839,0x9824,0x9910,0x9928,
+ 0x991e,0x991b,0x9921,0x991a,0x99ed,0x99e2,0x99f1,0x9ab8,0x9abc,0x9afb,
+ 0x9aed,0x9b28,0x9b91,0x9d15,0x9d23,0x9d26,0x9d28,0x9d12,0x9d1b,0x9ed8,
+ 0x9ed4,0x9f8d,0x9f9c,0x512a,0x511f,0x5121,0x5132,0x52f5,0x568e,0x5680,
+ 0x5690,0x5685,0x5687
+ },{
+ 0x568f,0x58d5,0x58d3,0x58d1,0x58ce,0x5b30,0x5b2a,0x5b24,0x5b7a,0x5c37,
+ 0x5c68,0x5dbc,0x5dba,0x5dbd,0x5db8,0x5e6b,0x5f4c,0x5fbd,0x61c9,0x61c2,
+ 0x61c7,0x61e6,0x61cb,0x6232,0x6234,0x64ce,0x64ca,0x64d8,0x64e0,0x64f0,
+ 0x64e6,0x64ec,0x64f1,0x64e2,0x64ed,0x6582,0x6583,0x66d9,0x66d6,0x6a80,
+ 0x6a94,0x6a84,0x6aa2,0x6a9c,0x6adb,0x6aa3,0x6a7e,0x6a97,0x6a90,0x6aa0,
+ 0x6b5c,0x6bae,0x6bda,0x6c08,0x6fd8,0x6ff1,0x6fdf,0x6fe0,0x6fdb,0x6fe4,
+ 0x6feb,0x6fef,0x6f80,0x6fec,0x6fe1,0x6fe9,0x6fd5,0x6fee,0x6ff0,0x71e7,
+ 0x71df,0x71ee,0x71e6,0x71e5,0x71ed,0x71ec,0x71f4,0x71e0,0x7235,0x7246,
+ 0x7370,0x7372,0x74a9,0x74b0,0x74a6,0x74a8,0x7646,0x7642,0x764c,0x76ea,
+ 0x77b3,0x77aa,0x77b0,0x77ac
+ }
+ },
+ { /* ku 21 */
+ {
+ 0x77a7,0x77ad,0x77ef,0x78f7,0x78fa,0x78f4,0x78ef,0x7901,0x79a7,0x79aa,
+ 0x7a57,0x7abf,0x7c07,0x7c0d,0x7bfe,0x7bf7,0x7c0c,0x7be0,0x7ce0,0x7cdc,
+ 0x7cde,0x7ce2,0x7cdf,0x7cd9,0x7cdd,0x7e2e,0x7e3e,0x7e46,0x7e37,0x7e32,
+ 0x7e43,0x7e2b,0x7e3d,0x7e31,0x7e45,0x7e41,0x7e34,0x7e39,0x7e48,0x7e35,
+ 0x7e3f,0x7e2f,0x7f44,0x7ff3,0x7ffc,0x8071,0x8072,0x8070,0x806f,0x8073,
+ 0x81c6,0x81c3,0x81ba,0x81c2,0x81c0,0x81bf,0x81bd,0x81c9,0x81be,0x81e8,
+ 0x8209,0x8271,0x85aa
+ },{
+ 0x8584,0x857e,0x859c,0x8591,0x8594,0x85af,0x859b,0x8587,0x85a8,0x858a,
+ 0x8667,0x87c0,0x87d1,0x87b3,0x87d2,0x87c6,0x87ab,0x87bb,0x87ba,0x87c8,
+ 0x87cb,0x893b,0x8936,0x8944,0x8938,0x893d,0x89ac,0x8b0e,0x8b17,0x8b19,
+ 0x8b1b,0x8b0a,0x8b20,0x8b1d,0x8b04,0x8b10,0x8c41,0x8c3f,0x8c73,0x8cfa,
+ 0x8cfd,0x8cfc,0x8cf8,0x8cfb,0x8da8,0x8e49,0x8e4b,0x8e48,0x8e4a,0x8f44,
+ 0x8f3e,0x8f42,0x8f45,0x8f3f,0x907f,0x907d,0x9084,0x9081,0x9082,0x9080,
+ 0x9139,0x91a3,0x919e,0x919c,0x934d,0x9382,0x9328,0x9375,0x934a,0x9365,
+ 0x934b,0x9318,0x937e,0x936c,0x935b,0x9370,0x935a,0x9354,0x95ca,0x95cb,
+ 0x95cc,0x95c8,0x95c6,0x96b1,0x96b8,0x96d6,0x971c,0x971e,0x97a0,0x97d3,
+ 0x9846,0x98b6,0x9935,0x9a01
+ }
+ },
+ { /* ku 22 */
+ {
+ 0x99ff,0x9bae,0x9bab,0x9baa,0x9bad,0x9d3b,0x9d3f,0x9e8b,0x9ecf,0x9ede,
+ 0x9edc,0x9edd,0x9edb,0x9f3e,0x9f4b,0x53e2,0x5695,0x56ae,0x58d9,0x58d8,
+ 0x5b38,0x5f5d,0x61e3,0x6233,0x64f4,0x64f2,0x64fe,0x6506,0x64fa,0x64fb,
+ 0x64f7,0x65b7,0x66dc,0x6726,0x6ab3,0x6aac,0x6ac3,0x6abb,0x6ab8,0x6ac2,
+ 0x6aae,0x6aaf,0x6b5f,0x6b78,0x6baf,0x7009,0x700b,0x6ffe,0x7006,0x6ffa,
+ 0x7011,0x700f,0x71fb,0x71fc,0x71fe,0x71f8,0x7377,0x7375,0x74a7,0x74bf,
+ 0x7515,0x7656,0x7658
+ },{
+ 0x7652,0x77bd,0x77bf,0x77bb,0x77bc,0x790e,0x79ae,0x7a61,0x7a62,0x7a60,
+ 0x7ac4,0x7ac5,0x7c2b,0x7c27,0x7c2a,0x7c1e,0x7c23,0x7c21,0x7ce7,0x7e54,
+ 0x7e55,0x7e5e,0x7e5a,0x7e61,0x7e52,0x7e59,0x7f48,0x7ff9,0x7ffb,0x8077,
+ 0x8076,0x81cd,0x81cf,0x820a,0x85cf,0x85a9,0x85cd,0x85d0,0x85c9,0x85b0,
+ 0x85ba,0x85b9,0x85a6,0x87ef,0x87ec,0x87f2,0x87e0,0x8986,0x89b2,0x89f4,
+ 0x8b28,0x8b39,0x8b2c,0x8b2b,0x8c50,0x8d05,0x8e59,0x8e63,0x8e66,0x8e64,
+ 0x8e5f,0x8e55,0x8ec0,0x8f49,0x8f4d,0x9087,0x9083,0x9088,0x91ab,0x91ac,
+ 0x91d0,0x9394,0x938a,0x9396,0x93a2,0x93b3,0x93ae,0x93ac,0x93b0,0x9398,
+ 0x939a,0x9397,0x95d4,0x95d6,0x95d0,0x95d5,0x96e2,0x96dc,0x96d9,0x96db,
+ 0x96de,0x9724,0x97a3,0x97a6
+ }
+ },
+ { /* ku 23 */
+ {
+ 0x97ad,0x97f9,0x984d,0x984f,0x984c,0x984e,0x9853,0x98ba,0x993e,0x993f,
+ 0x993d,0x992e,0x99a5,0x9a0e,0x9ac1,0x9b03,0x9b06,0x9b4f,0x9b4e,0x9b4d,
+ 0x9bca,0x9bc9,0x9bfd,0x9bc8,0x9bc0,0x9d51,0x9d5d,0x9d60,0x9ee0,0x9f15,
+ 0x9f2c,0x5133,0x56a5,0x58de,0x58df,0x58e2,0x5bf5,0x9f90,0x5eec,0x61f2,
+ 0x61f7,0x61f6,0x61f5,0x6500,0x650f,0x66e0,0x66dd,0x6ae5,0x6add,0x6ada,
+ 0x6ad3,0x701b,0x701f,0x7028,0x701a,0x701d,0x7015,0x7018,0x7206,0x720d,
+ 0x7258,0x72a2,0x7378
+ },{
+ 0x737a,0x74bd,0x74ca,0x74e3,0x7587,0x7586,0x765f,0x7661,0x77c7,0x7919,
+ 0x79b1,0x7a6b,0x7a69,0x7c3e,0x7c3f,0x7c38,0x7c3d,0x7c37,0x7c40,0x7e6b,
+ 0x7e6d,0x7e79,0x7e69,0x7e6a,0x7f85,0x7e73,0x7fb6,0x7fb9,0x7fb8,0x81d8,
+ 0x85e9,0x85dd,0x85ea,0x85d5,0x85e4,0x85e5,0x85f7,0x87fb,0x8805,0x880d,
+ 0x87f9,0x87fe,0x8960,0x895f,0x8956,0x895e,0x8b41,0x8b5c,0x8b58,0x8b49,
+ 0x8b5a,0x8b4e,0x8b4f,0x8b46,0x8b59,0x8d08,0x8d0a,0x8e7c,0x8e72,0x8e87,
+ 0x8e76,0x8e6c,0x8e7a,0x8e74,0x8f54,0x8f4e,0x8fad,0x908a,0x908b,0x91b1,
+ 0x91ae,0x93e1,0x93d1,0x93df,0x93c3,0x93c8,0x93dc,0x93dd,0x93d6,0x93e2,
+ 0x93cd,0x93d8,0x93e4,0x93d7,0x93e8,0x95dc,0x96b4,0x96e3,0x972a,0x9727,
+ 0x9761,0x97dc,0x97fb,0x985e
+ }
+ },
+ { /* ku 24 */
+ {
+ 0x9858,0x985b,0x98bc,0x9945,0x9949,0x9a16,0x9a19,0x9b0d,0x9be8,0x9be7,
+ 0x9bd6,0x9bdb,0x9d89,0x9d61,0x9d72,0x9d6a,0x9d6c,0x9e92,0x9e97,0x9e93,
+ 0x9eb4,0x52f8,0x56a8,0x56b7,0x56b6,0x56b4,0x56bc,0x58e4,0x5b40,0x5b43,
+ 0x5b7d,0x5bf6,0x5dc9,0x61f8,0x61fa,0x6518,0x6514,0x6519,0x66e6,0x6727,
+ 0x6aec,0x703e,0x7030,0x7032,0x7210,0x737b,0x74cf,0x7662,0x7665,0x7926,
+ 0x792a,0x792c,0x792b,0x7ac7,0x7af6,0x7c4c,0x7c43,0x7c4d,0x7cef,0x7cf0,
+ 0x8fae,0x7e7d,0x7e7c
+ },{
+ 0x7e82,0x7f4c,0x8000,0x81da,0x8266,0x85fb,0x85f9,0x8611,0x85fa,0x8606,
+ 0x860b,0x8607,0x860a,0x8814,0x8815,0x8964,0x89ba,0x89f8,0x8b70,0x8b6c,
+ 0x8b66,0x8b6f,0x8b5f,0x8b6b,0x8d0f,0x8d0d,0x8e89,0x8e81,0x8e85,0x8e82,
+ 0x91b4,0x91cb,0x9418,0x9403,0x93fd,0x95e1,0x9730,0x98c4,0x9952,0x9951,
+ 0x99a8,0x9a2b,0x9a30,0x9a37,0x9a35,0x9c13,0x9c0d,0x9e79,0x9eb5,0x9ee8,
+ 0x9f2f,0x9f5f,0x9f63,0x9f61,0x5137,0x5138,0x56c1,0x56c0,0x56c2,0x5914,
+ 0x5c6c,0x5dcd,0x61fc,0x61fe,0x651d,0x651c,0x6595,0x66e9,0x6afb,0x6b04,
+ 0x6afa,0x6bb2,0x704c,0x721b,0x72a7,0x74d6,0x74d4,0x7669,0x77d3,0x7c50,
+ 0x7e8f,0x7e8c,0x7fbc,0x8617,0x862d,0x861a,0x8823,0x8822,0x8821,0x881f,
+ 0x896a,0x896c,0x89bd,0x8b74
+ }
+ },
+ { /* ku 25 */
+ {
+ 0x8b77,0x8b7d,0x8d13,0x8e8a,0x8e8d,0x8e8b,0x8f5f,0x8faf,0x91ba,0x942e,
+ 0x9433,0x9435,0x943a,0x9438,0x9432,0x942b,0x95e2,0x9738,0x9739,0x9732,
+ 0x97ff,0x9867,0x9865,0x9957,0x9a45,0x9a43,0x9a40,0x9a3e,0x9acf,0x9b54,
+ 0x9b51,0x9c2d,0x9c25,0x9daf,0x9db4,0x9dc2,0x9db8,0x9e9d,0x9eef,0x9f19,
+ 0x9f5c,0x9f66,0x9f67,0x513c,0x513b,0x56c8,0x56ca,0x56c9,0x5b7f,0x5dd4,
+ 0x5dd2,0x5f4e,0x61ff,0x6524,0x6b0a,0x6b61,0x7051,0x7058,0x7380,0x74e4,
+ 0x758a,0x766e,0x766c
+ },{
+ 0x79b3,0x7c60,0x7c5f,0x807e,0x807d,0x81df,0x8972,0x896f,0x89fc,0x8b80,
+ 0x8d16,0x8d17,0x8e91,0x8e93,0x8f61,0x9148,0x9444,0x9451,0x9452,0x973d,
+ 0x973e,0x97c3,0x97c1,0x986b,0x9955,0x9a55,0x9a4d,0x9ad2,0x9b1a,0x9c49,
+ 0x9c31,0x9c3e,0x9c3b,0x9dd3,0x9dd7,0x9f34,0x9f6c,0x9f6a,0x9f94,0x56cc,
+ 0x5dd6,0x6200,0x6523,0x652b,0x652a,0x66ec,0x6b10,0x74da,0x7aca,0x7c64,
+ 0x7c63,0x7c65,0x7e93,0x7e96,0x7e94,0x81e2,0x8638,0x863f,0x8831,0x8b8a,
+ 0x9090,0x908f,0x9463,0x9460,0x9464,0x9768,0x986f,0x995c,0x9a5a,0x9a5b,
+ 0x9a57,0x9ad3,0x9ad4,0x9ad1,0x9c54,0x9c57,0x9c56,0x9de5,0x9e9f,0x9ef4,
+ 0x56d1,0x58e9,0x652c,0x705e,0x7671,0x7672,0x77d7,0x7f50,0x7f88,0x8836,
+ 0x8839,0x8862,0x8b93,0x8b92
+ }
+ },
+ { /* ku 26 */
+ {
+ 0x8b96,0x8277,0x8d1b,0x91c0,0x946a,0x9742,0x9748,0x9744,0x97c6,0x9870,
+ 0x9a5f,0x9b22,0x9b58,0x9c5f,0x9df9,0x9dfa,0x9e7c,0x9e7d,0x9f07,0x9f77,
+ 0x9f72,0x5ef3,0x6b16,0x7063,0x7c6c,0x7c6e,0x883b,0x89c0,0x8ea1,0x91c1,
+ 0x9472,0x9470,0x9871,0x995e,0x9ad6,0x9b23,0x9ecc,0x7064,0x77da,0x8b9a,
+ 0x9477,0x97c9,0x9a62,0x9a65,0x7e9c,0x8b9c,0x8eaa,0x91c5,0x947d,0x947e,
+ 0x947c,0x9c77,0x9c78,0x9ef7,0x8c54,0x947f,0x9e1a,0x7228,0x9a6a,0x9b31,
+ 0x9e1b,0x9e1e,0x7c72
+ },{
+ 0x30fe,0x309d,0x309e,0x3005,0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,
+ 0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,
+ 0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,
+ 0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,
+ 0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,
+ 0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,
+ 0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,
+ 0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,
+ 0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,0x30a1,0x30a2,0x30a3,
+ 0x30a4,0x30a5,0x30a6,0x30a7
+ }
+ },
+ { /* ku 27 */
+ {
+ 0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,
+ 0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,
+ 0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,
+ 0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,
+ 0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,
+ 0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,
+ 0x30e4,0x30e5,0x30e6
+ },{
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,0x0414,0x0415,0x0401,0x0416,
+ 0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,0x0423,0x0424,0x0425,0x0426,
+ 0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,0x0430,
+ 0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,
+ 0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,
+ 0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,
+ 0x044e,0x044f,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,
+ 0x2468,0x2469,0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,0x247b,
+ 0x247c,0x247d,UBOGON,UBOGON
+ }
+ },
+ { /* ku 28 */
+ {
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON
+ },{
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ },
+ { /* ku 29 */
+ {
+ 0x4e42,0x4e5c,0x51f5,0x531a,0x5382,0x4e07,0x4e0c,0x4e47,0x4e8d,0x56d7,
+ 0xfa0c,0x5c6e,0x5f73,0x4e0f,0x5187,0x4e0e,0x4e2e,0x4e93,0x4ec2,0x4ec9,
+ 0x4ec8,0x5198,0x52fc,0x536c,0x53b9,0x5720,0x5903,0x592c,0x5c10,0x5dff,
+ 0x65e1,0x6bb3,0x6bcc,0x6c14,0x723f,0x4e31,0x4e3c,0x4ee8,0x4edc,0x4ee9,
+ 0x4ee1,0x4edd,0x4eda,0x520c,0x531c,0x534c,0x5722,0x5723,0x5917,0x592f,
+ 0x5b81,0x5b84,0x5c12,0x5c3b,0x5c74,0x5c73,0x5e04,0x5e80,0x5e82,0x5fc9,
+ 0x6209,0x6250,0x6c15
+ },{
+ 0x6c36,0x6c43,0x6c3f,0x6c3b,0x72ae,0x72b0,0x738a,0x79b8,0x808a,0x961e,
+ 0x4f0e,0x4f18,0x4f2c,0x4ef5,0x4f14,0x4ef1,0x4f00,0x4ef7,0x4f08,0x4f1d,
+ 0x4f02,0x4f05,0x4f22,0x4f13,0x4f04,0x4ef4,0x4f12,0x51b1,0x5213,0x5209,
+ 0x5210,0x52a6,0x5322,0x531f,0x534d,0x538a,0x5407,0x56e1,0x56df,0x572e,
+ 0x572a,0x5734,0x593c,0x5980,0x597c,0x5985,0x597b,0x597e,0x5977,0x597f,
+ 0x5b56,0x5c15,0x5c25,0x5c7c,0x5c7a,0x5c7b,0x5c7e,0x5ddf,0x5e75,0x5e84,
+ 0x5f02,0x5f1a,0x5f74,0x5fd5,0x5fd4,0x5fcf,0x625c,0x625e,0x6264,0x6261,
+ 0x6266,0x6262,0x6259,0x6260,0x625a,0x6265,0x65ef,0x65ee,0x673e,0x6739,
+ 0x6738,0x673b,0x673a,0x673f,0x673c,0x6733,0x6c18,0x6c46,0x6c52,0x6c5c,
+ 0x6c4f,0x6c4a,0x6c54,0x6c4b
+ }
+ },
+ { /* ku 2a */
+ {
+ 0x6c4c,0x7071,0x725e,0x72b4,0x72b5,0x738e,0x752a,0x767f,0x7a75,0x7f51,
+ 0x8278,0x827c,0x8280,0x827d,0x827f,0x864d,0x897e,0x9099,0x9097,0x9098,
+ 0x909b,0x9094,0x9622,0x9624,0x9620,0x9623,0x4f56,0x4f3b,0x4f62,0x4f49,
+ 0x4f53,0x4f64,0x4f3e,0x4f67,0x4f52,0x4f5f,0x4f41,0x4f58,0x4f2d,0x4f33,
+ 0x4f3f,0x4f61,0x518f,0x51b9,0x521c,0x521e,0x5221,0x52ad,0x52ae,0x5309,
+ 0x5363,0x5372,0x538e,0x538f,0x5430,0x5437,0x542a,0x5454,0x5445,0x5419,
+ 0x541c,0x5425,0x5418
+ },{
+ 0x543d,0x544f,0x5441,0x5428,0x5424,0x5447,0x56ee,0x56e7,0x56e5,0x5741,
+ 0x5745,0x574c,0x5749,0x574b,0x5752,0x5906,0x5940,0x59a6,0x5998,0x59a0,
+ 0x5997,0x598e,0x59a2,0x5990,0x598f,0x59a7,0x59a1,0x5b8e,0x5b92,0x5c28,
+ 0x5c2a,0x5c8d,0x5c8f,0x5c88,0x5c8b,0x5c89,0x5c92,0x5c8a,0x5c86,0x5c93,
+ 0x5c95,0x5de0,0x5e0a,0x5e0e,0x5e8b,0x5e89,0x5e8c,0x5e88,0x5e8d,0x5f05,
+ 0x5f1d,0x5f78,0x5f76,0x5fd2,0x5fd1,0x5fd0,0x5fed,0x5fe8,0x5fee,0x5ff3,
+ 0x5fe1,0x5fe4,0x5fe3,0x5ffa,0x5fef,0x5ff7,0x5ffb,0x6000,0x5ff4,0x623a,
+ 0x6283,0x628c,0x628e,0x628f,0x6294,0x6287,0x6271,0x627b,0x627a,0x6270,
+ 0x6281,0x6288,0x6277,0x627d,0x6272,0x6274,0x6537,0x65f0,0x65f4,0x65f3,
+ 0x65f2,0x65f5,0x6745,0x6747
+ }
+ },
+ { /* ku 2b */
+ {
+ 0x6759,0x6755,0x674c,0x6748,0x675d,0x674d,0x675a,0x674b,0x6bd0,0x6c19,
+ 0x6c1a,0x6c78,0x6c67,0x6c6b,0x6c84,0x6c8b,0x6c8f,0x6c71,0x6c6f,0x6c69,
+ 0x6c9a,0x6c6d,0x6c87,0x6c95,0x6c9c,0x6c66,0x6c73,0x6c65,0x6c7b,0x6c8e,
+ 0x7074,0x707a,0x7263,0x72bf,0x72bd,0x72c3,0x72c6,0x72c1,0x72ba,0x72c5,
+ 0x7395,0x7397,0x7393,0x7394,0x7392,0x753a,0x7539,0x7594,0x7595,0x7681,
+ 0x793d,0x8034,0x8095,0x8099,0x8090,0x8092,0x809c,0x8290,0x828f,0x8285,
+ 0x828e,0x8291,0x8293
+ },{
+ 0x828a,0x8283,0x8284,0x8c78,0x8fc9,0x8fbf,0x909f,0x90a1,0x90a5,0x909e,
+ 0x90a7,0x90a0,0x9630,0x9628,0x962f,0x962d,0x4e33,0x4f98,0x4f7c,0x4f85,
+ 0x4f7d,0x4f80,0x4f87,0x4f76,0x4f74,0x4f89,0x4f84,0x4f77,0x4f4c,0x4f97,
+ 0x4f6a,0x4f9a,0x4f79,0x4f81,0x4f78,0x4f90,0x4f9c,0x4f94,0x4f9e,0x4f92,
+ 0x4f82,0x4f95,0x4f6b,0x4f6e,0x519e,0x51bc,0x51be,0x5235,0x5232,0x5233,
+ 0x5246,0x5231,0x52bc,0x530a,0x530b,0x533c,0x5392,0x5394,0x5487,0x547f,
+ 0x5481,0x5491,0x5482,0x5488,0x546b,0x547a,0x547e,0x5465,0x546c,0x5474,
+ 0x5466,0x548d,0x546f,0x5461,0x5460,0x5498,0x5463,0x5467,0x5464,0x56f7,
+ 0x56f9,0x576f,0x5772,0x576d,0x576b,0x5771,0x5770,0x5776,0x5780,0x5775,
+ 0x577b,0x5773,0x5774,0x5762
+ }
+ },
+ { /* ku 2c */
+ {
+ 0x5768,0x577d,0x590c,0x5945,0x59b5,0x59ba,0x59cf,0x59ce,0x59b2,0x59cc,
+ 0x59c1,0x59b6,0x59bc,0x59c3,0x59d6,0x59b1,0x59bd,0x59c0,0x59c8,0x59b4,
+ 0x59c7,0x5b62,0x5b65,0x5b93,0x5b95,0x5c44,0x5c47,0x5cae,0x5ca4,0x5ca0,
+ 0x5cb5,0x5caf,0x5ca8,0x5cac,0x5c9f,0x5ca3,0x5cad,0x5ca2,0x5caa,0x5ca7,
+ 0x5c9d,0x5ca5,0x5cb6,0x5cb0,0x5ca6,0x5e17,0x5e14,0x5e19,0x5f28,0x5f22,
+ 0x5f23,0x5f24,0x5f54,0x5f82,0x5f7e,0x5f7d,0x5fde,0x5fe5,0x602d,0x6026,
+ 0x6019,0x6032,0x600b
+ },{
+ 0x6034,0x600a,0x6017,0x6033,0x601a,0x601e,0x602c,0x6022,0x600d,0x6010,
+ 0x602e,0x6013,0x6011,0x600c,0x6009,0x601c,0x6214,0x623d,0x62ad,0x62b4,
+ 0x62d1,0x62be,0x62aa,0x62b6,0x62ca,0x62ae,0x62b3,0x62af,0x62bb,0x62a9,
+ 0x62b0,0x62b8,0x653d,0x65a8,0x65bb,0x6609,0x65fc,0x6604,0x6612,0x6608,
+ 0x65fb,0x6603,0x660b,0x660d,0x6605,0x65fd,0x6611,0x6610,0x66f6,0x670a,
+ 0x6785,0x676c,0x678e,0x6792,0x6776,0x677b,0x6798,0x6786,0x6784,0x6774,
+ 0x678d,0x678c,0x677a,0x679f,0x6791,0x6799,0x6783,0x677d,0x6781,0x6778,
+ 0x6779,0x6794,0x6b25,0x6b80,0x6b7e,0x6bde,0x6c1d,0x6c93,0x6cec,0x6ceb,
+ 0x6cee,0x6cd9,0x6cb6,0x6cd4,0x6cad,0x6ce7,0x6cb7,0x6cd0,0x6cc2,0x6cba,
+ 0x6cc3,0x6cc6,0x6ced,0x6cf2
+ }
+ },
+ { /* ku 2d */
+ {
+ 0x6cd2,0x6cdd,0x6cb4,0x6c8a,0x6c9d,0x6c80,0x6cde,0x6cc0,0x6d30,0x6ccd,
+ 0x6cc7,0x6cb0,0x6cf9,0x6ccf,0x6ce9,0x6cd1,0x7094,0x7098,0x7085,0x7093,
+ 0x7086,0x7084,0x7091,0x7096,0x7082,0x709a,0x7083,0x726a,0x72d6,0x72cb,
+ 0x72d8,0x72c9,0x72dc,0x72d2,0x72d4,0x72da,0x72cc,0x72d1,0x73a4,0x73a1,
+ 0x73ad,0x73a6,0x73a2,0x73a0,0x73ac,0x739d,0x74dd,0x74e8,0x753f,0x7540,
+ 0x753e,0x758c,0x7598,0x76af,0x76f3,0x76f1,0x76f0,0x76f5,0x77f8,0x77fc,
+ 0x77f9,0x77fb,0x77fa
+ },{
+ 0x77f7,0x7942,0x793f,0x79c5,0x7a78,0x7a7b,0x7afb,0x7c75,0x7cfd,0x8035,
+ 0x808f,0x80ae,0x80a3,0x80b8,0x80b5,0x80ad,0x8220,0x82a0,0x82c0,0x82ab,
+ 0x829a,0x8298,0x829b,0x82b5,0x82a7,0x82ae,0x82bc,0x829e,0x82ba,0x82b4,
+ 0x82a8,0x82a1,0x82a9,0x82c2,0x82a4,0x82c3,0x82b6,0x82a2,0x8670,0x866f,
+ 0x866d,0x866e,0x8c56,0x8fd2,0x8fcb,0x8fd3,0x8fcd,0x8fd6,0x8fd5,0x8fd7,
+ 0x90b2,0x90b4,0x90af,0x90b3,0x90b0,0x9639,0x963d,0x963c,0x963a,0x9643,
+ 0x4fcd,0x4fc5,0x4fd3,0x4fb2,0x4fc9,0x4fcb,0x4fc1,0x4fd4,0x4fdc,0x4fd9,
+ 0x4fbb,0x4fb3,0x4fdb,0x4fc7,0x4fd6,0x4fba,0x4fc0,0x4fb9,0x4fec,0x5244,
+ 0x5249,0x52c0,0x52c2,0x533d,0x537c,0x5397,0x5396,0x5399,0x5398,0x54ba,
+ 0x54a1,0x54ad,0x54a5,0x54cf
+ }
+ },
+ { /* ku 2e */
+ {
+ 0x54c3,0x830d,0x54b7,0x54ae,0x54d6,0x54b6,0x54c5,0x54c6,0x54a0,0x5470,
+ 0x54bc,0x54a2,0x54be,0x5472,0x54de,0x54b0,0x57b5,0x579e,0x579f,0x57a4,
+ 0x578c,0x5797,0x579d,0x579b,0x5794,0x5798,0x578f,0x5799,0x57a5,0x579a,
+ 0x5795,0x58f4,0x590d,0x5953,0x59e1,0x59de,0x59ee,0x5a00,0x59f1,0x59dd,
+ 0x59fa,0x59fd,0x59fc,0x59f6,0x59e4,0x59f2,0x59f7,0x59db,0x59e9,0x59f3,
+ 0x59f5,0x59e0,0x59fe,0x59f4,0x59ed,0x5ba8,0x5c4c,0x5cd0,0x5cd8,0x5ccc,
+ 0x5cd7,0x5ccb,0x5cdb
+ },{
+ 0x5cde,0x5cda,0x5cc9,0x5cc7,0x5cca,0x5cd6,0x5cd3,0x5cd4,0x5ccf,0x5cc8,
+ 0x5cc6,0x5cce,0x5cdf,0x5cf8,0x5df9,0x5e21,0x5e22,0x5e23,0x5e20,0x5e24,
+ 0x5eb0,0x5ea4,0x5ea2,0x5e9b,0x5ea3,0x5ea5,0x5f07,0x5f2e,0x5f56,0x5f86,
+ 0x6037,0x6039,0x6054,0x6072,0x605e,0x6045,0x6053,0x6047,0x6049,0x605b,
+ 0x604c,0x6040,0x6042,0x605f,0x6024,0x6044,0x6058,0x6066,0x606e,0x6242,
+ 0x6243,0x62cf,0x630d,0x630b,0x62f5,0x630e,0x6303,0x62eb,0x62f9,0x630f,
+ 0x630c,0x62f8,0x62f6,0x6300,0x6313,0x6314,0x62fa,0x6315,0x62fb,0x62f0,
+ 0x6541,0x6543,0x65aa,0x65bf,0x6636,0x6621,0x6632,0x6635,0x661c,0x6626,
+ 0x6622,0x6633,0x662b,0x663a,0x661d,0x6634,0x6639,0x662e,0x670f,0x6710,
+ 0x67c1,0x67f2,0x67c8,0x67ba
+ }
+ },
+ { /* ku 2f */
+ {
+ 0x67dc,0x67bb,0x67f8,0x67d8,0x67c0,0x67b7,0x67c5,0x67eb,0x67e4,0x67df,
+ 0x67b5,0x67cd,0x67b3,0x67f7,0x67f6,0x67ee,0x67e3,0x67c2,0x67b9,0x67ce,
+ 0x67e7,0x67f0,0x67b2,0x67fc,0x67c6,0x67ed,0x67cc,0x67ae,0x67e6,0x67db,
+ 0x67fa,0x67c9,0x67ca,0x67c3,0x67ea,0x67cb,0x6b28,0x6b82,0x6b84,0x6bb6,
+ 0x6bd6,0x6bd8,0x6be0,0x6c20,0x6c21,0x6d28,0x6d34,0x6d2d,0x6d1f,0x6d3c,
+ 0x6d3f,0x6d12,0x6d0a,0x6cda,0x6d33,0x6d04,0x6d19,0x6d3a,0x6d1a,0x6d11,
+ 0x6d00,0x6d1d,0x6d42
+ },{
+ 0x6d01,0x6d18,0x6d37,0x6d03,0x6d0f,0x6d40,0x6d07,0x6d20,0x6d2c,0x6d08,
+ 0x6d22,0x6d09,0x6d10,0x70b7,0x709f,0x70be,0x70b1,0x70b0,0x70a1,0x70b4,
+ 0x70b5,0x70a9,0x7241,0x7249,0x724a,0x726c,0x7270,0x7273,0x726e,0x72ca,
+ 0x72e4,0x72e8,0x72eb,0x72df,0x72ea,0x72e6,0x72e3,0x7385,0x73cc,0x73c2,
+ 0x73c8,0x73c5,0x73b9,0x73b6,0x73b5,0x73b4,0x73eb,0x73bf,0x73c7,0x73be,
+ 0x73c3,0x73c6,0x73b8,0x73cb,0x74ec,0x74ee,0x752e,0x7547,0x7548,0x75a7,
+ 0x75aa,0x7679,0x76c4,0x7708,0x7703,0x7704,0x7705,0x770a,0x76f7,0x76fb,
+ 0x76fa,0x77e7,0x77e8,0x7806,0x7811,0x7812,0x7805,0x7810,0x780f,0x780e,
+ 0x7809,0x7803,0x7813,0x794a,0x794c,0x794b,0x7945,0x7944,0x79d5,0x79cd,
+ 0x79cf,0x79d6,0x79ce,0x7a80
+ }
+ },
+ { /* ku 30 */
+ {
+ 0x7a7e,0x7ad1,0x7b00,0x7b01,0x7c7a,0x7c78,0x7c79,0x7c7f,0x7c80,0x7c81,
+ 0x7d03,0x7d08,0x7d01,0x7f58,0x7f91,0x7f8d,0x7fbe,0x8007,0x800e,0x800f,
+ 0x8014,0x8037,0x80d8,0x80c7,0x80e0,0x80d1,0x80c8,0x80c2,0x80d0,0x80c5,
+ 0x80e3,0x80d9,0x80dc,0x80ca,0x80d5,0x80c9,0x80cf,0x80d7,0x80e6,0x80cd,
+ 0x81ff,0x8221,0x8294,0x82d9,0x82fe,0x82f9,0x8307,0x82e8,0x8300,0x82d5,
+ 0x833a,0x82eb,0x82d6,0x82f4,0x82ec,0x82e1,0x82f2,0x82f5,0x830c,0x82fb,
+ 0x82f6,0x82f0,0x82ea
+ },{
+ 0x82e4,0x82e0,0x82fa,0x82f3,0x82ed,0x8677,0x8674,0x867c,0x8673,0x8841,
+ 0x884e,0x8867,0x886a,0x8869,0x89d3,0x8a04,0x8a07,0x8d72,0x8fe3,0x8fe1,
+ 0x8fee,0x8fe0,0x90f1,0x90bd,0x90bf,0x90d5,0x90c5,0x90be,0x90c7,0x90cb,
+ 0x90c8,0x91d4,0x91d3,0x9654,0x964f,0x9651,0x9653,0x964a,0x964e,0x501e,
+ 0x5005,0x5007,0x5013,0x5022,0x5030,0x501b,0x4ff5,0x4ff4,0x5033,0x5037,
+ 0x502c,0x4ff6,0x4ff7,0x5017,0x501c,0x5020,0x5027,0x5035,0x502f,0x5031,
+ 0x500e,0x515a,0x5194,0x5193,0x51ca,0x51c4,0x51c5,0x51c8,0x51ce,0x5261,
+ 0x525a,0x5252,0x525e,0x525f,0x5255,0x5262,0x52cd,0x530e,0x539e,0x5526,
+ 0x54e2,0x5517,0x5512,0x54e7,0x54f3,0x54e4,0x551a,0x54ff,0x5504,0x5508,
+ 0x54eb,0x5511,0x5505,0x54f1
+ }
+ },
+ { /* ku 31 */
+ {
+ 0x550a,0x54fb,0x54f7,0x54f8,0x54e0,0x550e,0x5503,0x550b,0x5701,0x5702,
+ 0x57cc,0x5832,0x57d5,0x57d2,0x57ba,0x57c6,0x57bd,0x57bc,0x57b8,0x57b6,
+ 0x57bf,0x57c7,0x57d0,0x57b9,0x57c1,0x590e,0x594a,0x5a19,0x5a16,0x5a2d,
+ 0x5a2e,0x5a15,0x5a0f,0x5a17,0x5a0a,0x5a1e,0x5a33,0x5b6c,0x5ba7,0x5bad,
+ 0x5bac,0x5c03,0x5c56,0x5c54,0x5cec,0x5cff,0x5cee,0x5cf1,0x5cf7,0x5d00,
+ 0x5cf9,0x5e29,0x5e28,0x5ea8,0x5eae,0x5eaa,0x5eac,0x5f33,0x5f30,0x5f67,
+ 0x605d,0x605a,0x6067
+ },{
+ 0x6041,0x60a2,0x6088,0x6080,0x6092,0x6081,0x609d,0x6083,0x6095,0x609b,
+ 0x6097,0x6087,0x609c,0x608e,0x6219,0x6246,0x62f2,0x6310,0x6356,0x632c,
+ 0x6344,0x6345,0x6336,0x6343,0x63e4,0x6339,0x634b,0x634a,0x633c,0x6329,
+ 0x6341,0x6334,0x6358,0x6354,0x6359,0x632d,0x6347,0x6333,0x635a,0x6351,
+ 0x6338,0x6357,0x6340,0x6348,0x654a,0x6546,0x65c6,0x65c3,0x65c4,0x65c2,
+ 0x664a,0x665f,0x6647,0x6651,0x6712,0x6713,0x681f,0x681a,0x6849,0x6832,
+ 0x6833,0x683b,0x684b,0x684f,0x6816,0x6831,0x681c,0x6835,0x682b,0x682d,
+ 0x682f,0x684e,0x6844,0x6834,0x681d,0x6812,0x6814,0x6826,0x6828,0x682e,
+ 0x684d,0x683a,0x6825,0x6820,0x6b2c,0x6b2f,0x6b2d,0x6b31,0x6b34,0x6b6d,
+ 0x8082,0x6b88,0x6be6,0x6be4
+ }
+ },
+ { /* ku 32 */
+ {
+ 0x6be8,0x6be3,0x6be2,0x6be7,0x6c25,0x6d7a,0x6d63,0x6d64,0x6d76,0x6d0d,
+ 0x6d61,0x6d92,0x6d58,0x6d62,0x6d6d,0x6d6f,0x6d91,0x6d8d,0x6def,0x6d7f,
+ 0x6d86,0x6d5e,0x6d67,0x6d60,0x6d97,0x6d70,0x6d7c,0x6d5f,0x6d82,0x6d98,
+ 0x6d2f,0x6d68,0x6d8b,0x6d7e,0x6d80,0x6d84,0x6d16,0x6d83,0x6d7b,0x6d7d,
+ 0x6d75,0x6d90,0x70dc,0x70d3,0x70d1,0x70dd,0x70cb,0x7f39,0x70e2,0x70d7,
+ 0x70d2,0x70de,0x70e0,0x70d4,0x70cd,0x70c5,0x70c6,0x70c7,0x70da,0x70ce,
+ 0x70e1,0x7242,0x7278
+ },{
+ 0x7277,0x7276,0x7300,0x72fa,0x72f4,0x72fe,0x72f6,0x72f3,0x72fb,0x7301,
+ 0x73d3,0x73d9,0x73e5,0x73d6,0x73bc,0x73e7,0x73e3,0x73e9,0x73dc,0x73d2,
+ 0x73db,0x73d4,0x73dd,0x73da,0x73d7,0x73d8,0x73e8,0x74de,0x74df,0x74f4,
+ 0x74f5,0x7521,0x755b,0x755f,0x75b0,0x75c1,0x75bb,0x75c4,0x75c0,0x75bf,
+ 0x75b6,0x75ba,0x768a,0x76c9,0x771d,0x771b,0x7710,0x7713,0x7712,0x7723,
+ 0x7711,0x7715,0x7719,0x771a,0x7722,0x7727,0x7823,0x782c,0x7822,0x7835,
+ 0x782f,0x7828,0x782e,0x782b,0x7821,0x7829,0x7833,0x782a,0x7831,0x7954,
+ 0x795b,0x794f,0x795c,0x7953,0x7952,0x7951,0x79eb,0x79ec,0x79e0,0x79ee,
+ 0x79ed,0x79ea,0x79dc,0x79de,0x79dd,0x7a86,0x7a89,0x7a85,0x7a8b,0x7a8c,
+ 0x7a8a,0x7a87,0x7ad8,0x7b10
+ }
+ },
+ { /* ku 33 */
+ {
+ 0x7b04,0x7b13,0x7b05,0x7b0f,0x7b08,0x7b0a,0x7b0e,0x7b09,0x7b12,0x7c84,
+ 0x7c91,0x7c8a,0x7c8c,0x7c88,0x7c8d,0x7c85,0x7d1e,0x7d1d,0x7d11,0x7d0e,
+ 0x7d18,0x7d16,0x7d13,0x7d1f,0x7d12,0x7d0f,0x7d0c,0x7f5c,0x7f61,0x7f5e,
+ 0x7f60,0x7f5d,0x7f5b,0x7f96,0x7f92,0x7fc3,0x7fc2,0x7fc0,0x8016,0x803e,
+ 0x8039,0x80fa,0x80f2,0x80f9,0x80f5,0x8101,0x80fb,0x8100,0x8201,0x822f,
+ 0x8225,0x8333,0x832d,0x8344,0x8319,0x8351,0x8325,0x8356,0x833f,0x8341,
+ 0x8326,0x831c,0x8322
+ },{
+ 0x8342,0x834e,0x831b,0x832a,0x8308,0x833c,0x834d,0x8316,0x8324,0x8320,
+ 0x8337,0x832f,0x8329,0x8347,0x8345,0x834c,0x8353,0x831e,0x832c,0x834b,
+ 0x8327,0x8348,0x8653,0x8652,0x86a2,0x86a8,0x8696,0x868d,0x8691,0x869e,
+ 0x8687,0x8697,0x8686,0x868b,0x869a,0x8685,0x86a5,0x8699,0x86a1,0x86a7,
+ 0x8695,0x8698,0x868e,0x869d,0x8690,0x8694,0x8843,0x8844,0x886d,0x8875,
+ 0x8876,0x8872,0x8880,0x8871,0x887f,0x886f,0x8883,0x887e,0x8874,0x887c,
+ 0x8a12,0x8c47,0x8c57,0x8c7b,0x8ca4,0x8ca3,0x8d76,0x8d78,0x8db5,0x8db7,
+ 0x8db6,0x8ed1,0x8ed3,0x8ffe,0x8ff5,0x9002,0x8fff,0x8ffb,0x9004,0x8ffc,
+ 0x8ff6,0x90d6,0x90e0,0x90d9,0x90da,0x90e3,0x90df,0x90e5,0x90d8,0x90db,
+ 0x90d7,0x90dc,0x90e4,0x9150
+ }
+ },
+ { /* ku 34 */
+ {
+ 0x914e,0x914f,0x91d5,0x91e2,0x91da,0x965c,0x965f,0x96bc,0x98e3,0x9adf,
+ 0x9b2f,0x4e7f,0x5070,0x506a,0x5061,0x505e,0x5060,0x5053,0x504b,0x505d,
+ 0x5072,0x5048,0x504d,0x5041,0x505b,0x504a,0x5062,0x5015,0x5045,0x505f,
+ 0x5069,0x506b,0x5063,0x5064,0x5046,0x5040,0x506e,0x5073,0x5057,0x5051,
+ 0x51d0,0x526b,0x526d,0x526c,0x526e,0x52d6,0x52d3,0x532d,0x539c,0x5575,
+ 0x5576,0x553c,0x554d,0x5550,0x5534,0x552a,0x5551,0x5562,0x5536,0x5535,
+ 0x5530,0x5552,0x5545
+ },{
+ 0x550c,0x5532,0x5565,0x554e,0x5539,0x5548,0x552d,0x553b,0x5540,0x554b,
+ 0x570a,0x5707,0x57fb,0x5814,0x57e2,0x57f6,0x57dc,0x57f4,0x5800,0x57ed,
+ 0x57fd,0x5808,0x57f8,0x580b,0x57f3,0x57cf,0x5807,0x57ee,0x57e3,0x57f2,
+ 0x57e5,0x57ec,0x57e1,0x580e,0x57fc,0x5810,0x57e7,0x5801,0x580c,0x57f1,
+ 0x57e9,0x57f0,0x580d,0x5804,0x595c,0x5a60,0x5a58,0x5a55,0x5a67,0x5a5e,
+ 0x5a38,0x5a35,0x5a6d,0x5a50,0x5a5f,0x5a65,0x5a6c,0x5a53,0x5a64,0x5a57,
+ 0x5a43,0x5a5d,0x5a52,0x5a44,0x5a5b,0x5a48,0x5a8e,0x5a3e,0x5a4d,0x5a39,
+ 0x5a4c,0x5a70,0x5a69,0x5a47,0x5a51,0x5a56,0x5a42,0x5a5c,0x5b72,0x5b6e,
+ 0x5bc1,0x5bc0,0x5c59,0x5d1e,0x5d0b,0x5d1d,0x5d1a,0x5d20,0x5d0c,0x5d28,
+ 0x5d0d,0x5d26,0x5d25,0x5d0f
+ }
+ },
+ { /* ku 35 */
+ {
+ 0x5d30,0x5d12,0x5d23,0x5d1f,0x5d2e,0x5e3e,0x5e34,0x5eb1,0x5eb4,0x5eb9,
+ 0x5eb2,0x5eb3,0x5f36,0x5f38,0x5f9b,0x5f96,0x5f9f,0x608a,0x6090,0x6086,
+ 0x60be,0x60b0,0x60ba,0x60d3,0x60d4,0x60cf,0x60e4,0x60d9,0x60dd,0x60c8,
+ 0x60b1,0x60db,0x60b7,0x60ca,0x60bf,0x60c3,0x60cd,0x60c0,0x6332,0x6365,
+ 0x638a,0x6382,0x637d,0x63bd,0x639e,0x63ad,0x639d,0x6397,0x63ab,0x638e,
+ 0x636f,0x6387,0x6390,0x636e,0x63af,0x6375,0x639c,0x636d,0x63ae,0x637c,
+ 0x63a4,0x633b,0x639f
+ },{
+ 0x6378,0x6385,0x6381,0x6391,0x638d,0x6370,0x6553,0x65cd,0x6665,0x6661,
+ 0x665b,0x6659,0x665c,0x6662,0x6718,0x6879,0x6887,0x6890,0x689c,0x686d,
+ 0x686e,0x68ae,0x68ab,0x6956,0x686f,0x68a3,0x68ac,0x68a9,0x6875,0x6874,
+ 0x68b2,0x688f,0x6877,0x6892,0x687c,0x686b,0x6872,0x68aa,0x6880,0x6871,
+ 0x687e,0x689b,0x6896,0x688b,0x68a0,0x6889,0x68a4,0x6878,0x687b,0x6891,
+ 0x688c,0x688a,0x687d,0x6b36,0x6b33,0x6b37,0x6b38,0x6b91,0x6b8f,0x6b8d,
+ 0x6b8e,0x6b8c,0x6c2a,0x6dc0,0x6dab,0x6db4,0x6db3,0x6e74,0x6dac,0x6de9,
+ 0x6de2,0x6db7,0x6df6,0x6dd4,0x6e00,0x6dc8,0x6de0,0x6ddf,0x6dd6,0x6dbe,
+ 0x6de5,0x6ddc,0x6ddd,0x6ddb,0x6df4,0x6dca,0x6dbd,0x6ded,0x6df0,0x6dba,
+ 0x6dd5,0x6dc2,0x6dcf,0x6dc9
+ }
+ },
+ { /* ku 36 */
+ {
+ 0x6dd0,0x6df2,0x6dd3,0x6dfd,0x6dd7,0x6dcd,0x6de3,0x6dbb,0x70fa,0x710d,
+ 0x70f7,0x7117,0x70f4,0x710c,0x70f0,0x7104,0x70f3,0x7110,0x70fc,0x70ff,
+ 0x7106,0x7113,0x7100,0x70f8,0x70f6,0x710b,0x7102,0x710e,0x727e,0x727b,
+ 0x727c,0x727f,0x731d,0x7317,0x7307,0x7311,0x7318,0x730a,0x7308,0x72ff,
+ 0x730f,0x731e,0x7388,0x73f6,0x73f8,0x73f5,0x7404,0x7401,0x73fd,0x7407,
+ 0x7400,0x73fa,0x73fc,0x73ff,0x740c,0x740b,0x73f4,0x7408,0x7564,0x7563,
+ 0x75ce,0x75d2,0x75cf
+ },{
+ 0x75cb,0x75cc,0x75d1,0x75d0,0x768f,0x7689,0x76d3,0x7739,0x772f,0x772d,
+ 0x7731,0x7732,0x7734,0x7733,0x773d,0x7725,0x773b,0x7735,0x7848,0x7852,
+ 0x7849,0x784d,0x784a,0x784c,0x7826,0x7845,0x7850,0x7964,0x7967,0x7969,
+ 0x796a,0x7963,0x796b,0x7961,0x79bb,0x79fa,0x79f8,0x79f6,0x79f7,0x7a8f,
+ 0x7a94,0x7a90,0x7b35,0x7b47,0x7b34,0x7b25,0x7b30,0x7b22,0x7b24,0x7b33,
+ 0x7b18,0x7b2a,0x7b1d,0x7b31,0x7b2b,0x7b2d,0x7b2f,0x7b32,0x7b38,0x7b1a,
+ 0x7b23,0x7c94,0x7c98,0x7c96,0x7ca3,0x7d35,0x7d3d,0x7d38,0x7d36,0x7d3a,
+ 0x7d45,0x7d2c,0x7d29,0x7d41,0x7d47,0x7d3e,0x7d3f,0x7d4a,0x7d3b,0x7d28,
+ 0x7f63,0x7f95,0x7f9c,0x7f9d,0x7f9b,0x7fca,0x7fcb,0x7fcd,0x7fd0,0x7fd1,
+ 0x7fc7,0x7fcf,0x7fc9,0x801f
+ }
+ },
+ { /* ku 37 */
+ {
+ 0x801e,0x801b,0x8047,0x8043,0x8048,0x8118,0x8125,0x8119,0x811b,0x812d,
+ 0x811f,0x812c,0x811e,0x8121,0x8115,0x8127,0x811d,0x8122,0x8211,0x8238,
+ 0x8233,0x823a,0x8234,0x8232,0x8274,0x8390,0x83a3,0x83a8,0x838d,0x837a,
+ 0x8373,0x83a4,0x8374,0x838f,0x8381,0x8395,0x8399,0x8375,0x8394,0x83a9,
+ 0x837d,0x8383,0x838c,0x839d,0x839b,0x83aa,0x838b,0x837e,0x83a5,0x83af,
+ 0x8388,0x8397,0x83b0,0x837f,0x83a6,0x8387,0x83ae,0x8376,0x839a,0x8659,
+ 0x8656,0x86bf,0x86b7
+ },{
+ 0x86c2,0x86c1,0x86c5,0x86ba,0x86b0,0x86c8,0x86b9,0x86b3,0x86b8,0x86cc,
+ 0x86b4,0x86bb,0x86bc,0x86c3,0x86bd,0x86be,0x8852,0x8889,0x8895,0x88a8,
+ 0x88a2,0x88aa,0x889a,0x8891,0x88a1,0x889f,0x8898,0x88a7,0x8899,0x889b,
+ 0x8897,0x88a4,0x88ac,0x888c,0x8893,0x888e,0x8982,0x89d6,0x89d9,0x89d5,
+ 0x8a30,0x8a27,0x8a2c,0x8a1e,0x8c39,0x8c3b,0x8c5c,0x8c5d,0x8c7d,0x8ca5,
+ 0x8d7d,0x8d7b,0x8d79,0x8dbc,0x8dc2,0x8db9,0x8dbf,0x8dc1,0x8ed8,0x8ede,
+ 0x8edd,0x8edc,0x8ed7,0x8ee0,0x8ee1,0x9024,0x900b,0x9011,0x901c,0x900c,
+ 0x9021,0x90ef,0x90ea,0x90f0,0x90f4,0x90f2,0x90f3,0x90d4,0x90eb,0x90ec,
+ 0x90e9,0x9156,0x9158,0x915a,0x9153,0x9155,0x91ec,0x91f4,0x91f1,0x91f3,
+ 0x91f8,0x91e4,0x91f9,0x91ea
+ }
+ },
+ { /* ku 38 */
+ {
+ 0x91eb,0x91f7,0x91e8,0x91ee,0x957a,0x9586,0x9588,0x967c,0x966d,0x966b,
+ 0x9671,0x966f,0x96bf,0x976a,0x9804,0x98e5,0x9997,0x509b,0x5095,0x5094,
+ 0x509e,0x508b,0x50a3,0x5083,0x508c,0x508e,0x509d,0x5068,0x509c,0x5092,
+ 0x5082,0x5087,0x515f,0x51d4,0x5312,0x5311,0x53a4,0x53a7,0x5591,0x55a8,
+ 0x55a5,0x55ad,0x5577,0x5645,0x55a2,0x5593,0x5588,0x558f,0x55b5,0x5581,
+ 0x55a3,0x5592,0x55a4,0x557d,0x558c,0x55a6,0x557f,0x5595,0x55a1,0x558e,
+ 0x570c,0x5829,0x5837
+ },{
+ 0x5819,0x581e,0x5827,0x5823,0x5828,0x57f5,0x5848,0x5825,0x581c,0x581b,
+ 0x5833,0x583f,0x5836,0x582e,0x5839,0x5838,0x582d,0x582c,0x583b,0x5961,
+ 0x5aaf,0x5a94,0x5a9f,0x5a7a,0x5aa2,0x5a9e,0x5a78,0x5aa6,0x5a7c,0x5aa5,
+ 0x5aac,0x5a95,0x5aae,0x5a37,0x5a84,0x5a8a,0x5a97,0x5a83,0x5a8b,0x5aa9,
+ 0x5a7b,0x5a7d,0x5a8c,0x5a9c,0x5a8f,0x5a93,0x5a9d,0x5bea,0x5bcd,0x5bcb,
+ 0x5bd4,0x5bd1,0x5bca,0x5bce,0x5c0c,0x5c30,0x5d37,0x5d43,0x5d6b,0x5d41,
+ 0x5d4b,0x5d3f,0x5d35,0x5d51,0x5d4e,0x5d55,0x5d33,0x5d3a,0x5d52,0x5d3d,
+ 0x5d31,0x5d59,0x5d42,0x5d39,0x5d49,0x5d38,0x5d3c,0x5d32,0x5d36,0x5d40,
+ 0x5d45,0x5e44,0x5e41,0x5f58,0x5fa6,0x5fa5,0x5fab,0x60c9,0x60b9,0x60cc,
+ 0x60e2,0x60ce,0x60c4,0x6114
+ }
+ },
+ { /* ku 39 */
+ {
+ 0x60f2,0x610a,0x6116,0x6105,0x60f5,0x6113,0x60f8,0x60fc,0x60fe,0x60c1,
+ 0x6103,0x6118,0x611d,0x6110,0x60ff,0x6104,0x610b,0x624a,0x6394,0x63b1,
+ 0x63b0,0x63ce,0x63e5,0x63e8,0x63ef,0x63c3,0x649d,0x63f3,0x63ca,0x63e0,
+ 0x63f6,0x63d5,0x63f2,0x63f5,0x6461,0x63df,0x63be,0x63dd,0x63dc,0x63c4,
+ 0x63d8,0x63d3,0x63c2,0x63c7,0x63cc,0x63cb,0x63c8,0x63f0,0x63d7,0x63d9,
+ 0x6532,0x6567,0x656a,0x6564,0x655c,0x6568,0x6565,0x658c,0x659d,0x659e,
+ 0x65ae,0x65d0,0x65d2
+ },{
+ 0x667c,0x666c,0x667b,0x6680,0x6671,0x6679,0x666a,0x6672,0x6701,0x690c,
+ 0x68d3,0x6904,0x68dc,0x692a,0x68ec,0x68ea,0x68f1,0x690f,0x68d6,0x68f7,
+ 0x68eb,0x68e4,0x68f6,0x6913,0x6910,0x68f3,0x68e1,0x6907,0x68cc,0x6908,
+ 0x6970,0x68b4,0x6911,0x68ef,0x68c6,0x6914,0x68f8,0x68d0,0x68fd,0x68fc,
+ 0x68e8,0x690b,0x690a,0x6917,0x68ce,0x68c8,0x68dd,0x68de,0x68e6,0x68f4,
+ 0x68d1,0x6906,0x68d4,0x68e9,0x6915,0x6925,0x68c7,0x6b39,0x6b3b,0x6b3f,
+ 0x6b3c,0x6b94,0x6b97,0x6b99,0x6b95,0x6bbd,0x6bf0,0x6bf2,0x6bf3,0x6c30,
+ 0x6dfc,0x6e46,0x6e47,0x6e1f,0x6e49,0x6e88,0x6e3c,0x6e3d,0x6e45,0x6e62,
+ 0x6e2b,0x6e3f,0x6e41,0x6e5d,0x6e73,0x6e1c,0x6e33,0x6e4b,0x6e40,0x6e51,
+ 0x6e3b,0x6e03,0x6e2e,0x6e5e
+ }
+ },
+ { /* ku 3a */
+ {
+ 0x6e68,0x6e5c,0x6e61,0x6e31,0x6e28,0x6e60,0x6e71,0x6e6b,0x6e39,0x6e22,
+ 0x6e30,0x6e53,0x6e65,0x6e27,0x6e78,0x6e64,0x6e77,0x6e55,0x6e79,0x6e52,
+ 0x6e66,0x6e35,0x6e36,0x6e5a,0x7120,0x711e,0x712f,0x70fb,0x712e,0x7131,
+ 0x7123,0x7125,0x7122,0x7132,0x711f,0x7128,0x713a,0x711b,0x724b,0x725a,
+ 0x7288,0x7289,0x7286,0x7285,0x728b,0x7312,0x730b,0x7330,0x7322,0x7331,
+ 0x7333,0x7327,0x7332,0x732d,0x7326,0x7323,0x7335,0x730c,0x742e,0x742c,
+ 0x7430,0x742b,0x7416
+ },{
+ 0x741a,0x7421,0x742d,0x7431,0x7424,0x7423,0x741d,0x7429,0x7420,0x7432,
+ 0x74fb,0x752f,0x756f,0x756c,0x75e7,0x75da,0x75e1,0x75e6,0x75dd,0x75df,
+ 0x75e4,0x75d7,0x7695,0x7692,0x76da,0x7746,0x7747,0x7744,0x774d,0x7745,
+ 0x774a,0x774e,0x774b,0x774c,0x77de,0x77ec,0x7860,0x7864,0x7865,0x785c,
+ 0x786d,0x7871,0x786a,0x786e,0x7870,0x7869,0x7868,0x785e,0x7862,0x7974,
+ 0x7973,0x7972,0x7970,0x7a02,0x7a0a,0x7a03,0x7a0c,0x7a04,0x7a99,0x7ae6,
+ 0x7ae4,0x7b4a,0x7b3b,0x7b44,0x7b48,0x7b4c,0x7b4e,0x7b40,0x7b58,0x7b45,
+ 0x7ca2,0x7c9e,0x7ca8,0x7ca1,0x7d58,0x7d6f,0x7d63,0x7d53,0x7d56,0x7d67,
+ 0x7d6a,0x7d4f,0x7d6d,0x7d5c,0x7d6b,0x7d52,0x7d54,0x7d69,0x7d51,0x7d5f,
+ 0x7d4e,0x7f3e,0x7f3f,0x7f65
+ }
+ },
+ { /* ku 3b */
+ {
+ 0x7f66,0x7fa2,0x7fa0,0x7fa1,0x7fd7,0x8051,0x804f,0x8050,0x80fe,0x80d4,
+ 0x8143,0x814a,0x8152,0x814f,0x8147,0x813d,0x814d,0x813a,0x81e6,0x81ee,
+ 0x81f7,0x81f8,0x81f9,0x8204,0x823c,0x823d,0x823f,0x8275,0x833b,0x83cf,
+ 0x83f9,0x8423,0x83c0,0x83e8,0x8412,0x83e7,0x83e4,0x83fc,0x83f6,0x8410,
+ 0x83c6,0x83c8,0x83eb,0x83e3,0x83bf,0x8401,0x83dd,0x83e5,0x83d8,0x83ff,
+ 0x83e1,0x83cb,0x83ce,0x83d6,0x83f5,0x83c9,0x8409,0x840f,0x83de,0x8411,
+ 0x8406,0x83c2,0x83f3
+ },{
+ 0x83d5,0x83fa,0x83c7,0x83d1,0x83ea,0x8413,0x83c3,0x83ec,0x83ee,0x83c4,
+ 0x83fb,0x83d7,0x83e2,0x841b,0x83db,0x83fe,0x86d8,0x86e2,0x86e6,0x86d3,
+ 0x86e3,0x86da,0x86ea,0x86dd,0x86eb,0x86dc,0x86ec,0x86e9,0x86d7,0x86e8,
+ 0x86d1,0x8848,0x8856,0x8855,0x88ba,0x88d7,0x88b9,0x88b8,0x88c0,0x88be,
+ 0x88b6,0x88bc,0x88b7,0x88bd,0x88b2,0x8901,0x88c9,0x8995,0x8998,0x8997,
+ 0x89dd,0x89da,0x89db,0x8a4e,0x8a4d,0x8a39,0x8a59,0x8a40,0x8a57,0x8a58,
+ 0x8a44,0x8a45,0x8a52,0x8a48,0x8a51,0x8a4a,0x8a4c,0x8a4f,0x8c5f,0x8c81,
+ 0x8c80,0x8cba,0x8cbe,0x8cb0,0x8cb9,0x8cb5,0x8d84,0x8d80,0x8d89,0x8dd8,
+ 0x8dd3,0x8dcd,0x8dc7,0x8dd6,0x8ddc,0x8dcf,0x8dd5,0x8dd9,0x8dc8,0x8dd7,
+ 0x8dc5,0x8eef,0x8ef7,0x8efa
+ }
+ },
+ { /* ku 3c */
+ {
+ 0x8ef9,0x8ee6,0x8eee,0x8ee5,0x8ef5,0x8ee7,0x8ee8,0x8ef6,0x8eeb,0x8ef1,
+ 0x8eec,0x8ef4,0x8ee9,0x902d,0x9034,0x902f,0x9106,0x912c,0x9104,0x90ff,
+ 0x90fc,0x9108,0x90f9,0x90fb,0x9101,0x9100,0x9107,0x9105,0x9103,0x9161,
+ 0x9164,0x915f,0x9162,0x9160,0x9201,0x920a,0x9225,0x9203,0x921a,0x9226,
+ 0x920f,0x920c,0x9200,0x9212,0x91ff,0x91fd,0x9206,0x9204,0x9227,0x9202,
+ 0x921c,0x9224,0x9219,0x9217,0x9205,0x9216,0x957b,0x958d,0x958c,0x9590,
+ 0x9687,0x967e,0x9688
+ },{
+ 0x9689,0x9683,0x9680,0x96c2,0x96c8,0x96c3,0x96f1,0x96f0,0x976c,0x9770,
+ 0x976e,0x9807,0x98a9,0x98eb,0x9ce6,0x9ef9,0x4e83,0x4e84,0x4eb6,0x50bd,
+ 0x50bf,0x50c6,0x50ae,0x50c4,0x50ca,0x50b4,0x50c8,0x50c2,0x50b0,0x50c1,
+ 0x50ba,0x50b1,0x50cb,0x50c9,0x50b6,0x50b8,0x51d7,0x527a,0x5278,0x527b,
+ 0x527c,0x55c3,0x55db,0x55cc,0x55d0,0x55cb,0x55ca,0x55dd,0x55c0,0x55d4,
+ 0x55c4,0x55e9,0x55bf,0x55d2,0x558d,0x55cf,0x55d5,0x55e2,0x55d6,0x55c8,
+ 0x55f2,0x55cd,0x55d9,0x55c2,0x5714,0x5853,0x5868,0x5864,0x584f,0x584d,
+ 0x5849,0x586f,0x5855,0x584e,0x585d,0x5859,0x5865,0x585b,0x583d,0x5863,
+ 0x5871,0x58fc,0x5ac7,0x5ac4,0x5acb,0x5aba,0x5ab8,0x5ab1,0x5ab5,0x5ab0,
+ 0x5abf,0x5ac8,0x5abb,0x5ac6
+ }
+ },
+ { /* ku 3d */
+ {
+ 0x5ab7,0x5ac0,0x5aca,0x5ab4,0x5ab6,0x5acd,0x5ab9,0x5a90,0x5bd6,0x5bd8,
+ 0x5bd9,0x5c1f,0x5c33,0x5d71,0x5d63,0x5d4a,0x5d65,0x5d72,0x5d6c,0x5d5e,
+ 0x5d68,0x5d67,0x5d62,0x5df0,0x5e4f,0x5e4e,0x5e4a,0x5e4d,0x5e4b,0x5ec5,
+ 0x5ecc,0x5ec6,0x5ecb,0x5ec7,0x5f40,0x5faf,0x5fad,0x60f7,0x6149,0x614a,
+ 0x612b,0x6145,0x6136,0x6132,0x612e,0x6146,0x612f,0x614f,0x6129,0x6140,
+ 0x6220,0x9168,0x6223,0x6225,0x6224,0x63c5,0x63f1,0x63eb,0x6410,0x6412,
+ 0x6409,0x6420,0x6424
+ },{
+ 0x6433,0x6443,0x641f,0x6415,0x6418,0x6439,0x6437,0x6422,0x6423,0x640c,
+ 0x6426,0x6430,0x6428,0x6441,0x6435,0x642f,0x640a,0x641a,0x6440,0x6425,
+ 0x6427,0x640b,0x63e7,0x641b,0x642e,0x6421,0x640e,0x656f,0x6592,0x65d3,
+ 0x6686,0x668c,0x6695,0x6690,0x668b,0x668a,0x6699,0x6694,0x6678,0x6720,
+ 0x6966,0x695f,0x6938,0x694e,0x6962,0x6971,0x693f,0x6945,0x696a,0x6939,
+ 0x6942,0x6957,0x6959,0x697a,0x6948,0x6949,0x6935,0x696c,0x6933,0x693d,
+ 0x6965,0x68f0,0x6978,0x6934,0x6969,0x6940,0x696f,0x6944,0x6976,0x6958,
+ 0x6941,0x6974,0x694c,0x693b,0x694b,0x6937,0x695c,0x694f,0x6951,0x6932,
+ 0x6952,0x692f,0x697b,0x693c,0x6b46,0x6b45,0x6b43,0x6b42,0x6b48,0x6b41,
+ 0x6b9b,0xfa0d,0x6bfb,0x6bfc
+ }
+ },
+ { /* ku 3e */
+ {
+ 0x6bf9,0x6bf7,0x6bf8,0x6e9b,0x6ed6,0x6ec8,0x6e8f,0x6ec0,0x6e9f,0x6e93,
+ 0x6e94,0x6ea0,0x6eb1,0x6eb9,0x6ec6,0x6ed2,0x6ebd,0x6ec1,0x6e9e,0x6ec9,
+ 0x6eb7,0x6eb0,0x6ecd,0x6ea6,0x6ecf,0x6eb2,0x6ebe,0x6ec3,0x6edc,0x6ed8,
+ 0x6e99,0x6e92,0x6e8e,0x6e8d,0x6ea4,0x6ea1,0x6ebf,0x6eb3,0x6ed0,0x6eca,
+ 0x6e97,0x6eae,0x6ea3,0x7147,0x7154,0x7152,0x7163,0x7160,0x7141,0x715d,
+ 0x7162,0x7172,0x7178,0x716a,0x7161,0x7142,0x7158,0x7143,0x714b,0x7170,
+ 0x715f,0x7150,0x7153
+ },{
+ 0x7144,0x714d,0x715a,0x724f,0x728d,0x728c,0x7291,0x7290,0x728e,0x733c,
+ 0x7342,0x733b,0x733a,0x7340,0x734a,0x7349,0x7444,0x744a,0x744b,0x7452,
+ 0x7451,0x7457,0x7440,0x744f,0x7450,0x744e,0x7442,0x7446,0x744d,0x7454,
+ 0x74e1,0x74ff,0x74fe,0x74fd,0x751d,0x7579,0x7577,0x6983,0x75ef,0x760f,
+ 0x7603,0x75f7,0x75fe,0x75fc,0x75f9,0x75f8,0x7610,0x75fb,0x75f6,0x75ed,
+ 0x75f5,0x75fd,0x7699,0x76b5,0x76dd,0x7755,0x775f,0x7760,0x7752,0x7756,
+ 0x775a,0x7769,0x7767,0x7754,0x7759,0x776d,0x77e0,0x7887,0x789a,0x7894,
+ 0x788f,0x7884,0x7895,0x7885,0x7886,0x78a1,0x7883,0x7879,0x7899,0x7880,
+ 0x7896,0x787b,0x797c,0x7982,0x797d,0x7979,0x7a11,0x7a18,0x7a19,0x7a12,
+ 0x7a17,0x7a15,0x7a22,0x7a13
+ }
+ },
+ { /* ku 3f */
+ {
+ 0x7a1b,0x7a10,0x7aa3,0x7aa2,0x7a9e,0x7aeb,0x7b66,0x7b64,0x7b6d,0x7b74,
+ 0x7b69,0x7b72,0x7b65,0x7b73,0x7b71,0x7b70,0x7b61,0x7b78,0x7b76,0x7b63,
+ 0x7cb2,0x7cb4,0x7caf,0x7d88,0x7d86,0x7d80,0x7d8d,0x7d7f,0x7d85,0x7d7a,
+ 0x7d8e,0x7d7b,0x7d83,0x7d7c,0x7d8c,0x7d94,0x7d84,0x7d7d,0x7d92,0x7f6d,
+ 0x7f6b,0x7f67,0x7f68,0x7f6c,0x7fa6,0x7fa5,0x7fa7,0x7fdb,0x7fdc,0x8021,
+ 0x8164,0x8160,0x8177,0x815c,0x8169,0x815b,0x8162,0x8172,0x6721,0x815e,
+ 0x8176,0x8167,0x816f
+ },{
+ 0x8144,0x8161,0x821d,0x8249,0x8244,0x8240,0x8242,0x8245,0x84f1,0x843f,
+ 0x8456,0x8476,0x8479,0x848f,0x848d,0x8465,0x8451,0x8440,0x8486,0x8467,
+ 0x8430,0x844d,0x847d,0x845a,0x8459,0x8474,0x8473,0x845d,0x8507,0x845e,
+ 0x8437,0x843a,0x8434,0x847a,0x8443,0x8478,0x8432,0x8445,0x8429,0x83d9,
+ 0x844b,0x842f,0x8442,0x842d,0x845f,0x8470,0x8439,0x844e,0x844c,0x8452,
+ 0x846f,0x84c5,0x848e,0x843b,0x8447,0x8436,0x8433,0x8468,0x847e,0x8444,
+ 0x842b,0x8460,0x8454,0x846e,0x8450,0x870b,0x8704,0x86f7,0x870c,0x86fa,
+ 0x86d6,0x86f5,0x874d,0x86f8,0x870e,0x8709,0x8701,0x86f6,0x870d,0x8705,
+ 0x88d6,0x88cb,0x88cd,0x88ce,0x88de,0x88db,0x88da,0x88cc,0x88d0,0x8985,
+ 0x899b,0x89df,0x89e5,0x89e4
+ }
+ },
+ { /* ku 40 */
+ {
+ 0x89e1,0x89e0,0x89e2,0x89dc,0x89e6,0x8a76,0x8a86,0x8a7f,0x8a61,0x8a3f,
+ 0x8a77,0x8a82,0x8a84,0x8a75,0x8a83,0x8a81,0x8a74,0x8a7a,0x8c3c,0x8c4b,
+ 0x8c4a,0x8c65,0x8c64,0x8c66,0x8c86,0x8c84,0x8c85,0x8ccc,0x8d68,0x8d69,
+ 0x8d91,0x8d8c,0x8d8e,0x8d8f,0x8d8d,0x8d93,0x8d94,0x8d90,0x8d92,0x8df0,
+ 0x8de0,0x8dec,0x8df1,0x8dee,0x8dd0,0x8de9,0x8de3,0x8de2,0x8de7,0x8df2,
+ 0x8deb,0x8df4,0x8f06,0x8eff,0x8f01,0x8f00,0x8f05,0x8f07,0x8f08,0x8f02,
+ 0x8f0b,0x9052,0x903f
+ },{
+ 0x9044,0x9049,0x903d,0x9110,0x910d,0x910f,0x9111,0x9116,0x9114,0x910b,
+ 0x910e,0x916e,0x916f,0x9248,0x9252,0x9230,0x923a,0x9266,0x9233,0x9265,
+ 0x925e,0x9283,0x922e,0x924a,0x9246,0x926d,0x926c,0x924f,0x9260,0x9267,
+ 0x926f,0x9236,0x9261,0x9270,0x9231,0x9254,0x9263,0x9250,0x9272,0x924e,
+ 0x9253,0x924c,0x9256,0x9232,0x959f,0x959c,0x959e,0x959b,0x9692,0x9693,
+ 0x9691,0x9697,0x96ce,0x96fa,0x96fd,0x96f8,0x96f5,0x9773,0x9777,0x9778,
+ 0x9772,0x980f,0x980d,0x980e,0x98ac,0x98f6,0x98f9,0x99af,0x99b2,0x99b0,
+ 0x99b5,0x9aad,0x9aab,0x9b5b,0x9cea,0x9ced,0x9ce7,0x9e80,0x9efd,0x50e6,
+ 0x50d4,0x50d7,0x50e8,0x50f3,0x50db,0x50ea,0x50dd,0x50e4,0x50d3,0x50ec,
+ 0x50f0,0x50ef,0x50e3,0x50e0
+ }
+ },
+ { /* ku 41 */
+ {
+ 0x51d8,0x5280,0x5281,0x52e9,0x52eb,0x5330,0x53ac,0x5627,0x5615,0x560c,
+ 0x5612,0x55fc,0x560f,0x561c,0x5601,0x5613,0x5602,0x55fa,0x561d,0x5604,
+ 0x55ff,0x55f9,0x5889,0x587c,0x5890,0x5898,0x5886,0x5881,0x587f,0x5874,
+ 0x588b,0x587a,0x5887,0x5891,0x588e,0x5876,0x5882,0x5888,0x587b,0x5894,
+ 0x588f,0x58fe,0x596b,0x5adc,0x5aee,0x5ae5,0x5ad5,0x5aea,0x5ada,0x5aed,
+ 0x5aeb,0x5af3,0x5ae2,0x5ae0,0x5adb,0x5aec,0x5ade,0x5add,0x5ad9,0x5ae8,
+ 0x5adf,0x5b77,0x5be0
+ },{
+ 0x5be3,0x5c63,0x5d82,0x5d80,0x5d7d,0x5d86,0x5d7a,0x5d81,0x5d77,0x5d8a,
+ 0x5d89,0x5d88,0x5d7e,0x5d7c,0x5d8d,0x5d79,0x5d7f,0x5e58,0x5e59,0x5e53,
+ 0x5ed8,0x5ed1,0x5ed7,0x5ece,0x5edc,0x5ed5,0x5ed9,0x5ed2,0x5ed4,0x5f44,
+ 0x5f43,0x5f6f,0x5fb6,0x612c,0x6128,0x6141,0x615e,0x6171,0x6173,0x6152,
+ 0x6153,0x6172,0x616c,0x6180,0x6174,0x6154,0x617a,0x615b,0x6165,0x613b,
+ 0x616a,0x6161,0x6156,0x6229,0x6227,0x622b,0x642b,0x644d,0x645b,0x645d,
+ 0x6474,0x6476,0x6472,0x6473,0x647d,0x6475,0x6466,0x64a6,0x644e,0x6482,
+ 0x645e,0x645c,0x644b,0x6453,0x6460,0x6450,0x647f,0x643f,0x646c,0x646b,
+ 0x6459,0x6465,0x6477,0x6573,0x65a0,0x66a1,0x66a0,0x669f,0x6705,0x6704,
+ 0x6722,0x69b1,0x69b6,0x69c9
+ }
+ },
+ { /* ku 42 */
+ {
+ 0x69a0,0x69ce,0x6996,0x69b0,0x69ac,0x69bc,0x6991,0x6999,0x698e,0x69a7,
+ 0x698d,0x69a9,0x69be,0x69af,0x69bf,0x69c4,0x69bd,0x69a4,0x69d4,0x69b9,
+ 0x69ca,0x699a,0x69cf,0x69b3,0x6993,0x69aa,0x69a1,0x699e,0x69d9,0x6997,
+ 0x6990,0x69c2,0x69b5,0x69a5,0x69c6,0x6b4a,0x6b4d,0x6b4b,0x6b9e,0x6b9f,
+ 0x6ba0,0x6bc3,0x6bc4,0x6bfe,0x6ece,0x6ef5,0x6ef1,0x6f03,0x6f25,0x6ef8,
+ 0x6f37,0x6efb,0x6f2e,0x6f09,0x6f4e,0x6f19,0x6f1a,0x6f27,0x6f18,0x6f3b,
+ 0x6f12,0x6eed,0x6f0a
+ },{
+ 0x6f36,0x6f73,0x6ef9,0x6eee,0x6f2d,0x6f40,0x6f30,0x6f3c,0x6f35,0x6eeb,
+ 0x6f07,0x6f0e,0x6f43,0x6f05,0x6efd,0x6ef6,0x6f39,0x6f1c,0x6efc,0x6f3a,
+ 0x6f1f,0x6f0d,0x6f1e,0x6f08,0x6f21,0x7187,0x7190,0x7189,0x7180,0x7185,
+ 0x7182,0x718f,0x717b,0x7186,0x7181,0x7197,0x7244,0x7253,0x7297,0x7295,
+ 0x7293,0x7343,0x734d,0x7351,0x734c,0x7462,0x7473,0x7471,0x7475,0x7472,
+ 0x7467,0x746e,0x7500,0x7502,0x7503,0x757d,0x7590,0x7616,0x7608,0x760c,
+ 0x7615,0x7611,0x760a,0x7614,0x76b8,0x7781,0x777c,0x7785,0x7782,0x776e,
+ 0x7780,0x776f,0x777e,0x7783,0x78b2,0x78aa,0x78b4,0x78ad,0x78a8,0x787e,
+ 0x78ab,0x789e,0x78a5,0x78a0,0x78ac,0x78a2,0x78a4,0x7998,0x798a,0x798b,
+ 0x7996,0x7995,0x7994,0x7993
+ }
+ },
+ { /* ku 43 */
+ {
+ 0x7997,0x7988,0x7992,0x7990,0x7a2b,0x7a4a,0x7a30,0x7a2f,0x7a28,0x7a26,
+ 0x7aa8,0x7aab,0x7aac,0x7aee,0x7b88,0x7b9c,0x7b8a,0x7b91,0x7b90,0x7b96,
+ 0x7b8d,0x7b8c,0x7b9b,0x7b8e,0x7b85,0x7b98,0x5284,0x7b99,0x7ba4,0x7b82,
+ 0x7cbb,0x7cbf,0x7cbc,0x7cba,0x7da7,0x7db7,0x7dc2,0x7da3,0x7daa,0x7dc1,
+ 0x7dc0,0x7dc5,0x7d9d,0x7dce,0x7dc4,0x7dc6,0x7dcb,0x7dcc,0x7daf,0x7db9,
+ 0x7d96,0x7dbc,0x7d9f,0x7da6,0x7dae,0x7da9,0x7da1,0x7dc9,0x7f73,0x7fe2,
+ 0x7fe3,0x7fe5,0x7fde
+ },{
+ 0x8024,0x805d,0x805c,0x8189,0x8186,0x8183,0x8187,0x818d,0x818c,0x818b,
+ 0x8215,0x8497,0x84a4,0x84a1,0x849f,0x84ba,0x84ce,0x84c2,0x84ac,0x84ae,
+ 0x84ab,0x84b9,0x84b4,0x84c1,0x84cd,0x84aa,0x849a,0x84b1,0x84d0,0x849d,
+ 0x84a7,0x84bb,0x84a2,0x8494,0x84c7,0x84cc,0x849b,0x84a9,0x84af,0x84a8,
+ 0x84d6,0x8498,0x84b6,0x84cf,0x84a0,0x84d7,0x84d4,0x84d2,0x84db,0x84b0,
+ 0x8491,0x8661,0x8733,0x8723,0x8728,0x876b,0x8740,0x872e,0x871e,0x8721,
+ 0x8719,0x871b,0x8743,0x872c,0x8741,0x873e,0x8746,0x8720,0x8732,0x872a,
+ 0x872d,0x873c,0x8712,0x873a,0x8731,0x8735,0x8742,0x8726,0x8727,0x8738,
+ 0x8724,0x871a,0x8730,0x8711,0x88f7,0x88e7,0x88f1,0x88f2,0x88fa,0x88fe,
+ 0x88ee,0x88fc,0x88f6,0x88fb
+ }
+ },
+ { /* ku 44 */
+ {
+ 0x88f0,0x88ec,0x88eb,0x899d,0x89a1,0x899f,0x899e,0x89e9,0x89eb,0x89e8,
+ 0x8aab,0x8a99,0x8a8b,0x8a92,0x8a8f,0x8a96,0x8c3d,0x8c68,0x8c69,0x8cd5,
+ 0x8ccf,0x8cd7,0x8d96,0x8e09,0x8e02,0x8dff,0x8e0d,0x8dfd,0x8e0a,0x8e03,
+ 0x8e07,0x8e06,0x8e05,0x8dfe,0x8e00,0x8e04,0x8f10,0x8f11,0x8f0e,0x8f0d,
+ 0x9123,0x911c,0x9120,0x9122,0x911f,0x911d,0x911a,0x9124,0x9121,0x911b,
+ 0x917a,0x9172,0x9179,0x9173,0x92a5,0x92a4,0x9276,0x929b,0x927a,0x92a0,
+ 0x9294,0x92aa,0x928d
+ },{
+ 0x92a6,0x929a,0x92ab,0x9279,0x9297,0x927f,0x92a3,0x92ee,0x928e,0x9282,
+ 0x9295,0x92a2,0x927d,0x9288,0x92a1,0x928a,0x9286,0x928c,0x9299,0x92a7,
+ 0x927e,0x9287,0x92a9,0x929d,0x928b,0x922d,0x969e,0x96a1,0x96ff,0x9758,
+ 0x977d,0x977a,0x977e,0x9783,0x9780,0x9782,0x977b,0x9784,0x9781,0x977f,
+ 0x97ce,0x97cd,0x9816,0x98ad,0x98ae,0x9902,0x9900,0x9907,0x999d,0x999c,
+ 0x99c3,0x99b9,0x99bb,0x99ba,0x99c2,0x99bd,0x99c7,0x9ab1,0x9ae3,0x9ae7,
+ 0x9b3e,0x9b3f,0x9b60,0x9b61,0x9b5f,0x9cf1,0x9cf2,0x9cf5,0x9ea7,0x50ff,
+ 0x5103,0x5130,0x50f8,0x5106,0x5107,0x50f6,0x50fe,0x510b,0x510c,0x50fd,
+ 0x510a,0x528b,0x528c,0x52f1,0x52ef,0x5648,0x5642,0x564c,0x5635,0x5641,
+ 0x564a,0x5649,0x5646,0x5658
+ }
+ },
+ { /* ku 45 */
+ {
+ 0x565a,0x5640,0x5633,0x563d,0x562c,0x563e,0x5638,0x562a,0x563a,0x571a,
+ 0x58ab,0x589d,0x58b1,0x58a0,0x58a3,0x58af,0x58ac,0x58a5,0x58a1,0x58ff,
+ 0x5aff,0x5af4,0x5afd,0x5af7,0x5af6,0x5b03,0x5af8,0x5b02,0x5af9,0x5b01,
+ 0x5b07,0x5b05,0x5b0f,0x5c67,0x5d99,0x5d97,0x5d9f,0x5d92,0x5da2,0x5d93,
+ 0x5d95,0x5da0,0x5d9c,0x5da1,0x5d9a,0x5d9e,0x5e69,0x5e5d,0x5e60,0x5e5c,
+ 0x7df3,0x5edb,0x5ede,0x5ee1,0x5f49,0x5fb2,0x618b,0x6183,0x6179,0x61b1,
+ 0x61b0,0x61a2,0x6189
+ },{
+ 0x619b,0x6193,0x61af,0x61ad,0x619f,0x6192,0x61aa,0x61a1,0x618d,0x6166,
+ 0x61b3,0x622d,0x646e,0x6470,0x6496,0x64a0,0x6485,0x6497,0x649c,0x648f,
+ 0x648b,0x648a,0x648c,0x64a3,0x649f,0x6468,0x64b1,0x6498,0x6576,0x657a,
+ 0x6579,0x657b,0x65b2,0x65b3,0x66b5,0x66b0,0x66a9,0x66b2,0x66b7,0x66aa,
+ 0x66af,0x6a00,0x6a06,0x6a17,0x69e5,0x69f8,0x6a15,0x69f1,0x69e4,0x6a20,
+ 0x69ff,0x69ec,0x69e2,0x6a1b,0x6a1d,0x69fe,0x6a27,0x69f2,0x69ee,0x6a14,
+ 0x69f7,0x69e7,0x6a40,0x6a08,0x69e6,0x69fb,0x6a0d,0x69fc,0x69eb,0x6a09,
+ 0x6a04,0x6a18,0x6a25,0x6a0f,0x69f6,0x6a26,0x6a07,0x69f4,0x6a16,0x6b51,
+ 0x6ba5,0x6ba3,0x6ba2,0x6ba6,0x6c01,0x6c00,0x6bff,0x6c02,0x6f41,0x6f26,
+ 0x6f7e,0x6f87,0x6fc6,0x6f92
+ }
+ },
+ { /* ku 46 */
+ {
+ 0x6f8d,0x6f89,0x6f8c,0x6f62,0x6f4f,0x6f85,0x6f5a,0x6f96,0x6f76,0x6f6c,
+ 0x6f82,0x6f55,0x6f72,0x6f52,0x6f50,0x6f57,0x6f94,0x6f93,0x6f5d,0x6f00,
+ 0x6f61,0x6f6b,0x6f7d,0x6f67,0x6f90,0x6f53,0x6f8b,0x6f69,0x6f7f,0x6f95,
+ 0x6f63,0x6f77,0x6f6a,0x6f7b,0x71b2,0x71af,0x719b,0x71b0,0x71a0,0x719a,
+ 0x71a9,0x71b5,0x719d,0x71a5,0x719e,0x71a4,0x71a1,0x71aa,0x719c,0x71a7,
+ 0x71b3,0x7298,0x729a,0x7358,0x7352,0x735e,0x735f,0x7360,0x735d,0x735b,
+ 0x7361,0x735a,0x7359
+ },{
+ 0x7362,0x7487,0x7489,0x748a,0x7486,0x7481,0x747d,0x7485,0x7488,0x747c,
+ 0x7479,0x7508,0x7507,0x757e,0x7625,0x761e,0x7619,0x761d,0x761c,0x7623,
+ 0x761a,0x7628,0x761b,0x769c,0x769d,0x769e,0x769b,0x778d,0x778f,0x7789,
+ 0x7788,0x78cd,0x78bb,0x78cf,0x78cc,0x78d1,0x78ce,0x78d4,0x78c8,0x78c3,
+ 0x78c4,0x78c9,0x799a,0x79a1,0x79a0,0x799c,0x79a2,0x799b,0x6b76,0x7a39,
+ 0x7ab2,0x7ab4,0x7ab3,0x7bb7,0x7bcb,0x7bbe,0x7bac,0x7bce,0x7baf,0x7bb9,
+ 0x7bca,0x7bb5,0x7cc5,0x7cc8,0x7ccc,0x7ccb,0x7df7,0x7ddb,0x7dea,0x7de7,
+ 0x7dd7,0x7de1,0x7e03,0x7dfa,0x7de6,0x7df6,0x7df1,0x7df0,0x7dee,0x7ddf,
+ 0x7f76,0x7fac,0x7fb0,0x7fad,0x7fed,0x7feb,0x7fea,0x7fec,0x7fe6,0x7fe8,
+ 0x8064,0x8067,0x81a3,0x819f
+ }
+ },
+ { /* ku 47 */
+ {
+ 0x819e,0x8195,0x81a2,0x8199,0x8197,0x8216,0x824f,0x8253,0x8252,0x8250,
+ 0x824e,0x8251,0x8524,0x853b,0x850f,0x8500,0x8529,0x850e,0x8509,0x850d,
+ 0x851f,0x850a,0x8527,0x851c,0x84fb,0x852b,0x84fa,0x8508,0x850c,0x84f4,
+ 0x852a,0x84f2,0x8515,0x84f7,0x84eb,0x84f3,0x84fc,0x8512,0x84ea,0x84e9,
+ 0x8516,0x84fe,0x8528,0x851d,0x852e,0x8502,0x84fd,0x851e,0x84f6,0x8531,
+ 0x8526,0x84e7,0x84e8,0x84f0,0x84ef,0x84f9,0x8518,0x8520,0x8530,0x850b,
+ 0x8519,0x852f,0x8662
+ },{
+ 0x8756,0x8763,0x8764,0x8777,0x87e1,0x8773,0x8758,0x8754,0x875b,0x8752,
+ 0x8761,0x875a,0x8751,0x875e,0x876d,0x876a,0x8750,0x874e,0x875f,0x875d,
+ 0x876f,0x876c,0x877a,0x876e,0x875c,0x8765,0x874f,0x877b,0x8775,0x8762,
+ 0x8767,0x8769,0x885a,0x8905,0x890c,0x8914,0x890b,0x8917,0x8918,0x8919,
+ 0x8906,0x8916,0x8911,0x890e,0x8909,0x89a2,0x89a4,0x89a3,0x89ed,0x89f0,
+ 0x89ec,0x8acf,0x8ac6,0x8ab8,0x8ad3,0x8ad1,0x8ad4,0x8ad5,0x8abb,0x8ad7,
+ 0x8abe,0x8ac0,0x8ac5,0x8ad8,0x8ac3,0x8aba,0x8abd,0x8ad9,0x8c3e,0x8c4d,
+ 0x8c8f,0x8ce5,0x8cdf,0x8cd9,0x8ce8,0x8cda,0x8cdd,0x8ce7,0x8da0,0x8d9c,
+ 0x8da1,0x8d9b,0x8e20,0x8e23,0x8e25,0x8e24,0x8e2e,0x8e15,0x8e1b,0x8e16,
+ 0x8e11,0x8e19,0x8e26,0x8e27
+ }
+ },
+ { /* ku 48 */
+ {
+ 0x8e14,0x8e12,0x8e18,0x8e13,0x8e1c,0x8e17,0x8e1a,0x8f2c,0x8f24,0x8f18,
+ 0x8f1a,0x8f20,0x8f23,0x8f16,0x8f17,0x9073,0x9070,0x906f,0x9067,0x906b,
+ 0x912f,0x912b,0x9129,0x912a,0x9132,0x9126,0x912e,0x9185,0x9186,0x918a,
+ 0x9181,0x9182,0x9184,0x9180,0x92d0,0x92c3,0x92c4,0x92c0,0x92d9,0x92b6,
+ 0x92cf,0x92f1,0x92df,0x92d8,0x92e9,0x92d7,0x92dd,0x92cc,0x92ef,0x92c2,
+ 0x92e8,0x92ca,0x92c8,0x92ce,0x92e6,0x92cd,0x92d5,0x92c9,0x92e0,0x92de,
+ 0x92e7,0x92d1,0x92d3
+ },{
+ 0x92b5,0x92e1,0x92c6,0x92b4,0x957c,0x95ac,0x95ab,0x95ae,0x95b0,0x96a4,
+ 0x96a2,0x96d3,0x9705,0x9708,0x9702,0x975a,0x978a,0x978e,0x9788,0x97d0,
+ 0x97cf,0x981e,0x981d,0x9826,0x9829,0x9828,0x9820,0x981b,0x9827,0x98b2,
+ 0x9908,0x98fa,0x9911,0x9914,0x9916,0x9917,0x9915,0x99dc,0x99cd,0x99cf,
+ 0x99d3,0x99d4,0x99ce,0x99c9,0x99d6,0x99d8,0x99cb,0x99d7,0x99cc,0x9ab3,
+ 0x9aec,0x9aeb,0x9af3,0x9af2,0x9af1,0x9b46,0x9b43,0x9b67,0x9b74,0x9b71,
+ 0x9b66,0x9b76,0x9b75,0x9b70,0x9b68,0x9b64,0x9b6c,0x9cfc,0x9cfa,0x9cfd,
+ 0x9cff,0x9cf7,0x9d07,0x9d00,0x9cf9,0x9cfb,0x9d08,0x9d05,0x9d04,0x9e83,
+ 0x9ed3,0x9f0f,0x9f10,0x511c,0x5113,0x5117,0x511a,0x5111,0x51de,0x5334,
+ 0x53e1,0x5670,0x5660,0x566e
+ }
+ },
+ { /* ku 49 */
+ {
+ 0x5673,0x5666,0x5663,0x566d,0x5672,0x565e,0x5677,0x571c,0x571b,0x58c8,
+ 0x58bd,0x58c9,0x58bf,0x58ba,0x58c2,0x58bc,0x58c6,0x5b17,0x5b19,0x5b1b,
+ 0x5b21,0x5b14,0x5b13,0x5b10,0x5b16,0x5b28,0x5b1a,0x5b20,0x5b1e,0x5bef,
+ 0x5dac,0x5db1,0x5da9,0x5da7,0x5db5,0x5db0,0x5dae,0x5daa,0x5da8,0x5db2,
+ 0x5dad,0x5daf,0x5db4,0x5e67,0x5e68,0x5e66,0x5e6f,0x5ee9,0x5ee7,0x5ee6,
+ 0x5ee8,0x5ee5,0x5f4b,0x5fbc,0x619d,0x61a8,0x6196,0x61c5,0x61b4,0x61c6,
+ 0x61c1,0x61cc,0x61ba
+ },{
+ 0x61bf,0x61b8,0x618c,0x64d7,0x64d6,0x64d0,0x64cf,0x64c9,0x64bd,0x6489,
+ 0x64c3,0x64db,0x64f3,0x64d9,0x6533,0x657f,0x657c,0x65a2,0x66c8,0x66be,
+ 0x66c0,0x66ca,0x66cb,0x66cf,0x66bd,0x66bb,0x66ba,0x66cc,0x6723,0x6a34,
+ 0x6a66,0x6a49,0x6a67,0x6a32,0x6a68,0x6a3e,0x6a5d,0x6a6d,0x6a76,0x6a5b,
+ 0x6a51,0x6a28,0x6a5a,0x6a3b,0x6a3f,0x6a41,0x6a6a,0x6a64,0x6a50,0x6a4f,
+ 0x6a54,0x6a6f,0x6a69,0x6a60,0x6a3c,0x6a5e,0x6a56,0x6a55,0x6a4d,0x6a4e,
+ 0x6a46,0x6b55,0x6b54,0x6b56,0x6ba7,0x6baa,0x6bab,0x6bc8,0x6bc7,0x6c04,
+ 0x6c03,0x6c06,0x6fad,0x6fcb,0x6fa3,0x6fc7,0x6fbc,0x6fce,0x6fc8,0x6f5e,
+ 0x6fc4,0x6fbd,0x6f9e,0x6fca,0x6fa8,0x7004,0x6fa5,0x6fae,0x6fba,0x6fac,
+ 0x6faa,0x6fcf,0x6fbf,0x6fb8
+ }
+ },
+ { /* ku 4a */
+ {
+ 0x6fa2,0x6fc9,0x6fab,0x6fcd,0x6faf,0x6fb2,0x6fb0,0x71c5,0x71c2,0x71bf,
+ 0x71b8,0x71d6,0x71c0,0x71c1,0x71cb,0x71d4,0x71ca,0x71c7,0x71cf,0x71bd,
+ 0x71d8,0x71bc,0x71c6,0x71da,0x71db,0x729d,0x729e,0x7369,0x7366,0x7367,
+ 0x736c,0x7365,0x736b,0x736a,0x747f,0x749a,0x74a0,0x7494,0x7492,0x7495,
+ 0x74a1,0x750b,0x7580,0x762f,0x762d,0x7631,0x763d,0x7633,0x763c,0x7635,
+ 0x7632,0x7630,0x76bb,0x76e6,0x779a,0x779d,0x77a1,0x779c,0x779b,0x77a2,
+ 0x77a3,0x7795,0x7799
+ },{
+ 0x7797,0x78dd,0x78e9,0x78e5,0x78ea,0x78de,0x78e3,0x78db,0x78e1,0x78e2,
+ 0x78ed,0x78df,0x78e0,0x79a4,0x7a44,0x7a48,0x7a47,0x7ab6,0x7ab8,0x7ab5,
+ 0x7ab1,0x7ab7,0x7bde,0x7be3,0x7be7,0x7bdd,0x7bd5,0x7be5,0x7bda,0x7be8,
+ 0x7bf9,0x7bd4,0x7bea,0x7be2,0x7bdc,0x7beb,0x7bd8,0x7bdf,0x7cd2,0x7cd4,
+ 0x7cd7,0x7cd0,0x7cd1,0x7e12,0x7e21,0x7e17,0x7e0c,0x7e1f,0x7e20,0x7e13,
+ 0x7e0e,0x7e1c,0x7e15,0x7e1a,0x7e22,0x7e0b,0x7e0f,0x7e16,0x7e0d,0x7e14,
+ 0x7e25,0x7e24,0x7f43,0x7f7b,0x7f7c,0x7f7a,0x7fb1,0x7fef,0x802a,0x8029,
+ 0x806c,0x81b1,0x81a6,0x81ae,0x81b9,0x81b5,0x81ab,0x81b0,0x81ac,0x81b4,
+ 0x81b2,0x81b7,0x81a7,0x81f2,0x8255,0x8256,0x8257,0x8556,0x8545,0x856b,
+ 0x854d,0x8553,0x8561,0x8558
+ }
+ },
+ { /* ku 4b */
+ {
+ 0x8540,0x8546,0x8564,0x8541,0x8562,0x8544,0x8551,0x8547,0x8563,0x853e,
+ 0x855b,0x8571,0x854e,0x856e,0x8575,0x8555,0x8567,0x8560,0x858c,0x8566,
+ 0x855d,0x8554,0x8565,0x856c,0x8663,0x8665,0x8664,0x879b,0x878f,0x8797,
+ 0x8793,0x8792,0x8788,0x8781,0x8796,0x8798,0x8779,0x8787,0x87a3,0x8785,
+ 0x8790,0x8791,0x879d,0x8784,0x8794,0x879c,0x879a,0x8789,0x891e,0x8926,
+ 0x8930,0x892d,0x892e,0x8927,0x8931,0x8922,0x8929,0x8923,0x892f,0x892c,
+ 0x891f,0x89f1,0x8ae0
+ },{
+ 0x8ae2,0x8af2,0x8af4,0x8af5,0x8add,0x8b14,0x8ae4,0x8adf,0x8af0,0x8ac8,
+ 0x8ade,0x8ae1,0x8ae8,0x8aff,0x8aef,0x8afb,0x8c91,0x8c92,0x8c90,0x8cf5,
+ 0x8cee,0x8cf1,0x8cf0,0x8cf3,0x8d6c,0x8d6e,0x8da5,0x8da7,0x8e33,0x8e3e,
+ 0x8e38,0x8e40,0x8e45,0x8e36,0x8e3c,0x8e3d,0x8e41,0x8e30,0x8e3f,0x8ebd,
+ 0x8f36,0x8f2e,0x8f35,0x8f32,0x8f39,0x8f37,0x8f34,0x9076,0x9079,0x907b,
+ 0x9086,0x90fa,0x9133,0x9135,0x9136,0x9193,0x9190,0x9191,0x918d,0x918f,
+ 0x9327,0x931e,0x9308,0x931f,0x9306,0x930f,0x937a,0x9338,0x933c,0x931b,
+ 0x9323,0x9312,0x9301,0x9346,0x932d,0x930e,0x930d,0x92cb,0x931d,0x92fa,
+ 0x9325,0x9313,0x92f9,0x92f7,0x9334,0x9302,0x9324,0x92ff,0x9329,0x9339,
+ 0x9335,0x932a,0x9314,0x930c
+ }
+ },
+ { /* ku 4c */
+ {
+ 0x930b,0x92fe,0x9309,0x9300,0x92fb,0x9316,0x95bc,0x95cd,0x95be,0x95b9,
+ 0x95ba,0x95b6,0x95bf,0x95b5,0x95bd,0x96a9,0x96d4,0x970b,0x9712,0x9710,
+ 0x9799,0x9797,0x9794,0x97f0,0x97f8,0x9835,0x982f,0x9832,0x9924,0x991f,
+ 0x9927,0x9929,0x999e,0x99ee,0x99ec,0x99e5,0x99e4,0x99f0,0x99e3,0x99ea,
+ 0x99e9,0x99e7,0x9ab9,0x9abf,0x9ab4,0x9abb,0x9af6,0x9afa,0x9af9,0x9af7,
+ 0x9b33,0x9b80,0x9b85,0x9b87,0x9b7c,0x9b7e,0x9b7b,0x9b82,0x9b93,0x9b92,
+ 0x9b90,0x9b7a,0x9b95
+ },{
+ 0x9b7d,0x9b88,0x9d25,0x9d17,0x9d20,0x9d1e,0x9d14,0x9d29,0x9d1d,0x9d18,
+ 0x9d22,0x9d10,0x9d19,0x9d1f,0x9e88,0x9e86,0x9e87,0x9eae,0x9ead,0x9ed5,
+ 0x9ed6,0x9efa,0x9f12,0x9f3d,0x5126,0x5125,0x5122,0x5124,0x5120,0x5129,
+ 0x52f4,0x5693,0x568c,0x568d,0x5686,0x5684,0x5683,0x567e,0x5682,0x567f,
+ 0x5681,0x58d6,0x58d4,0x58cf,0x58d2,0x5b2d,0x5b25,0x5b32,0x5b23,0x5b2c,
+ 0x5b27,0x5b26,0x5b2f,0x5b2e,0x5b7b,0x5bf1,0x5bf2,0x5db7,0x5e6c,0x5e6a,
+ 0x5fbe,0x5fbb,0x61c3,0x61b5,0x61bc,0x61e7,0x61e0,0x61e5,0x61e4,0x61e8,
+ 0x61de,0x64ef,0x64e9,0x64e3,0x64eb,0x64e4,0x64e8,0x6581,0x6580,0x65b6,
+ 0x65da,0x66d2,0x6a8d,0x6a96,0x6a81,0x6aa5,0x6a89,0x6a9f,0x6a9b,0x6aa1,
+ 0x6a9e,0x6a87,0x6a93,0x6a8e
+ }
+ },
+ { /* ku 4d */
+ {
+ 0x6a95,0x6a83,0x6aa8,0x6aa4,0x6a91,0x6a7f,0x6aa6,0x6a9a,0x6a85,0x6a8c,
+ 0x6a92,0x6b5b,0x6bad,0x6c09,0x6fcc,0x6fa9,0x6ff4,0x6fd4,0x6fe3,0x6fdc,
+ 0x6fed,0x6fe7,0x6fe6,0x6fde,0x6ff2,0x6fdd,0x6fe2,0x6fe8,0x71e1,0x71f1,
+ 0x71e8,0x71f2,0x71e4,0x71f0,0x71e2,0x7373,0x736e,0x736f,0x7497,0x74b2,
+ 0x74ab,0x7490,0x74aa,0x74ad,0x74b1,0x74a5,0x74af,0x7510,0x7511,0x7512,
+ 0x750f,0x7584,0x7643,0x7648,0x7649,0x7647,0x76a4,0x76e9,0x77b5,0x77ab,
+ 0x77b2,0x77b7,0x77b6
+ },{
+ 0x77b4,0x77b1,0x77a8,0x77f0,0x78f3,0x78fd,0x7902,0x78fb,0x78fc,0x78f2,
+ 0x7905,0x78f9,0x78fe,0x7904,0x79ab,0x79a8,0x7a5c,0x7a5b,0x7a56,0x7a58,
+ 0x7a54,0x7a5a,0x7abe,0x7ac0,0x7ac1,0x7c05,0x7c0f,0x7bf2,0x7c00,0x7bff,
+ 0x7bfb,0x7c0e,0x7bf4,0x7c0b,0x7bf3,0x7c02,0x7c09,0x7c03,0x7c01,0x7bf8,
+ 0x7bfd,0x7c06,0x7bf0,0x7bf1,0x7c10,0x7c0a,0x7ce8,0x7e2d,0x7e3c,0x7e42,
+ 0x7e33,0x9848,0x7e38,0x7e2a,0x7e49,0x7e40,0x7e47,0x7e29,0x7e4c,0x7e30,
+ 0x7e3b,0x7e36,0x7e44,0x7e3a,0x7f45,0x7f7f,0x7f7e,0x7f7d,0x7ff4,0x7ff2,
+ 0x802c,0x81bb,0x81c4,0x81cc,0x81ca,0x81c5,0x81c7,0x81bc,0x81e9,0x825b,
+ 0x825a,0x825c,0x8583,0x8580,0x858f,0x85a7,0x8595,0x85a0,0x858b,0x85a3,
+ 0x857b,0x85a4,0x859a,0x859e
+ }
+ },
+ { /* ku 4e */
+ {
+ 0x8577,0x857c,0x8589,0x85a1,0x857a,0x8578,0x8557,0x858e,0x8596,0x8586,
+ 0x858d,0x8599,0x859d,0x8581,0x85a2,0x8582,0x8588,0x8585,0x8579,0x8576,
+ 0x8598,0x8590,0x859f,0x8668,0x87be,0x87aa,0x87ad,0x87c5,0x87b0,0x87ac,
+ 0x87b9,0x87b5,0x87bc,0x87ae,0x87c9,0x87c3,0x87c2,0x87cc,0x87b7,0x87af,
+ 0x87c4,0x87ca,0x87b4,0x87b6,0x87bf,0x87b8,0x87bd,0x87de,0x87b2,0x8935,
+ 0x8933,0x893c,0x893e,0x8941,0x8952,0x8937,0x8942,0x89ad,0x89af,0x89ae,
+ 0x89f2,0x89f3,0x8b1e
+ },{
+ 0x8b18,0x8b16,0x8b11,0x8b05,0x8b0b,0x8b22,0x8b0f,0x8b12,0x8b15,0x8b07,
+ 0x8b0d,0x8b08,0x8b06,0x8b1c,0x8b13,0x8b1a,0x8c4f,0x8c70,0x8c72,0x8c71,
+ 0x8c6f,0x8c95,0x8c94,0x8cf9,0x8d6f,0x8e4e,0x8e4d,0x8e53,0x8e50,0x8e4c,
+ 0x8e47,0x8f43,0x8f40,0x9085,0x907e,0x9138,0x919a,0x91a2,0x919b,0x9199,
+ 0x919f,0x91a1,0x919d,0x91a0,0x93a1,0x9383,0x93af,0x9364,0x9356,0x9347,
+ 0x937c,0x9358,0x935c,0x9376,0x9349,0x9350,0x9351,0x9360,0x936d,0x938f,
+ 0x934c,0x936a,0x9379,0x9357,0x9355,0x9352,0x934f,0x9371,0x9377,0x937b,
+ 0x9361,0x935e,0x9363,0x9367,0x9380,0x934e,0x9359,0x95c7,0x95c0,0x95c9,
+ 0x95c3,0x95c5,0x95b7,0x96ae,0x96b0,0x96ac,0x9720,0x971f,0x9718,0x971d,
+ 0x9719,0x979a,0x97a1,0x979c
+ }
+ },
+ { /* ku 4f */
+ {
+ 0x979e,0x979d,0x97d5,0x97d4,0x97f1,0x9841,0x9844,0x984a,0x9849,0x9845,
+ 0x9843,0x9925,0x992b,0x992c,0x992a,0x9933,0x9932,0x992f,0x992d,0x9931,
+ 0x9930,0x9998,0x99a3,0x99a1,0x9a02,0x99fa,0x99f4,0x99f7,0x99f9,0x99f8,
+ 0x99f6,0x99fb,0x99fd,0x99fe,0x99fc,0x9a03,0x9abe,0x9afe,0x9afd,0x9b01,
+ 0x9afc,0x9b48,0x9b9a,0x9ba8,0x9b9e,0x9b9b,0x9ba6,0x9ba1,0x9ba5,0x9ba4,
+ 0x9b86,0x9ba2,0x9ba0,0x9baf,0x9d33,0x9d41,0x9d67,0x9d36,0x9d2e,0x9d2f,
+ 0x9d31,0x9d38,0x9d30
+ },{
+ 0x9d45,0x9d42,0x9d43,0x9d3e,0x9d37,0x9d40,0x9d3d,0x7ff5,0x9d2d,0x9e8a,
+ 0x9e89,0x9e8d,0x9eb0,0x9ec8,0x9eda,0x9efb,0x9eff,0x9f24,0x9f23,0x9f22,
+ 0x9f54,0x9fa0,0x5131,0x512d,0x512e,0x5698,0x569c,0x5697,0x569a,0x569d,
+ 0x5699,0x5970,0x5b3c,0x5c69,0x5c6a,0x5dc0,0x5e6d,0x5e6e,0x61d8,0x61df,
+ 0x61ed,0x61ee,0x61f1,0x61ea,0x61f0,0x61eb,0x61d6,0x61e9,0x64ff,0x6504,
+ 0x64fd,0x64f8,0x6501,0x6503,0x64fc,0x6594,0x65db,0x66da,0x66db,0x66d8,
+ 0x6ac5,0x6ab9,0x6abd,0x6ae1,0x6ac6,0x6aba,0x6ab6,0x6ab7,0x6ac7,0x6ab4,
+ 0x6aad,0x6b5e,0x6bc9,0x6c0b,0x7007,0x700c,0x700d,0x7001,0x7005,0x7014,
+ 0x700e,0x6fff,0x7000,0x6ffb,0x7026,0x6ffc,0x6ff7,0x700a,0x7201,0x71ff,
+ 0x71f9,0x7203,0x71fd,0x7376
+ }
+ },
+ { /* ku 50 */
+ {
+ 0x74b8,0x74c0,0x74b5,0x74c1,0x74be,0x74b6,0x74bb,0x74c2,0x7514,0x7513,
+ 0x765c,0x7664,0x7659,0x7650,0x7653,0x7657,0x765a,0x76a6,0x76bd,0x76ec,
+ 0x77c2,0x77ba,0x78ff,0x790c,0x7913,0x7914,0x7909,0x7910,0x7912,0x7911,
+ 0x79ad,0x79ac,0x7a5f,0x7c1c,0x7c29,0x7c19,0x7c20,0x7c1f,0x7c2d,0x7c1d,
+ 0x7c26,0x7c28,0x7c22,0x7c25,0x7c30,0x7e5c,0x7e50,0x7e56,0x7e63,0x7e58,
+ 0x7e62,0x7e5f,0x7e51,0x7e60,0x7e57,0x7e53,0x7fb5,0x7fb3,0x7ff7,0x7ff8,
+ 0x8075,0x81d1,0x81d2
+ },{
+ 0x81d0,0x825f,0x825e,0x85b4,0x85c6,0x85c0,0x85c3,0x85c2,0x85b3,0x85b5,
+ 0x85bd,0x85c7,0x85c4,0x85bf,0x85cb,0x85ce,0x85c8,0x85c5,0x85b1,0x85b6,
+ 0x85d2,0x8624,0x85b8,0x85b7,0x85be,0x8669,0x87e7,0x87e6,0x87e2,0x87db,
+ 0x87eb,0x87ea,0x87e5,0x87df,0x87f3,0x87e4,0x87d4,0x87dc,0x87d3,0x87ed,
+ 0x87d8,0x87e3,0x87a4,0x87d7,0x87d9,0x8801,0x87f4,0x87e8,0x87dd,0x8953,
+ 0x894b,0x894f,0x894c,0x8946,0x8950,0x8951,0x8949,0x8b2a,0x8b27,0x8b23,
+ 0x8b33,0x8b30,0x8b35,0x8b47,0x8b2f,0x8b3c,0x8b3e,0x8b31,0x8b25,0x8b37,
+ 0x8b26,0x8b36,0x8b2e,0x8b24,0x8b3b,0x8b3d,0x8b3a,0x8c42,0x8c75,0x8c99,
+ 0x8c98,0x8c97,0x8cfe,0x8d04,0x8d02,0x8d00,0x8e5c,0x8e62,0x8e60,0x8e57,
+ 0x8e56,0x8e5e,0x8e65,0x8e67
+ }
+ },
+ { /* ku 51 */
+ {
+ 0x8e5b,0x8e5a,0x8e61,0x8e5d,0x8e69,0x8e54,0x8f46,0x8f47,0x8f48,0x8f4b,
+ 0x9128,0x913a,0x913b,0x913e,0x91a8,0x91a5,0x91a7,0x91af,0x91aa,0x93b5,
+ 0x938c,0x9392,0x93b7,0x939b,0x939d,0x9389,0x93a7,0x938e,0x93aa,0x939e,
+ 0x93a6,0x9395,0x9388,0x9399,0x939f,0x938d,0x93b1,0x9391,0x93b2,0x93a4,
+ 0x93a8,0x93b4,0x93a3,0x93a5,0x95d2,0x95d3,0x95d1,0x96b3,0x96d7,0x96da,
+ 0x5dc2,0x96df,0x96d8,0x96dd,0x9723,0x9722,0x9725,0x97ac,0x97ae,0x97a8,
+ 0x97ab,0x97a4,0x97aa
+ },{
+ 0x97a2,0x97a5,0x97d7,0x97d9,0x97d6,0x97d8,0x97fa,0x9850,0x9851,0x9852,
+ 0x98b8,0x9941,0x993c,0x993a,0x9a0f,0x9a0b,0x9a09,0x9a0d,0x9a04,0x9a11,
+ 0x9a0a,0x9a05,0x9a07,0x9a06,0x9ac0,0x9adc,0x9b08,0x9b04,0x9b05,0x9b29,
+ 0x9b35,0x9b4a,0x9b4c,0x9b4b,0x9bc7,0x9bc6,0x9bc3,0x9bbf,0x9bc1,0x9bb5,
+ 0x9bb8,0x9bd3,0x9bb6,0x9bc4,0x9bb9,0x9bbd,0x9d5c,0x9d53,0x9d4f,0x9d4a,
+ 0x9d5b,0x9d4b,0x9d59,0x9d56,0x9d4c,0x9d57,0x9d52,0x9d54,0x9d5f,0x9d58,
+ 0x9d5a,0x9e8e,0x9e8c,0x9edf,0x9f01,0x9f00,0x9f16,0x9f25,0x9f2b,0x9f2a,
+ 0x9f29,0x9f28,0x9f4c,0x9f55,0x5134,0x5135,0x5296,0x52f7,0x53b4,0x56ab,
+ 0x56ad,0x56a6,0x56a7,0x56aa,0x56ac,0x58da,0x58dd,0x58db,0x5912,0x5b3d,
+ 0x5b3e,0x5b3f,0x5dc3,0x5e70
+ }
+ },
+ { /* ku 52 */
+ {
+ 0x5fbf,0x61fb,0x6507,0x6510,0x650d,0x6509,0x650c,0x650e,0x6584,0x65de,
+ 0x65dd,0x66de,0x6ae7,0x6ae0,0x6acc,0x6ad1,0x6ad9,0x6acb,0x6adf,0x6adc,
+ 0x6ad0,0x6aeb,0x6acf,0x6acd,0x6ade,0x6b60,0x6bb0,0x6c0c,0x7019,0x7027,
+ 0x7020,0x7016,0x702b,0x7021,0x7022,0x7023,0x7029,0x7017,0x7024,0x701c,
+ 0x702a,0x720c,0x720a,0x7207,0x7202,0x7205,0x72a5,0x72a6,0x72a4,0x72a3,
+ 0x72a1,0x74cb,0x74c5,0x74b7,0x74c3,0x7516,0x7660,0x77c9,0x77ca,0x77c4,
+ 0x77f1,0x791d,0x791b
+ },{
+ 0x7921,0x791c,0x7917,0x791e,0x79b0,0x7a67,0x7a68,0x7c33,0x7c3c,0x7c39,
+ 0x7c2c,0x7c3b,0x7cec,0x7cea,0x7e76,0x7e75,0x7e78,0x7e70,0x7e77,0x7e6f,
+ 0x7e7a,0x7e72,0x7e74,0x7e68,0x7f4b,0x7f4a,0x7f83,0x7f86,0x7fb7,0x7ffd,
+ 0x7ffe,0x8078,0x81d7,0x81d5,0x8264,0x8261,0x8263,0x85eb,0x85f1,0x85ed,
+ 0x85d9,0x85e1,0x85e8,0x85da,0x85d7,0x85ec,0x85f2,0x85f8,0x85d8,0x85df,
+ 0x85e3,0x85dc,0x85d1,0x85f0,0x85e6,0x85ef,0x85de,0x85e2,0x8800,0x87fa,
+ 0x8803,0x87f6,0x87f7,0x8809,0x880c,0x880b,0x8806,0x87fc,0x8808,0x87ff,
+ 0x880a,0x8802,0x8962,0x895a,0x895b,0x8957,0x8961,0x895c,0x8958,0x895d,
+ 0x8959,0x8988,0x89b7,0x89b6,0x89f6,0x8b50,0x8b48,0x8b4a,0x8b40,0x8b53,
+ 0x8b56,0x8b54,0x8b4b,0x8b55
+ }
+ },
+ { /* ku 53 */
+ {
+ 0x8b51,0x8b42,0x8b52,0x8b57,0x8c43,0x8c77,0x8c76,0x8c9a,0x8d06,0x8d07,
+ 0x8d09,0x8dac,0x8daa,0x8dad,0x8dab,0x8e6d,0x8e78,0x8e73,0x8e6a,0x8e6f,
+ 0x8e7b,0x8ec2,0x8f52,0x8f51,0x8f4f,0x8f50,0x8f53,0x8fb4,0x9140,0x913f,
+ 0x91b0,0x91ad,0x93de,0x93c7,0x93cf,0x93c2,0x93da,0x93d0,0x93f9,0x93ec,
+ 0x93cc,0x93d9,0x93a9,0x93e6,0x93ca,0x93d4,0x93ee,0x93e3,0x93d5,0x93c4,
+ 0x93ce,0x93c0,0x93d2,0x93e7,0x957d,0x95da,0x95db,0x96e1,0x9729,0x972b,
+ 0x972c,0x9728,0x9726
+ },{
+ 0x97b3,0x97b7,0x97b6,0x97dd,0x97de,0x97df,0x985c,0x9859,0x985d,0x9857,
+ 0x98bf,0x98bd,0x98bb,0x98be,0x9948,0x9947,0x9943,0x99a6,0x99a7,0x9a1a,
+ 0x9a15,0x9a25,0x9a1d,0x9a24,0x9a1b,0x9a22,0x9a20,0x9a27,0x9a23,0x9a1e,
+ 0x9a1c,0x9a14,0x9ac2,0x9b0b,0x9b0a,0x9b0e,0x9b0c,0x9b37,0x9bea,0x9beb,
+ 0x9be0,0x9bde,0x9be4,0x9be6,0x9be2,0x9bf0,0x9bd4,0x9bd7,0x9bec,0x9bdc,
+ 0x9bd9,0x9be5,0x9bd5,0x9be1,0x9bda,0x9d77,0x9d81,0x9d8a,0x9d84,0x9d88,
+ 0x9d71,0x9d80,0x9d78,0x9d86,0x9d8b,0x9d8c,0x9d7d,0x9d6b,0x9d74,0x9d75,
+ 0x9d70,0x9d69,0x9d85,0x9d73,0x9d7b,0x9d82,0x9d6f,0x9d79,0x9d7f,0x9d87,
+ 0x9d68,0x9e94,0x9e91,0x9ec0,0x9efc,0x9f2d,0x9f40,0x9f41,0x9f4d,0x9f56,
+ 0x9f57,0x9f58,0x5337,0x56b2
+ }
+ },
+ { /* ku 54 */
+ {
+ 0x56b5,0x56b3,0x58e3,0x5b45,0x5dc6,0x5dc7,0x5eee,0x5eef,0x5fc0,0x5fc1,
+ 0x61f9,0x6517,0x6516,0x6515,0x6513,0x65df,0x66e8,0x66e3,0x66e4,0x6af3,
+ 0x6af0,0x6aea,0x6ae8,0x6af9,0x6af1,0x6aee,0x6aef,0x703c,0x7035,0x702f,
+ 0x7037,0x7034,0x7031,0x7042,0x7038,0x703f,0x703a,0x7039,0x7040,0x703b,
+ 0x7033,0x7041,0x7213,0x7214,0x72a8,0x737d,0x737c,0x74ba,0x76ab,0x76aa,
+ 0x76be,0x76ed,0x77cc,0x77ce,0x77cf,0x77cd,0x77f2,0x7925,0x7923,0x7927,
+ 0x7928,0x7924,0x7929
+ },{
+ 0x79b2,0x7a6e,0x7a6c,0x7a6d,0x7af7,0x7c49,0x7c48,0x7c4a,0x7c47,0x7c45,
+ 0x7cee,0x7e7b,0x7e7e,0x7e81,0x7e80,0x7fba,0x7fff,0x8079,0x81db,0x81d9,
+ 0x820b,0x8268,0x8269,0x8622,0x85ff,0x8601,0x85fe,0x861b,0x8600,0x85f6,
+ 0x8604,0x8609,0x8605,0x860c,0x85fd,0x8819,0x8810,0x8811,0x8817,0x8813,
+ 0x8816,0x8963,0x8966,0x89b9,0x89f7,0x8b60,0x8b6a,0x8b5d,0x8b68,0x8b63,
+ 0x8b65,0x8b67,0x8b6d,0x8dae,0x8e86,0x8e88,0x8e84,0x8f59,0x8f56,0x8f57,
+ 0x8f55,0x8f58,0x8f5a,0x908d,0x9143,0x9141,0x91b7,0x91b5,0x91b2,0x91b3,
+ 0x940b,0x9413,0x93fb,0x9420,0x940f,0x9414,0x93fe,0x9415,0x9410,0x9428,
+ 0x9419,0x940d,0x93f5,0x9400,0x93f7,0x9407,0x940e,0x9416,0x9412,0x93fa,
+ 0x9409,0x93f8,0x940a,0x93ff
+ }
+ },
+ { /* ku 55 */
+ {
+ 0x93fc,0x940c,0x93f6,0x9411,0x9406,0x95de,0x95e0,0x95df,0x972e,0x972f,
+ 0x97b9,0x97bb,0x97fd,0x97fe,0x9860,0x9862,0x9863,0x985f,0x98c1,0x98c2,
+ 0x9950,0x994e,0x9959,0x994c,0x994b,0x9953,0x9a32,0x9a34,0x9a31,0x9a2c,
+ 0x9a2a,0x9a36,0x9a29,0x9a2e,0x9a38,0x9a2d,0x9ac7,0x9aca,0x9ac6,0x9b10,
+ 0x9b12,0x9b11,0x9c0b,0x9c08,0x9bf7,0x9c05,0x9c12,0x9bf8,0x9c40,0x9c07,
+ 0x9c0e,0x9c06,0x9c17,0x9c14,0x9c09,0x9d9f,0x9d99,0x9da4,0x9d9d,0x9d92,
+ 0x9d98,0x9d90,0x9d9b
+ },{
+ 0x9da0,0x9d94,0x9d9c,0x9daa,0x9d97,0x9da1,0x9d9a,0x9da2,0x9da8,0x9d9e,
+ 0x9da3,0x9dbf,0x9da9,0x9d96,0x9da6,0x9da7,0x9e99,0x9e9b,0x9e9a,0x9ee5,
+ 0x9ee4,0x9ee7,0x9ee6,0x9f30,0x9f2e,0x9f5b,0x9f60,0x9f5e,0x9f5d,0x9f59,
+ 0x9f91,0x513a,0x5139,0x5298,0x5297,0x56c3,0x56bd,0x56be,0x5b48,0x5b47,
+ 0x5dcb,0x5dcf,0x5ef1,0x61fd,0x651b,0x6b02,0x6afc,0x6b03,0x6af8,0x6b00,
+ 0x7043,0x7044,0x704a,0x7048,0x7049,0x7045,0x7046,0x721d,0x721a,0x7219,
+ 0x737e,0x7517,0x766a,0x77d0,0x792d,0x7931,0x792f,0x7c54,0x7c53,0x7cf2,
+ 0x7e8a,0x7e87,0x7e88,0x7e8b,0x7e86,0x7e8d,0x7f4d,0x7fbb,0x8030,0x81dd,
+ 0x8618,0x862a,0x8626,0x861f,0x8623,0x861c,0x8619,0x8627,0x862e,0x8621,
+ 0x8620,0x8629,0x861e,0x8625
+ }
+ },
+ { /* ku 56 */
+ {
+ 0x8829,0x881d,0x881b,0x8820,0x8824,0x881c,0x882b,0x884a,0x896d,0x8969,
+ 0x896e,0x896b,0x89fa,0x8b79,0x8b78,0x8b45,0x8b7a,0x8b7b,0x8d10,0x8d14,
+ 0x8daf,0x8e8e,0x8e8c,0x8f5e,0x8f5b,0x8f5d,0x9146,0x9144,0x9145,0x91b9,
+ 0x943f,0x943b,0x9436,0x9429,0x943d,0x943c,0x9430,0x9439,0x942a,0x9437,
+ 0x942c,0x9440,0x9431,0x95e5,0x95e4,0x95e3,0x9735,0x973a,0x97bf,0x97e1,
+ 0x9864,0x98c9,0x98c6,0x98c0,0x9958,0x9956,0x9a39,0x9a3d,0x9a46,0x9a44,
+ 0x9a42,0x9a41,0x9a3a
+ },{
+ 0x9a3f,0x9acd,0x9b15,0x9b17,0x9b18,0x9b16,0x9b3a,0x9b52,0x9c2b,0x9c1d,
+ 0x9c1c,0x9c2c,0x9c23,0x9c28,0x9c29,0x9c24,0x9c21,0x9db7,0x9db6,0x9dbc,
+ 0x9dc1,0x9dc7,0x9dca,0x9dcf,0x9dbe,0x9dc5,0x9dc3,0x9dbb,0x9db5,0x9dce,
+ 0x9db9,0x9dba,0x9dac,0x9dc8,0x9db1,0x9dad,0x9dcc,0x9db3,0x9dcd,0x9db2,
+ 0x9e7a,0x9e9c,0x9eeb,0x9eee,0x9eed,0x9f1b,0x9f18,0x9f1a,0x9f31,0x9f4e,
+ 0x9f65,0x9f64,0x9f92,0x4eb9,0x56c6,0x56c5,0x56cb,0x5971,0x5b4b,0x5b4c,
+ 0x5dd5,0x5dd1,0x5ef2,0x6521,0x6520,0x6526,0x6522,0x6b0b,0x6b08,0x6b09,
+ 0x6c0d,0x7055,0x7056,0x7057,0x7052,0x721e,0x721f,0x72a9,0x737f,0x74d8,
+ 0x74d5,0x74d9,0x74d7,0x766d,0x76ad,0x7935,0x79b4,0x7a70,0x7a71,0x7c57,
+ 0x7c5c,0x7c59,0x7c5b,0x7c5a
+ }
+ },
+ { /* ku 57 */
+ {
+ 0x7cf4,0x7cf1,0x7e91,0x7f4f,0x7f87,0x81de,0x826b,0x8634,0x8635,0x8633,
+ 0x862c,0x8632,0x8636,0x882c,0x8828,0x8826,0x882a,0x8825,0x8971,0x89bf,
+ 0x89be,0x89fb,0x8b7e,0x8b84,0x8b82,0x8b86,0x8b85,0x8b7f,0x8d15,0x8e95,
+ 0x8e94,0x8e9a,0x8e92,0x8e90,0x8e96,0x8e97,0x8f60,0x8f62,0x9147,0x944c,
+ 0x9450,0x944a,0x944b,0x944f,0x9447,0x9445,0x9448,0x9449,0x9446,0x973f,
+ 0x97e3,0x986a,0x9869,0x98cb,0x9954,0x995b,0x9a4e,0x9a53,0x9a54,0x9a4c,
+ 0x9a4f,0x9a48,0x9a4a
+ },{
+ 0x9a49,0x9a52,0x9a50,0x9ad0,0x9b19,0x9b2b,0x9b3b,0x9b56,0x9b55,0x9c46,
+ 0x9c48,0x9c3f,0x9c44,0x9c39,0x9c33,0x9c41,0x9c3c,0x9c37,0x9c34,0x9c32,
+ 0x9c3d,0x9c36,0x9ddb,0x9dd2,0x9dde,0x9dda,0x9dcb,0x9dd0,0x9ddc,0x9dd1,
+ 0x9ddf,0x9de9,0x9dd9,0x9dd8,0x9dd6,0x9df5,0x9dd5,0x9ddd,0x9eb6,0x9ef0,
+ 0x9f35,0x9f33,0x9f32,0x9f42,0x9f6b,0x9f95,0x9fa2,0x513d,0x5299,0x58e8,
+ 0x58e7,0x5972,0x5b4d,0x5dd8,0x882f,0x5f4f,0x6201,0x6203,0x6204,0x6529,
+ 0x6525,0x6596,0x66eb,0x6b11,0x6b12,0x6b0f,0x6bca,0x705b,0x705a,0x7222,
+ 0x7382,0x7381,0x7383,0x7670,0x77d4,0x7c67,0x7c66,0x7e95,0x826c,0x863a,
+ 0x8640,0x8639,0x863c,0x8631,0x863b,0x863e,0x8830,0x8832,0x882e,0x8833,
+ 0x8976,0x8974,0x8973,0x89fe
+ }
+ },
+ { /* ku 58 */
+ {
+ 0x8b8c,0x8b8e,0x8b8b,0x8b88,0x8c45,0x8d19,0x8e98,0x8f64,0x8f63,0x91bc,
+ 0x9462,0x9455,0x945d,0x9457,0x945e,0x97c4,0x97c5,0x9800,0x9a56,0x9a59,
+ 0x9b1e,0x9b1f,0x9b20,0x9c52,0x9c58,0x9c50,0x9c4a,0x9c4d,0x9c4b,0x9c55,
+ 0x9c59,0x9c4c,0x9c4e,0x9dfb,0x9df7,0x9def,0x9de3,0x9deb,0x9df8,0x9de4,
+ 0x9df6,0x9de1,0x9dee,0x9de6,0x9df2,0x9df0,0x9de2,0x9dec,0x9df4,0x9df3,
+ 0x9de8,0x9ded,0x9ec2,0x9ed0,0x9ef2,0x9ef3,0x9f06,0x9f1c,0x9f38,0x9f37,
+ 0x9f36,0x9f43,0x9f4f
+ },{
+ 0x9f71,0x9f70,0x9f6e,0x9f6f,0x56d3,0x56cd,0x5b4e,0x5c6d,0x652d,0x66ed,
+ 0x66ee,0x6b13,0x705f,0x7061,0x705d,0x7060,0x7223,0x74db,0x74e5,0x77d5,
+ 0x7938,0x79b7,0x79b6,0x7c6a,0x7e97,0x7f89,0x826d,0x8643,0x8838,0x8837,
+ 0x8835,0x884b,0x8b94,0x8b95,0x8e9e,0x8e9f,0x8ea0,0x8e9d,0x91be,0x91bd,
+ 0x91c2,0x946b,0x9468,0x9469,0x96e5,0x9746,0x9743,0x9747,0x97c7,0x97e5,
+ 0x9a5e,0x9ad5,0x9b59,0x9c63,0x9c67,0x9c66,0x9c62,0x9c5e,0x9c60,0x9e02,
+ 0x9dfe,0x9e07,0x9e03,0x9e06,0x9e05,0x9e00,0x9e01,0x9e09,0x9dff,0x9dfd,
+ 0x9e04,0x9ea0,0x9f1e,0x9f46,0x9f74,0x9f75,0x9f76,0x56d4,0x652e,0x65b8,
+ 0x6b18,0x6b19,0x6b17,0x6b1a,0x7062,0x7226,0x72aa,0x77d8,0x77d9,0x7939,
+ 0x7c69,0x7c6b,0x7cf6,0x7e9a
+ }
+ },
+ { /* ku 59 */
+ {
+ 0x7e98,0x7e9b,0x7e99,0x81e0,0x81e1,0x8646,0x8647,0x8648,0x8979,0x897a,
+ 0x897c,0x897b,0x89ff,0x8b98,0x8b99,0x8ea5,0x8ea4,0x8ea3,0x946e,0x946d,
+ 0x946f,0x9471,0x9473,0x9749,0x9872,0x995f,0x9c68,0x9c6e,0x9c6d,0x9e0b,
+ 0x9e0d,0x9e10,0x9e0f,0x9e12,0x9e11,0x9ea1,0x9ef5,0x9f09,0x9f47,0x9f78,
+ 0x9f7b,0x9f7a,0x9f79,0x571e,0x7066,0x7c6f,0x883c,0x8db2,0x8ea6,0x91c3,
+ 0x9474,0x9478,0x9476,0x9475,0x9a60,0x9c74,0x9c73,0x9c71,0x9c75,0x9e14,
+ 0x9e13,0x9ef6,0x9f0a
+ },{
+ 0x9fa4,0x7068,0x7065,0x7cf7,0x866a,0x883e,0x883d,0x883f,0x8b9e,0x8c9c,
+ 0x8ea9,0x8ec9,0x974b,0x9873,0x9874,0x98cc,0x9961,0x99ab,0x9a64,0x9a66,
+ 0x9a67,0x9b24,0x9e15,0x9e17,0x9f48,0x6207,0x6b1e,0x7227,0x864c,0x8ea8,
+ 0x9482,0x9480,0x9481,0x9a69,0x9a68,0x9b2e,0x9e19,0x7229,0x864b,0x8b9f,
+ 0x9483,0x9c79,0x9eb7,0x7675,0x9a6b,0x9c7a,0x9e1d,0x7069,0x706a,0x9ea4,
+ 0x9f7e,0x9f49,0x9f98,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ }
+};
diff --git a/imap/src/charset/cns11643.c b/imap/src/charset/cns11643.c
new file mode 100644
index 00000000..1c209d50
--- /dev/null
+++ b/imap/src/charset/cns11643.c
@@ -0,0 +1,8590 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: CSN 11643 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* CNS 11643 is the national standard of the Republic of China (Taiwan).
+ * Thanks to Emily Hsu of the ROC's Institute for Information Industry for
+ * this data.
+ *
+ * Note: It is assumed that CJK Unified Ideographs Extension A are encoded
+ * in the BMP at U+3400 to U+4DB5.
+ */
+
+#define CNS_EXTENDED 1 /* include extended planes 3-15 */
+#define CNS_OBSOLETE 0 /* include obsolete plane 14 */
+#define CNS_EXTENSION 0 /* include extension plane 15 */
+
+#define BASE_CNS11643_KU 0x21
+#define BASE_CNS11643_TEN 0x21
+#define MAX_CNS11643_KU_1 93
+#define MAX_CNS11643_KU_2 82
+#if CNS_EXTENDED
+#define MAX_CNS11643_KU_3 71
+#define MAX_CNS11643_KU_4 78
+#define MAX_CNS11643_KU_5 92
+#define MAX_CNS11643_KU_6 68
+#define MAX_CNS11643_KU_7 69
+#if CNS_OBSOLETE
+#define MAX_CNS11643_KU_14 71
+#endif
+#if CNS_EXTENSION
+#define MAX_CNS11643_KU_15 77
+#endif
+#endif
+#define MAX_CNS11643_TEN 94
+
+
+#define CNS1TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_1) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_1tab[ku][ten] : UBOGON)
+
+
+#define CNS2TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_2) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_2tab[ku][ten] : UBOGON)
+
+
+#if CNS_EXTENDED
+#define CNS3TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_3) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_3tab[ku][ten] : UBOGON)
+
+
+#define CNS4TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_4) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_4tab[ku][ten] : UBOGON)
+
+
+#define CNS5TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_5) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_5tab[ku][ten] : UBOGON)
+
+
+#define CNS6TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_6) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_6tab[ku][ten] : UBOGON)
+
+
+#define CNS7TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_7) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_7tab[ku][ten] : UBOGON)
+
+
+#if CNS_OBSOLETE
+#define CNS14TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_14) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_14tab[ku][ten] : UBOGON)
+#endif
+
+
+#if CNS_EXTENSION
+#define CNS15TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_15) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_15tab[ku][ten] : UBOGON)
+#endif
+#endif
+
+/* CNS 11643 plane 1 conversion table */
+
+static const unsigned short
+ cns11643_1tab[MAX_CNS11643_KU_1][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x3000,0xff0c,0x3001,0x3002,0xff0e,0x30fb,0xff1b,0xff1a,0xff1f,0xff01,
+ 0xfe30,0x2026,0x2025,0xfe50,0xfe51,0xfe52,0x00b7,0xfe54,0xfe55,0xfe56,
+ 0xfe57,0xfe31,0x2014,0xfe32,0x2013,UBOGON,UBOGON,UBOGON,UBOGON,0xff08,
+ 0xff09,0xfe35,0xfe36,0xff5b,0xff5d,0xfe37,0xfe38,0x3014,0x3015,0xfe39,
+ 0xfe3a,0x3010,0x3011,0xfe3b,0xfe3c,0x300a,0x300b,0xfe3d,0xfe3e,0x3008,
+ 0x3009,0xfe3f,0xfe40,0x300c,0x300d,0xfe41,0xfe42,0x300e,0x300f,0xfe43,
+ 0xfe44,0xfe59,0xfe5a,0xfe5b,0xfe5c,0xfe5d,0xfe5e,0x2018,0x2019,0x201c,
+ 0x201d,0x301d,0x301e,0x2032,0x2035,0xff03,0xff06,0xff0a,0x203b,0x00a7,
+ 0x3003,0x25cb,0x25cf,0x25b3,0x25b2,0x25ce,0x2606,0x2605,0x25c7,0x25c6,
+ 0x25a1,0x25a0,0x25bd,0x25bc
+ },
+ { /* ku 02 */
+ 0x32a3,0x2105,0x203e,UBOGON,0xff3f,UBOGON,0xfe49,0xfe4a,0xfe4d,0xfe4e,
+ 0xfe4b,0xfe4c,0xfe5f,0xfe60,0xfe61,0xff0b,0xff0d,0x00d7,0x00f7,0x00b1,
+ 0x221a,0xff1c,0xff1e,0xff1d,0x2266,0x2267,0x2260,0x221e,0x2252,0x2261,
+ 0xfe62,0xfe63,0xfe64,0xfe66,0xfe65,0x223c,0x2229,0x222a,0x22a5,0x2220,
+ 0x221f,0x22bf,0x33d2,0x33d1,0x222b,0x222e,0x2235,0x2234,0x2640,0x2642,
+ 0x2641,0x2609,0x2191,0x2193,0x2192,0x2190,0x2196,0x2197,0x2199,0x2198,
+ 0x2016,0xff5c,0xff0f,0xff3c,0x2215,0xfe68,0xff04,0xffe5,0x3012,0xffe0,
+ 0xffe1,0xff05,0xff20,0x2103,0x2109,0xfe69,0xfe6a,0xfe6b,0x33d5,0x339c,
+ 0x339d,0x339e,0x33ce,0x33a1,0x338e,0x338f,0x33c4,0x00b0,0x5159,0x515b,
+ 0x515e,0x515d,0x5161,0x5163
+ },
+ { /* ku 03 */
+ 0x55e7,0x74e9,0x7cce,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,
+ 0x2588,0x258f,0x258e,0x258d,0x258c,0x258b,0x258a,0x2589,0x253c,0x2534,
+ 0x252c,0x2524,0x251c,0x2594,0x2500,0x2502,0x2595,0x250c,0x2510,0x2514,
+ 0x2518,0x256d,0x256e,0x2570,0x256f,0x2550,0x255e,0x256a,0x2561,0x25e2,
+ 0x25e3,0x25e5,0x25e4,0x2571,0x2572,0x2573,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,0xff18,0xff19,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ 0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029,UBOGON,
+ 0x5344,UBOGON,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,0xff41,0xff42,
+ 0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,
+ 0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,
+ 0xff57,0xff58,0xff59,0xff5a,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a
+ },
+ { /* ku 05 */
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x3105,0x3106,
+ 0x3107,0x3108,0x3109,0x310a,0x310b,0x310c,0x310d,0x310e,0x310f,0x3110,
+ 0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,0x311a,
+ 0x311b,0x311c,0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,0x3124,
+ 0x3125,0x3126,0x3127,0x3128,0x3129,0x02d9,0x02c9,0x02ca,0x02c7,0x02cb,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,
+ 0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,
+ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ea0,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x51ab,UBOGON,UBOGON,UBOGON,UBOGON,0x52f9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ 0x2400,0x2401,0x2402,0x2403,0x2404,0x2405,0x2406,0x2407,0x2408,0x2409,
+ 0x240a,0x240b,0x240c,0x240d,0x240e,0x240f,0x2410,0x2411,0x2412,0x2413,
+ 0x2414,0x2415,0x2416,0x2417,0x2418,0x2419,0x241a,0x241b,0x241c,0x241d,
+ 0x241e,0x241f,0x2421,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ 0x4e00,0x4e59,0x4e01,0x4e03,0x4e43,0x4e5d,0x4e86,0x4e8c,0x4eba,0x513f,
+ 0x5165,0x516b,0x51e0,0x5200,0x5201,0x529b,0x5315,0x5341,0x535c,0x53c8,
+ 0x4e09,0x4e0b,0x4e08,0x4e0a,0x4e2b,0x4e38,0x51e1,0x4e45,0x4e48,0x4e5f,
+ 0x4e5e,0x4e8e,0x4ea1,0x5140,0x5203,0x52fa,0x5343,0x53c9,0x53e3,0x571f,
+ 0x58eb,0x5915,0x5927,0x5973,0x5b50,0x5b51,0x5b53,0x5bf8,0x5c0f,0x5c22,
+ 0x5c38,0x5c71,0x5ddd,0x5de5,0x5df1,0x5df2,0x5df3,0x5dfe,0x5e72,0x5efe,
+ 0x5f0b,0x5f13,0x624d,0x4e11,0x4e10,0x4e0d,0x4e2d,0x4e30,0x4e39,0x4e4b,
+ 0x5c39,0x4e88,0x4e91,0x4e95,0x4e92,0x4e94,0x4ea2,0x4ec1,0x4ec0,0x4ec3,
+ 0x4ec6,0x4ec7,0x4ecd,0x4eca,0x4ecb,0x4ec4,0x5143,0x5141,0x5167,0x516d,
+ 0x516e,0x516c,0x5197,0x51f6
+ },
+ { /* ku 25 */
+ 0x5206,0x5207,0x5208,0x52fb,0x52fe,0x52ff,0x5316,0x5339,0x5348,0x5347,
+ 0x5345,0x535e,0x5384,0x53cb,0x53ca,0x53cd,0x58ec,0x5929,0x592b,0x592a,
+ 0x592d,0x5b54,0x5c11,0x5c24,0x5c3a,0x5c6f,0x5df4,0x5e7b,0x5eff,0x5f14,
+ 0x5f15,0x5fc3,0x6208,0x6236,0x624b,0x624e,0x652f,0x6587,0x6597,0x65a4,
+ 0x65b9,0x65e5,0x66f0,0x6708,0x6728,0x6b20,0x6b62,0x6b79,0x6bcb,0x6bd4,
+ 0x6bdb,0x6c0f,0x6c34,0x706b,0x722a,0x7236,0x723b,0x7247,0x7259,0x725b,
+ 0x72ac,0x738b,0x4e19,0x4e16,0x4e15,0x4e14,0x4e18,0x4e3b,0x4e4d,0x4e4f,
+ 0x4e4e,0x4ee5,0x4ed8,0x4ed4,0x4ed5,0x4ed6,0x4ed7,0x4ee3,0x4ee4,0x4ed9,
+ 0x4ede,0x5145,0x5144,0x5189,0x518a,0x51ac,0x51f9,0x51fa,0x51f8,0x520a,
+ 0x52a0,0x529f,0x5305,0x5306
+ },
+ { /* ku 26 */
+ 0x5317,0x531d,0x4edf,0x534a,0x5349,0x5361,0x5360,0x536f,0x536e,0x53bb,
+ 0x53ef,0x53e4,0x53f3,0x53ec,0x53ee,0x53e9,0x53e8,0x53fc,0x53f8,0x53f5,
+ 0x53eb,0x53e6,0x53ea,0x53f2,0x53f1,0x53f0,0x53e5,0x53ed,0x53fb,0x56db,
+ 0x56da,0x5916,0x592e,0x5931,0x5974,0x5976,0x5b55,0x5b83,0x5c3c,0x5de8,
+ 0x5de7,0x5de6,0x5e02,0x5e03,0x5e73,0x5e7c,0x5f01,0x5f18,0x5f17,0x5fc5,
+ 0x620a,0x6253,0x6254,0x6252,0x6251,0x65a5,0x65e6,0x672e,0x672c,0x672a,
+ 0x672b,0x672d,0x6b63,0x6bcd,0x6c11,0x6c10,0x6c38,0x6c41,0x6c40,0x6c3e,
+ 0x72af,0x7384,0x7389,0x74dc,0x74e6,0x7518,0x751f,0x7528,0x7529,0x7530,
+ 0x7531,0x7532,0x7533,0x758b,0x767d,0x76ae,0x76bf,0x76ee,0x77db,0x77e2,
+ 0x77f3,0x793a,0x79be,0x7a74
+ },
+ { /* ku 27 */
+ 0x7acb,0x4e1e,0x4e1f,0x4e52,0x4e53,0x4e69,0x4e99,0x4ea4,0x4ea6,0x4ea5,
+ 0x4eff,0x4f09,0x4f19,0x4f0a,0x4f15,0x4f0d,0x4f10,0x4f11,0x4f0f,0x4ef2,
+ 0x4ef6,0x4efb,0x4ef0,0x4ef3,0x4efd,0x4f01,0x4f0b,0x5149,0x5147,0x5146,
+ 0x5148,0x5168,0x5171,0x518d,0x51b0,0x5217,0x5211,0x5212,0x520e,0x5216,
+ 0x52a3,0x5308,0x5321,0x5320,0x5370,0x5371,0x5409,0x540f,0x540c,0x540a,
+ 0x5410,0x5401,0x540b,0x5404,0x5411,0x540d,0x5408,0x5403,0x540e,0x5406,
+ 0x5412,0x56e0,0x56de,0x56dd,0x5733,0x5730,0x5728,0x572d,0x572c,0x572f,
+ 0x5729,0x5919,0x591a,0x5937,0x5938,0x5984,0x5978,0x5983,0x597d,0x5979,
+ 0x5982,0x5981,0x5b57,0x5b58,0x5b87,0x5b88,0x5b85,0x5b89,0x5bfa,0x5c16,
+ 0x5c79,0x5dde,0x5e06,0x5e76
+ },
+ { /* ku 28 */
+ 0x5e74,0x5f0f,0x5f1b,0x5fd9,0x5fd6,0x620e,0x620c,0x620d,0x6210,0x6263,
+ 0x625b,0x6258,0x6536,0x65e9,0x65e8,0x65ec,0x65ed,0x66f2,0x66f3,0x6709,
+ 0x673d,0x6734,0x6731,0x6735,0x6b21,0x6b64,0x6b7b,0x6c16,0x6c5d,0x6c57,
+ 0x6c59,0x6c5f,0x6c60,0x6c50,0x6c55,0x6c61,0x6c5b,0x6c4d,0x6c4e,0x7070,
+ 0x725f,0x725d,0x767e,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f8a,0x7fbd,0x8001,
+ 0x8003,0x800c,0x8012,0x8033,0x807f,0x8089,0x808b,0x808c,0x81e3,0x81ea,
+ 0x81f3,0x81fc,0x820c,0x821b,0x821f,0x826e,0x8272,0x827e,0x866b,0x8840,
+ 0x884c,0x8863,0x897f,0x9621,0x4e32,0x4ea8,0x4f4d,0x4f4f,0x4f47,0x4f57,
+ 0x4f5e,0x4f34,0x4f5b,0x4f55,0x4f30,0x4f50,0x4f51,0x4f3d,0x4f3a,0x4f38,
+ 0x4f43,0x4f54,0x4f3c,0x4f46
+ },
+ { /* ku 29 */
+ 0x4f63,0x4f5c,0x4f60,0x4f2f,0x4f4e,0x4f36,0x4f59,0x4f5d,0x4f48,0x4f5a,
+ 0x514c,0x514b,0x514d,0x5175,0x51b6,0x51b7,0x5225,0x5224,0x5229,0x522a,
+ 0x5228,0x52ab,0x52a9,0x52aa,0x52ac,0x5323,0x5373,0x5375,0x541d,0x542d,
+ 0x541e,0x543e,0x5426,0x544e,0x5427,0x5446,0x5443,0x5433,0x5448,0x5442,
+ 0x541b,0x5429,0x544a,0x5439,0x543b,0x5438,0x542e,0x5435,0x5436,0x5420,
+ 0x543c,0x5440,0x5431,0x542b,0x541f,0x542c,0x56ea,0x56f0,0x56e4,0x56eb,
+ 0x574a,0x5751,0x5740,0x574d,0x5747,0x574e,0x573e,0x5750,0x574f,0x573b,
+ 0x58ef,0x593e,0x599d,0x5992,0x59a8,0x599e,0x59a3,0x5999,0x5996,0x598d,
+ 0x59a4,0x5993,0x598a,0x59a5,0x5b5d,0x5b5c,0x5b5a,0x5b5b,0x5b8c,0x5b8b,
+ 0x5b8f,0x5c2c,0x5c40,0x5c41
+ },
+ { /* ku 2a */
+ 0x5c3f,0x5c3e,0x5c90,0x5c91,0x5c94,0x5c8c,0x5deb,0x5e0c,0x5e8f,0x5e87,
+ 0x5e8a,0x5ef7,0x5f04,0x5f1f,0x5f64,0x5f62,0x5f77,0x5f79,0x5fd8,0x5fcc,
+ 0x5fd7,0x5fcd,0x5ff1,0x5feb,0x5ff8,0x5fea,0x6212,0x6211,0x6284,0x6297,
+ 0x6296,0x6280,0x6276,0x6289,0x626d,0x628a,0x627c,0x627e,0x6279,0x6273,
+ 0x6292,0x626f,0x6298,0x626e,0x6295,0x6293,0x6291,0x6286,0x6539,0x653b,
+ 0x6538,0x65f1,0x66f4,0x675f,0x674e,0x674f,0x6750,0x6751,0x675c,0x6756,
+ 0x675e,0x6749,0x6746,0x6760,0x6753,0x6757,0x6b65,0x6bcf,0x6c42,0x6c5e,
+ 0x6c99,0x6c81,0x6c88,0x6c89,0x6c85,0x6c9b,0x6c6a,0x6c7a,0x6c90,0x6c70,
+ 0x6c8c,0x6c68,0x6c96,0x6c92,0x6c7d,0x6c83,0x6c72,0x6c7e,0x6c74,0x6c86,
+ 0x6c76,0x6c8d,0x6c94,0x6c98
+ },
+ { /* ku 2b */
+ 0x6c82,0x7076,0x707c,0x707d,0x7078,0x7262,0x7261,0x7260,0x72c4,0x72c2,
+ 0x7396,0x752c,0x752b,0x7537,0x7538,0x7682,0x76ef,0x77e3,0x79c1,0x79c0,
+ 0x79bf,0x7a76,0x7cfb,0x7f55,0x8096,0x8093,0x809d,0x8098,0x809b,0x809a,
+ 0x80b2,0x826f,0x8292,0x828b,0x828d,0x898b,0x89d2,0x8a00,0x8c37,0x8c46,
+ 0x8c55,0x8c9d,0x8d64,0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fc2,
+ 0x8fc6,0x8fc5,0x8fc4,0x5de1,0x9091,0x90a2,0x90aa,0x90a6,0x90a3,0x9149,
+ 0x91c6,0x91cc,0x9632,0x962e,0x9631,0x962a,0x962c,0x4e26,0x4e56,0x4e73,
+ 0x4e8b,0x4e9b,0x4e9e,0x4eab,0x4eac,0x4f6f,0x4f9d,0x4f8d,0x4f73,0x4f7f,
+ 0x4f6c,0x4f9b,0x4f8b,0x4f86,0x4f83,0x4f70,0x4f75,0x4f88,0x4f69,0x4f7b,
+ 0x4f96,0x4f7e,0x4f8f,0x4f91
+ },
+ { /* ku 2c */
+ 0x4f7a,0x5154,0x5152,0x5155,0x5169,0x5177,0x5176,0x5178,0x51bd,0x51fd,
+ 0x523b,0x5238,0x5237,0x523a,0x5230,0x522e,0x5236,0x5241,0x52be,0x52bb,
+ 0x5352,0x5354,0x5353,0x5351,0x5366,0x5377,0x5378,0x5379,0x53d6,0x53d4,
+ 0x53d7,0x5473,0x5475,0x5496,0x5478,0x5495,0x5480,0x547b,0x5477,0x5484,
+ 0x5492,0x5486,0x547c,0x5490,0x5471,0x5476,0x548c,0x549a,0x5462,0x5468,
+ 0x548b,0x547d,0x548e,0x56fa,0x5783,0x5777,0x576a,0x5769,0x5761,0x5766,
+ 0x5764,0x577c,0x591c,0x5949,0x5947,0x5948,0x5944,0x5954,0x59be,0x59bb,
+ 0x59d4,0x59b9,0x59ae,0x59d1,0x59c6,0x59d0,0x59cd,0x59cb,0x59d3,0x59ca,
+ 0x59af,0x59b3,0x59d2,0x59c5,0x5b5f,0x5b64,0x5b63,0x5b97,0x5b9a,0x5b98,
+ 0x5b9c,0x5b99,0x5b9b,0x5c1a
+ },
+ { /* ku 2d */
+ 0x5c48,0x5c45,0x5c46,0x5cb7,0x5ca1,0x5cb8,0x5ca9,0x5cab,0x5cb1,0x5cb3,
+ 0x5e18,0x5e1a,0x5e16,0x5e15,0x5e1b,0x5e11,0x5e78,0x5e9a,0x5e97,0x5e9c,
+ 0x5e95,0x5e96,0x5ef6,0x5f26,0x5f27,0x5f29,0x5f80,0x5f81,0x5f7f,0x5f7c,
+ 0x5fdd,0x5fe0,0x5ffd,0x5ff5,0x5fff,0x600f,0x6014,0x602f,0x6035,0x6016,
+ 0x602a,0x6015,0x6021,0x6027,0x6029,0x602b,0x601b,0x6216,0x6215,0x623f,
+ 0x623e,0x6240,0x627f,0x62c9,0x62cc,0x62c4,0x62bf,0x62c2,0x62b9,0x62d2,
+ 0x62db,0x62ab,0x62d3,0x62d4,0x62cb,0x62c8,0x62a8,0x62bd,0x62bc,0x62d0,
+ 0x62d9,0x62c7,0x62cd,0x62b5,0x62da,0x62b1,0x62d8,0x62d6,0x62d7,0x62c6,
+ 0x62ac,0x62ce,0x653e,0x65a7,0x65bc,0x65fa,0x6614,0x6613,0x660c,0x6606,
+ 0x6602,0x660e,0x6600,0x660f
+ },
+ { /* ku 2e */
+ 0x6615,0x660a,0x6607,0x670d,0x670b,0x676d,0x678b,0x6795,0x6771,0x679c,
+ 0x6773,0x6777,0x6787,0x679d,0x6797,0x676f,0x6770,0x677f,0x6789,0x677e,
+ 0x6790,0x6775,0x679a,0x6793,0x677c,0x676a,0x6772,0x6b23,0x6b66,0x6b67,
+ 0x6b7f,0x6c13,0x6c1b,0x6ce3,0x6ce8,0x6cf3,0x6cb1,0x6ccc,0x6ce5,0x6cb3,
+ 0x6cbd,0x6cbe,0x6cbc,0x6ce2,0x6cab,0x6cd5,0x6cd3,0x6cb8,0x6cc4,0x6cb9,
+ 0x6cc1,0x6cae,0x6cd7,0x6cc5,0x6cf1,0x6cbf,0x6cbb,0x6ce1,0x6cdb,0x6cca,
+ 0x6cac,0x6cef,0x6cdc,0x6cd6,0x6ce0,0x7095,0x708e,0x7092,0x708a,0x7099,
+ 0x722c,0x722d,0x7238,0x7248,0x7267,0x7269,0x72c0,0x72ce,0x72d9,0x72d7,
+ 0x72d0,0x73a9,0x73a8,0x739f,0x73ab,0x73a5,0x753d,0x759d,0x7599,0x759a,
+ 0x7684,0x76c2,0x76f2,0x76f4
+ },
+ { /* ku 2f */
+ 0x77e5,0x77fd,0x793e,0x7940,0x7941,0x79c9,0x79c8,0x7a7a,0x7a79,0x7afa,
+ 0x7cfe,0x7f54,0x7f8c,0x7f8b,0x8005,0x80ba,0x80a5,0x80a2,0x80b1,0x80a1,
+ 0x80ab,0x80a9,0x80b4,0x80aa,0x80af,0x81e5,0x81fe,0x820d,0x82b3,0x829d,
+ 0x8299,0x82ad,0x82bd,0x829f,0x82b9,0x82b1,0x82ac,0x82a5,0x82af,0x82b8,
+ 0x82a3,0x82b0,0x82be,0x82b7,0x864e,0x8671,0x521d,0x8868,0x8ecb,0x8fce,
+ 0x8fd4,0x8fd1,0x90b5,0x90b8,0x90b1,0x90b6,0x91c7,0x91d1,0x9577,0x9580,
+ 0x961c,0x9640,0x963f,0x963b,0x9644,0x9642,0x96b9,0x96e8,0x9752,0x975e,
+ 0x4e9f,0x4ead,0x4eae,0x4fe1,0x4fb5,0x4faf,0x4fbf,0x4fe0,0x4fd1,0x4fcf,
+ 0x4fdd,0x4fc3,0x4fb6,0x4fd8,0x4fdf,0x4fca,0x4fd7,0x4fae,0x4fd0,0x4fc4,
+ 0x4fc2,0x4fda,0x4fce,0x4fde
+ },
+ { /* ku 30 */
+ 0x4fb7,0x5157,0x5192,0x5191,0x51a0,0x524e,0x5243,0x524a,0x524d,0x524c,
+ 0x524b,0x5247,0x52c7,0x52c9,0x52c3,0x52c1,0x530d,0x5357,0x537b,0x539a,
+ 0x53db,0x54ac,0x54c0,0x54a8,0x54ce,0x54c9,0x54b8,0x54a6,0x54b3,0x54c7,
+ 0x54c2,0x54bd,0x54aa,0x54c1,0x54c4,0x54c8,0x54af,0x54ab,0x54b1,0x54bb,
+ 0x54a9,0x54a7,0x54bf,0x56ff,0x5782,0x578b,0x57a0,0x57a3,0x57a2,0x57ce,
+ 0x57ae,0x5793,0x5955,0x5951,0x594f,0x594e,0x5950,0x59dc,0x59d8,0x59ff,
+ 0x59e3,0x59e8,0x5a03,0x59e5,0x59ea,0x59da,0x59e6,0x5a01,0x59fb,0x5b69,
+ 0x5ba3,0x5ba6,0x5ba4,0x5ba2,0x5ba5,0x5c01,0x5c4e,0x5c4f,0x5c4d,0x5c4b,
+ 0x5cd9,0x5cd2,0x5df7,0x5e1d,0x5e25,0x5e1f,0x5e7d,0x5ea0,0x5ea6,0x5efa,
+ 0x5f08,0x5f2d,0x5f65,0x5f88
+ },
+ { /* ku 31 */
+ 0x5f85,0x5f8a,0x5f8b,0x5f87,0x5f8c,0x5f89,0x6012,0x601d,0x6020,0x6025,
+ 0x600e,0x6028,0x604d,0x6070,0x6068,0x6062,0x6046,0x6043,0x606c,0x606b,
+ 0x606a,0x6064,0x6241,0x62dc,0x6316,0x6309,0x62fc,0x62ed,0x6301,0x62ee,
+ 0x62fd,0x6307,0x62f1,0x62f7,0x62ef,0x62ec,0x62fe,0x62f4,0x6311,0x6302,
+ 0x653f,0x6545,0x65ab,0x65bd,0x65e2,0x6625,0x662d,0x6620,0x6627,0x662f,
+ 0x661f,0x6628,0x6631,0x6624,0x66f7,0x67ff,0x67d3,0x67f1,0x67d4,0x67d0,
+ 0x67ec,0x67b6,0x67af,0x67f5,0x67e9,0x67ef,0x67c4,0x67d1,0x67b4,0x67da,
+ 0x67e5,0x67b8,0x67cf,0x67de,0x67f3,0x67b0,0x67d9,0x67e2,0x67dd,0x67d2,
+ 0x6b6a,0x6b83,0x6b86,0x6bb5,0x6bd2,0x6bd7,0x6c1f,0x6cc9,0x6d0b,0x6d32,
+ 0x6d2a,0x6d41,0x6d25,0x6d0c
+ },
+ { /* ku 32 */
+ 0x6d31,0x6d1e,0x6d17,0x6d3b,0x6d3d,0x6d3e,0x6d36,0x6d1b,0x6cf5,0x6d39,
+ 0x6d27,0x6d38,0x6d29,0x6d2e,0x6d35,0x6d0e,0x6d2b,0x70ab,0x70ba,0x70b3,
+ 0x70ac,0x70af,0x70ad,0x70b8,0x70ae,0x70a4,0x7230,0x7272,0x726f,0x7274,
+ 0x72e9,0x72e0,0x72e1,0x73b7,0x73ca,0x73bb,0x73b2,0x73cd,0x73c0,0x73b3,
+ 0x751a,0x752d,0x754f,0x754c,0x754e,0x754b,0x75ab,0x75a4,0x75a5,0x75a2,
+ 0x75a3,0x7678,0x7686,0x7687,0x7688,0x76c8,0x76c6,0x76c3,0x76c5,0x7701,
+ 0x76f9,0x76f8,0x7709,0x770b,0x76fe,0x76fc,0x7707,0x77dc,0x7802,0x7814,
+ 0x780c,0x780d,0x7946,0x7949,0x7948,0x7947,0x79b9,0x79ba,0x79d1,0x79d2,
+ 0x79cb,0x7a7f,0x7a81,0x7aff,0x7afd,0x7c7d,0x7d02,0x7d05,0x7d00,0x7d09,
+ 0x7d07,0x7d04,0x7d06,0x7f38
+ },
+ { /* ku 33 */
+ 0x7f8e,0x7fbf,0x8010,0x800d,0x8011,0x8036,0x80d6,0x80e5,0x80da,0x80c3,
+ 0x80c4,0x80cc,0x80e1,0x80db,0x80ce,0x80de,0x80e4,0x80dd,0x81f4,0x8222,
+ 0x82e7,0x8303,0x8305,0x82e3,0x82db,0x82e6,0x8304,0x82e5,0x8302,0x8309,
+ 0x82d2,0x82d7,0x82f1,0x8301,0x82dc,0x82d4,0x82d1,0x82de,0x82d3,0x82df,
+ 0x82ef,0x8306,0x8650,0x8679,0x867b,0x867a,0x884d,0x886b,0x8981,0x89d4,
+ 0x8a08,0x8a02,0x8a03,0x8c9e,0x8ca0,0x8d74,0x8d73,0x8db4,0x8ecd,0x8ecc,
+ 0x8ff0,0x8fe6,0x8fe2,0x8fea,0x8fe5,0x8fed,0x8feb,0x8fe4,0x8fe8,0x90ca,
+ 0x90ce,0x90c1,0x90c3,0x914b,0x914a,0x91cd,0x9582,0x9650,0x964b,0x964c,
+ 0x964d,0x9762,0x9769,0x97cb,0x97ed,0x97f3,0x9801,0x98a8,0x98db,0x98df,
+ 0x9996,0x9999,0x4e58,0x4eb3
+ },
+ { /* ku 34 */
+ 0x500c,0x500d,0x5023,0x4fef,0x5026,0x5025,0x4ff8,0x5029,0x5016,0x5006,
+ 0x503c,0x501f,0x501a,0x5012,0x5011,0x4ffa,0x5000,0x5014,0x5028,0x4ff1,
+ 0x5021,0x500b,0x5019,0x5018,0x4ff3,0x4fee,0x502d,0x502a,0x4ffe,0x502b,
+ 0x5009,0x517c,0x51a4,0x51a5,0x51a2,0x51cd,0x51cc,0x51c6,0x51cb,0x5256,
+ 0x525c,0x5254,0x525b,0x525d,0x532a,0x537f,0x539f,0x539d,0x53df,0x54e8,
+ 0x5510,0x5501,0x5537,0x54fc,0x54e5,0x54f2,0x5506,0x54fa,0x5514,0x54e9,
+ 0x54ed,0x54e1,0x5509,0x54ee,0x54ea,0x54e6,0x5527,0x5507,0x54fd,0x550f,
+ 0x5703,0x5704,0x57c2,0x57d4,0x57cb,0x57c3,0x5809,0x590f,0x5957,0x5958,
+ 0x595a,0x5a11,0x5a18,0x5a1c,0x5a1f,0x5a1b,0x5a13,0x59ec,0x5a20,0x5a23,
+ 0x5a29,0x5a25,0x5a0c,0x5a09
+ },
+ { /* ku 35 */
+ 0x5b6b,0x5c58,0x5bb0,0x5bb3,0x5bb6,0x5bb4,0x5bae,0x5bb5,0x5bb9,0x5bb8,
+ 0x5c04,0x5c51,0x5c55,0x5c50,0x5ced,0x5cfd,0x5cfb,0x5cea,0x5ce8,0x5cf0,
+ 0x5cf6,0x5d01,0x5cf4,0x5dee,0x5e2d,0x5e2b,0x5eab,0x5ead,0x5ea7,0x5f31,
+ 0x5f92,0x5f91,0x5f90,0x6059,0x6063,0x6065,0x6050,0x6055,0x606d,0x6069,
+ 0x606f,0x6084,0x609f,0x609a,0x608d,0x6094,0x608c,0x6085,0x6096,0x6247,
+ 0x62f3,0x6308,0x62ff,0x634e,0x633e,0x632f,0x6355,0x6342,0x6346,0x634f,
+ 0x6349,0x633a,0x6350,0x633d,0x632a,0x632b,0x6328,0x634d,0x634c,0x6548,
+ 0x6549,0x6599,0x65c1,0x65c5,0x6642,0x6649,0x664f,0x6643,0x6652,0x664c,
+ 0x6645,0x6641,0x66f8,0x6714,0x6715,0x6717,0x6821,0x6838,0x6848,0x6846,
+ 0x6853,0x6839,0x6842,0x6854
+ },
+ { /* ku 36 */
+ 0x6829,0x68b3,0x6817,0x684c,0x6851,0x683d,0x67f4,0x6850,0x6840,0x683c,
+ 0x6843,0x682a,0x6845,0x6813,0x6818,0x6841,0x6b8a,0x6b89,0x6bb7,0x6c23,
+ 0x6c27,0x6c28,0x6c26,0x6c24,0x6cf0,0x6d6a,0x6d95,0x6d88,0x6d87,0x6d66,
+ 0x6d78,0x6d77,0x6d59,0x6d93,0x6d6c,0x6d89,0x6d6e,0x6d5a,0x6d74,0x6d69,
+ 0x6d8c,0x6d8a,0x6d79,0x6d85,0x6d65,0x6d94,0x70ca,0x70d8,0x70e4,0x70d9,
+ 0x70c8,0x70cf,0x7239,0x7279,0x72fc,0x72f9,0x72fd,0x72f8,0x72f7,0x7386,
+ 0x73ed,0x7409,0x73ee,0x73e0,0x73ea,0x73de,0x7554,0x755d,0x755c,0x755a,
+ 0x7559,0x75be,0x75c5,0x75c7,0x75b2,0x75b3,0x75bd,0x75bc,0x75b9,0x75c2,
+ 0x75b8,0x768b,0x76b0,0x76ca,0x76cd,0x76ce,0x7729,0x771f,0x7720,0x7728,
+ 0x77e9,0x7830,0x7827,0x7838
+ },
+ { /* ku 37 */
+ 0x781d,0x7834,0x7837,0x7825,0x782d,0x7820,0x781f,0x7832,0x7955,0x7950,
+ 0x7960,0x795f,0x7956,0x795e,0x795d,0x7957,0x795a,0x79e4,0x79e3,0x79e7,
+ 0x79df,0x79e6,0x79e9,0x79d8,0x7a84,0x7a88,0x7ad9,0x7b06,0x7b11,0x7c89,
+ 0x7d21,0x7d17,0x7d0b,0x7d0a,0x7d20,0x7d22,0x7d14,0x7d10,0x7d15,0x7d1a,
+ 0x7d1c,0x7d0d,0x7d19,0x7d1b,0x7f3a,0x7f5f,0x7f94,0x7fc5,0x7fc1,0x8006,
+ 0x8004,0x8018,0x8015,0x8019,0x8017,0x803d,0x803f,0x80f1,0x8102,0x80f0,
+ 0x8105,0x80ed,0x80f4,0x8106,0x80f8,0x80f3,0x8108,0x80fd,0x810a,0x80fc,
+ 0x80ef,0x81ed,0x81ec,0x8200,0x8210,0x822a,0x822b,0x8228,0x822c,0x82bb,
+ 0x832b,0x8352,0x8354,0x834a,0x8338,0x8350,0x8349,0x8335,0x8334,0x834f,
+ 0x8332,0x8339,0x8336,0x8317
+ },
+ { /* ku 38 */
+ 0x8340,0x8331,0x8328,0x8343,0x8654,0x868a,0x86aa,0x8693,0x86a4,0x86a9,
+ 0x868c,0x86a3,0x869c,0x8870,0x8877,0x8881,0x8882,0x887d,0x8879,0x8a18,
+ 0x8a10,0x8a0e,0x8a0c,0x8a15,0x8a0a,0x8a17,0x8a13,0x8a16,0x8a0f,0x8a11,
+ 0x8c48,0x8c7a,0x8c79,0x8ca1,0x8ca2,0x8d77,0x8eac,0x8ed2,0x8ed4,0x8ecf,
+ 0x8fb1,0x9001,0x9006,0x8ff7,0x9000,0x8ffa,0x8ff4,0x9003,0x8ffd,0x9005,
+ 0x8ff8,0x9095,0x90e1,0x90dd,0x90e2,0x9152,0x914d,0x914c,0x91d8,0x91dd,
+ 0x91d7,0x91dc,0x91d9,0x9583,0x9662,0x9663,0x9661,0x965b,0x965d,0x9664,
+ 0x9658,0x965e,0x96bb,0x98e2,0x99ac,0x9aa8,0x9ad8,0x9b25,0x9b32,0x9b3c,
+ 0x4e7e,0x507a,0x507d,0x505c,0x5047,0x5043,0x504c,0x505a,0x5049,0x5065,
+ 0x5076,0x504e,0x5055,0x5075
+ },
+ { /* ku 39 */
+ 0x5074,0x5077,0x504f,0x500f,0x506f,0x506d,0x515c,0x5195,0x51f0,0x526a,
+ 0x526f,0x52d2,0x52d9,0x52d8,0x52d5,0x5310,0x530f,0x5319,0x533f,0x5340,
+ 0x533e,0x53c3,0x66fc,0x5546,0x556a,0x5566,0x5544,0x555e,0x5561,0x5543,
+ 0x554a,0x5531,0x5556,0x554f,0x5555,0x552f,0x5564,0x5538,0x552e,0x555c,
+ 0x552c,0x5563,0x5533,0x5541,0x5557,0x5708,0x570b,0x5709,0x57df,0x5805,
+ 0x580a,0x5806,0x57e0,0x57e4,0x57fa,0x5802,0x5835,0x57f7,0x57f9,0x5920,
+ 0x5962,0x5a36,0x5a41,0x5a49,0x5a66,0x5a6a,0x5a40,0x5a3c,0x5a62,0x5a5a,
+ 0x5a46,0x5a4a,0x5b70,0x5bc7,0x5bc5,0x5bc4,0x5bc2,0x5bbf,0x5bc6,0x5c09,
+ 0x5c08,0x5c07,0x5c60,0x5c5c,0x5c5d,0x5d07,0x5d06,0x5d0e,0x5d1b,0x5d16,
+ 0x5d22,0x5d11,0x5d29,0x5d14
+ },
+ { /* ku 3a */
+ 0x5d19,0x5d24,0x5d27,0x5d17,0x5de2,0x5e38,0x5e36,0x5e33,0x5e37,0x5eb7,
+ 0x5eb8,0x5eb6,0x5eb5,0x5ebe,0x5f35,0x5f37,0x5f57,0x5f6c,0x5f69,0x5f6b,
+ 0x5f97,0x5f99,0x5f9e,0x5f98,0x5fa1,0x5fa0,0x5f9c,0x607f,0x60a3,0x6089,
+ 0x60a0,0x60a8,0x60cb,0x60b4,0x60e6,0x60bd,0x60c5,0x60bb,0x60b5,0x60dc,
+ 0x60bc,0x60d8,0x60d5,0x60c6,0x60df,0x60b8,0x60da,0x60c7,0x621a,0x621b,
+ 0x6248,0x63a0,0x63a7,0x6372,0x6396,0x63a2,0x63a5,0x6377,0x6367,0x6398,
+ 0x63aa,0x6371,0x63a9,0x6389,0x6383,0x639b,0x636b,0x63a8,0x6384,0x6388,
+ 0x6399,0x63a1,0x63ac,0x6392,0x638f,0x6380,0x637b,0x6369,0x6368,0x637a,
+ 0x655d,0x6556,0x6551,0x6559,0x6557,0x555f,0x654f,0x6558,0x6555,0x6554,
+ 0x659c,0x659b,0x65ac,0x65cf
+ },
+ { /* ku 3b */
+ 0x65cb,0x65cc,0x65ce,0x665d,0x665a,0x6664,0x6668,0x6666,0x665e,0x66f9,
+ 0x52d7,0x671b,0x6881,0x68af,0x68a2,0x6893,0x68b5,0x687f,0x6876,0x68b1,
+ 0x68a7,0x6897,0x68b0,0x6883,0x68c4,0x68ad,0x6886,0x6885,0x6894,0x689d,
+ 0x68a8,0x689f,0x68a1,0x6882,0x6b32,0x6bba,0x6beb,0x6bec,0x6c2b,0x6d8e,
+ 0x6dbc,0x6df3,0x6dd9,0x6db2,0x6de1,0x6dcc,0x6de4,0x6dfb,0x6dfa,0x6e05,
+ 0x6dc7,0x6dcb,0x6daf,0x6dd1,0x6dae,0x6dde,0x6df9,0x6db8,0x6df7,0x6df5,
+ 0x6dc5,0x6dd2,0x6e1a,0x6db5,0x6dda,0x6deb,0x6dd8,0x6dea,0x6df1,0x6dee,
+ 0x6de8,0x6dc6,0x6dc4,0x6daa,0x6dec,0x6dbf,0x6de6,0x70f9,0x7109,0x710a,
+ 0x70fd,0x70ef,0x723d,0x727d,0x7281,0x731c,0x731b,0x7316,0x7313,0x7319,
+ 0x7387,0x7405,0x740a,0x7403
+ },
+ { /* ku 3c */
+ 0x7406,0x73fe,0x740d,0x74e0,0x74f6,0x74f7,0x751c,0x7522,0x7565,0x7566,
+ 0x7562,0x7570,0x758f,0x75d4,0x75d5,0x75b5,0x75ca,0x75cd,0x768e,0x76d4,
+ 0x76d2,0x76db,0x7737,0x773e,0x773c,0x7736,0x7738,0x773a,0x786b,0x7843,
+ 0x784e,0x7965,0x7968,0x796d,0x79fb,0x7a92,0x7a95,0x7b20,0x7b28,0x7b1b,
+ 0x7b2c,0x7b26,0x7b19,0x7b1e,0x7b2e,0x7c92,0x7c97,0x7c95,0x7d46,0x7d43,
+ 0x7d71,0x7d2e,0x7d39,0x7d3c,0x7d40,0x7d30,0x7d33,0x7d44,0x7d2f,0x7d42,
+ 0x7d32,0x7d31,0x7f3d,0x7f9e,0x7f9a,0x7fcc,0x7fce,0x7fd2,0x801c,0x804a,
+ 0x8046,0x812f,0x8116,0x8123,0x812b,0x8129,0x8130,0x8124,0x8202,0x8235,
+ 0x8237,0x8236,0x8239,0x838e,0x839e,0x8398,0x8378,0x83a2,0x8396,0x83bd,
+ 0x83ab,0x8392,0x838a,0x8393
+ },
+ { /* ku 3d */
+ 0x8389,0x83a0,0x8377,0x837b,0x837c,0x8386,0x83a7,0x8655,0x5f6a,0x86c7,
+ 0x86c0,0x86b6,0x86c4,0x86b5,0x86c6,0x86cb,0x86b1,0x86af,0x86c9,0x8853,
+ 0x889e,0x8888,0x88ab,0x8892,0x8896,0x888d,0x888b,0x8993,0x898f,0x8a2a,
+ 0x8a1d,0x8a23,0x8a25,0x8a31,0x8a2d,0x8a1f,0x8a1b,0x8a22,0x8c49,0x8c5a,
+ 0x8ca9,0x8cac,0x8cab,0x8ca8,0x8caa,0x8ca7,0x8d67,0x8d66,0x8dbe,0x8dba,
+ 0x8edb,0x8edf,0x9019,0x900d,0x901a,0x9017,0x9023,0x901f,0x901d,0x9010,
+ 0x9015,0x901e,0x9020,0x900f,0x9022,0x9016,0x901b,0x9014,0x90e8,0x90ed,
+ 0x90fd,0x9157,0x91ce,0x91f5,0x91e6,0x91e3,0x91e7,0x91ed,0x91e9,0x9589,
+ 0x966a,0x9675,0x9673,0x9678,0x9670,0x9674,0x9676,0x9677,0x966c,0x96c0,
+ 0x96ea,0x96e9,0x7ae0,0x7adf
+ },
+ { /* ku 3e */
+ 0x9802,0x9803,0x9b5a,0x9ce5,0x9e75,0x9e7f,0x9ea5,0x9ebb,0x50a2,0x508d,
+ 0x5085,0x5099,0x5091,0x5080,0x5096,0x5098,0x509a,0x6700,0x51f1,0x5272,
+ 0x5274,0x5275,0x5269,0x52de,0x52dd,0x52db,0x535a,0x53a5,0x557b,0x5580,
+ 0x55a7,0x557c,0x558a,0x559d,0x5598,0x5582,0x559c,0x55aa,0x5594,0x5587,
+ 0x558b,0x5583,0x55b3,0x55ae,0x559f,0x553e,0x55b2,0x559a,0x55bb,0x55ac,
+ 0x55b1,0x557e,0x5589,0x55ab,0x5599,0x570d,0x582f,0x582a,0x5834,0x5824,
+ 0x5830,0x5831,0x5821,0x581d,0x5820,0x58f9,0x58fa,0x5960,0x5a77,0x5a9a,
+ 0x5a7f,0x5a92,0x5a9b,0x5aa7,0x5b73,0x5b71,0x5bd2,0x5bcc,0x5bd3,0x5bd0,
+ 0x5c0a,0x5c0b,0x5c31,0x5d4c,0x5d50,0x5d34,0x5d47,0x5dfd,0x5e45,0x5e3d,
+ 0x5e40,0x5e43,0x5e7e,0x5eca
+ },
+ { /* ku 3f */
+ 0x5ec1,0x5ec2,0x5ec4,0x5f3c,0x5f6d,0x5fa9,0x5faa,0x5fa8,0x60d1,0x60e1,
+ 0x60b2,0x60b6,0x60e0,0x611c,0x6123,0x60fa,0x6115,0x60f0,0x60fb,0x60f4,
+ 0x6168,0x60f1,0x610e,0x60f6,0x6109,0x6100,0x6112,0x621f,0x6249,0x63a3,
+ 0x638c,0x63cf,0x63c0,0x63e9,0x63c9,0x63c6,0x63cd,0x63d2,0x63e3,0x63d0,
+ 0x63e1,0x63d6,0x63ed,0x63ee,0x6376,0x63f4,0x63ea,0x63db,0x6452,0x63da,
+ 0x63f9,0x655e,0x6566,0x6562,0x6563,0x6591,0x6590,0x65af,0x666e,0x6670,
+ 0x6674,0x6676,0x666f,0x6691,0x667a,0x667e,0x6677,0x66fe,0x66ff,0x671f,
+ 0x671d,0x68fa,0x68d5,0x68e0,0x68d8,0x68d7,0x6905,0x68df,0x68f5,0x68ee,
+ 0x68e7,0x68f9,0x68d2,0x68f2,0x68e3,0x68cb,0x68cd,0x690d,0x6912,0x690e,
+ 0x68c9,0x68da,0x696e,0x68fb
+ },
+ { /* ku 40 */
+ 0x6b3e,0x6b3a,0x6b3d,0x6b98,0x6b96,0x6bbc,0x6bef,0x6c2e,0x6c2f,0x6c2c,
+ 0x6e2f,0x6e38,0x6e54,0x6e21,0x6e32,0x6e67,0x6e4a,0x6e20,0x6e25,0x6e23,
+ 0x6e1b,0x6e5b,0x6e58,0x6e24,0x6e56,0x6e6e,0x6e2d,0x6e26,0x6e6f,0x6e34,
+ 0x6e4d,0x6e3a,0x6e2c,0x6e43,0x6e1d,0x6e3e,0x6ecb,0x6e89,0x6e19,0x6e4e,
+ 0x6e63,0x6e44,0x6e72,0x6e69,0x6e5f,0x7119,0x711a,0x7126,0x7130,0x7121,
+ 0x7136,0x716e,0x711c,0x724c,0x7284,0x7280,0x7336,0x7325,0x7334,0x7329,
+ 0x743a,0x742a,0x7433,0x7422,0x7425,0x7435,0x7436,0x7434,0x742f,0x741b,
+ 0x7426,0x7428,0x7525,0x7526,0x756b,0x756a,0x75e2,0x75db,0x75e3,0x75d9,
+ 0x75d8,0x75de,0x75e0,0x767b,0x767c,0x7696,0x7693,0x76b4,0x76dc,0x774f,
+ 0x77ed,0x785d,0x786c,0x786f
+ },
+ { /* ku 41 */
+ 0x7a0d,0x7a08,0x7a0b,0x7a05,0x7a00,0x7a98,0x7a97,0x7a96,0x7ae5,0x7ae3,
+ 0x7b49,0x7b56,0x7b46,0x7b50,0x7b52,0x7b54,0x7b4d,0x7b4b,0x7b4f,0x7b51,
+ 0x7c9f,0x7ca5,0x7d5e,0x7d50,0x7d68,0x7d55,0x7d2b,0x7d6e,0x7d72,0x7d61,
+ 0x7d66,0x7d62,0x7d70,0x7d73,0x5584,0x7fd4,0x7fd5,0x800b,0x8052,0x8085,
+ 0x8155,0x8154,0x814b,0x8151,0x814e,0x8139,0x8146,0x813e,0x814c,0x8153,
+ 0x8174,0x8212,0x821c,0x83e9,0x8403,0x83f8,0x840d,0x83e0,0x83c5,0x840b,
+ 0x83c1,0x83ef,0x83f1,0x83f4,0x8457,0x840a,0x83f0,0x840c,0x83cc,0x83fd,
+ 0x83f2,0x83ca,0x8438,0x840e,0x8404,0x83dc,0x8407,0x83d4,0x83df,0x865b,
+ 0x86df,0x86d9,0x86ed,0x86d4,0x86db,0x86e4,0x86d0,0x86de,0x8857,0x88c1,
+ 0x88c2,0x88b1,0x8983,0x8996
+ },
+ { /* ku 42 */
+ 0x8a3b,0x8a60,0x8a55,0x8a5e,0x8a3c,0x8a41,0x8a54,0x8a5b,0x8a50,0x8a46,
+ 0x8a34,0x8a3a,0x8a36,0x8a56,0x8c61,0x8c82,0x8caf,0x8cbc,0x8cb3,0x8cbd,
+ 0x8cc1,0x8cbb,0x8cc0,0x8cb4,0x8cb7,0x8cb6,0x8cbf,0x8cb8,0x8d8a,0x8d85,
+ 0x8d81,0x8dce,0x8ddd,0x8dcb,0x8dda,0x8dd1,0x8dcc,0x8ddb,0x8dc6,0x8efb,
+ 0x8ef8,0x8efc,0x8f9c,0x902e,0x9035,0x9031,0x9038,0x9032,0x9036,0x9102,
+ 0x90f5,0x9109,0x90fe,0x9163,0x9165,0x91cf,0x9214,0x9215,0x9223,0x9209,
+ 0x921e,0x920d,0x9210,0x9207,0x9211,0x9594,0x958f,0x958b,0x9591,0x9593,
+ 0x9592,0x958e,0x968a,0x968e,0x968b,0x967d,0x9685,0x9686,0x968d,0x9672,
+ 0x9684,0x96c1,0x96c5,0x96c4,0x96c6,0x96c7,0x96ef,0x96f2,0x97cc,0x9805,
+ 0x9806,0x9808,0x98e7,0x98ea
+ },
+ { /* ku 43 */
+ 0x98ef,0x98e9,0x98f2,0x98ed,0x99ae,0x99ad,0x9ec3,0x9ecd,0x9ed1,0x4e82,
+ 0x50ad,0x50b5,0x50b2,0x50b3,0x50c5,0x50be,0x50ac,0x50b7,0x50bb,0x50af,
+ 0x50c7,0x527f,0x5277,0x527d,0x52df,0x52e6,0x52e4,0x52e2,0x52e3,0x532f,
+ 0x55df,0x55e8,0x55d3,0x55e6,0x55ce,0x55dc,0x55c7,0x55d1,0x55e3,0x55e4,
+ 0x55ef,0x55da,0x55e1,0x55c5,0x55c6,0x55e5,0x55c9,0x5712,0x5713,0x585e,
+ 0x5851,0x5858,0x5857,0x585a,0x5854,0x586b,0x584c,0x586d,0x584a,0x5862,
+ 0x5852,0x584b,0x5967,0x5ac1,0x5ac9,0x5acc,0x5abe,0x5abd,0x5abc,0x5ab3,
+ 0x5ac2,0x5ab2,0x5d69,0x5d6f,0x5e4c,0x5e79,0x5ec9,0x5ec8,0x5f12,0x5f59,
+ 0x5fac,0x5fae,0x611a,0x610f,0x6148,0x611f,0x60f3,0x611b,0x60f9,0x6101,
+ 0x6108,0x614e,0x614c,0x6144
+ },
+ { /* ku 44 */
+ 0x614d,0x613e,0x6134,0x6127,0x610d,0x6106,0x6137,0x6221,0x6222,0x6413,
+ 0x643e,0x641e,0x642a,0x642d,0x643d,0x642c,0x640f,0x641c,0x6414,0x640d,
+ 0x6436,0x6416,0x6417,0x6406,0x656c,0x659f,0x65b0,0x6697,0x6689,0x6687,
+ 0x6688,0x6696,0x6684,0x6698,0x668d,0x6703,0x6994,0x696d,0x695a,0x6977,
+ 0x6960,0x6954,0x6975,0x6930,0x6982,0x694a,0x6968,0x696b,0x695e,0x6953,
+ 0x6979,0x6986,0x695d,0x6963,0x695b,0x6b47,0x6b72,0x6bc0,0x6bbf,0x6bd3,
+ 0x6bfd,0x6ea2,0x6eaf,0x6ed3,0x6eb6,0x6ec2,0x6e90,0x6e9d,0x6ec7,0x6ec5,
+ 0x6ea5,0x6e98,0x6ebc,0x6eba,0x6eab,0x6ed1,0x6e96,0x6e9c,0x6ec4,0x6ed4,
+ 0x6eaa,0x6ea7,0x6eb4,0x714e,0x7159,0x7169,0x7164,0x7149,0x7167,0x715c,
+ 0x716c,0x7166,0x714c,0x7165
+ },
+ { /* ku 45 */
+ 0x715e,0x7146,0x7168,0x7156,0x723a,0x7252,0x7337,0x7345,0x733f,0x733e,
+ 0x746f,0x745a,0x7455,0x745f,0x745e,0x7441,0x743f,0x7459,0x745b,0x745c,
+ 0x7576,0x7578,0x7600,0x75f0,0x7601,0x75f2,0x75f1,0x75fa,0x75ff,0x75f4,
+ 0x75f3,0x76de,0x76df,0x775b,0x776b,0x7766,0x775e,0x7763,0x7779,0x776a,
+ 0x776c,0x775c,0x7765,0x7768,0x7762,0x77ee,0x788e,0x78b0,0x7897,0x7898,
+ 0x788c,0x7889,0x787c,0x7891,0x7893,0x787f,0x797a,0x797f,0x7981,0x842c,
+ 0x79bd,0x7a1c,0x7a1a,0x7a20,0x7a14,0x7a1f,0x7a1e,0x7a9f,0x7aa0,0x7b77,
+ 0x7bc0,0x7b60,0x7b6e,0x7b67,0x7cb1,0x7cb3,0x7cb5,0x7d93,0x7d79,0x7d91,
+ 0x7d81,0x7d8f,0x7d5b,0x7f6e,0x7f69,0x7f6a,0x7f72,0x7fa9,0x7fa8,0x7fa4,
+ 0x8056,0x8058,0x8086,0x8084
+ },
+ { /* ku 46 */
+ 0x8171,0x8170,0x8178,0x8165,0x816e,0x8173,0x816b,0x8179,0x817a,0x8166,
+ 0x8205,0x8247,0x8482,0x8477,0x843d,0x8431,0x8475,0x8466,0x846b,0x8449,
+ 0x846c,0x845b,0x843c,0x8435,0x8461,0x8463,0x8469,0x846d,0x8446,0x865e,
+ 0x865c,0x865f,0x86f9,0x8713,0x8708,0x8707,0x8700,0x86fe,0x86fb,0x8702,
+ 0x8703,0x8706,0x870a,0x8859,0x88df,0x88d4,0x88d9,0x88dc,0x88d8,0x88dd,
+ 0x88e1,0x88ca,0x88d5,0x88d2,0x899c,0x89e3,0x8a6b,0x8a72,0x8a73,0x8a66,
+ 0x8a69,0x8a70,0x8a87,0x8a7c,0x8a63,0x8aa0,0x8a71,0x8a85,0x8a6d,0x8a62,
+ 0x8a6e,0x8a6c,0x8a79,0x8a7b,0x8a3e,0x8a68,0x8c62,0x8c8a,0x8c89,0x8cca,
+ 0x8cc7,0x8cc8,0x8cc4,0x8cb2,0x8cc3,0x8cc2,0x8cc5,0x8de1,0x8ddf,0x8de8,
+ 0x8def,0x8df3,0x8dfa,0x8dea
+ },
+ { /* ku 47 */
+ 0x8de4,0x8de6,0x8eb2,0x8f03,0x8f09,0x8efe,0x8f0a,0x8f9f,0x8fb2,0x904b,
+ 0x904a,0x9053,0x9042,0x9054,0x903c,0x9055,0x9050,0x9047,0x904f,0x904e,
+ 0x904d,0x9051,0x903e,0x9041,0x9112,0x9117,0x916c,0x916a,0x9169,0x91c9,
+ 0x9237,0x9257,0x9238,0x923d,0x9240,0x923e,0x925b,0x924b,0x9264,0x9251,
+ 0x9234,0x9249,0x924d,0x9245,0x9239,0x923f,0x925a,0x9598,0x9698,0x9694,
+ 0x9695,0x96cd,0x96cb,0x96c9,0x96ca,0x96f7,0x96fb,0x96f9,0x96f6,0x9756,
+ 0x9774,0x9776,0x9810,0x9811,0x9813,0x980a,0x9812,0x980c,0x98fc,0x98f4,
+ 0x98fd,0x98fe,0x99b3,0x99b1,0x99b4,0x9ae1,0x9ce9,0x9e82,0x9f0e,0x9f13,
+ 0x9f20,0x50e7,0x50ee,0x50e5,0x50d6,0x50ed,0x50da,0x50d5,0x50cf,0x50d1,
+ 0x50f1,0x50ce,0x50e9,0x5162
+ },
+ { /* ku 48 */
+ 0x51f3,0x5283,0x5282,0x5331,0x53ad,0x55fe,0x5600,0x561b,0x5617,0x55fd,
+ 0x5614,0x5606,0x5609,0x560d,0x560e,0x55f7,0x5616,0x561f,0x5608,0x5610,
+ 0x55f6,0x5718,0x5716,0x5875,0x587e,0x5883,0x5893,0x588a,0x5879,0x5885,
+ 0x587d,0x58fd,0x5925,0x5922,0x5924,0x596a,0x5969,0x5ae1,0x5ae6,0x5ae9,
+ 0x5ad7,0x5ad6,0x5ad8,0x5ae3,0x5b75,0x5bde,0x5be7,0x5be1,0x5be5,0x5be6,
+ 0x5be8,0x5be2,0x5be4,0x5bdf,0x5c0d,0x5c62,0x5d84,0x5d87,0x5e5b,0x5e63,
+ 0x5e55,0x5e57,0x5e54,0x5ed3,0x5ed6,0x5f0a,0x5f46,0x5f70,0x5fb9,0x6147,
+ 0x613f,0x614b,0x6177,0x6162,0x6163,0x615f,0x615a,0x6158,0x6175,0x622a,
+ 0x6487,0x6458,0x6454,0x64a4,0x6478,0x645f,0x647a,0x6451,0x6467,0x6434,
+ 0x646d,0x647b,0x6572,0x65a1
+ },
+ { /* ku 49 */
+ 0x65d7,0x65d6,0x66a2,0x66a8,0x669d,0x699c,0x69a8,0x6995,0x69c1,0x69ae,
+ 0x69d3,0x69cb,0x699b,0x69b7,0x69bb,0x69ab,0x69b4,0x69d0,0x69cd,0x69ad,
+ 0x69cc,0x69a6,0x69c3,0x69a3,0x6b49,0x6b4c,0x6c33,0x6f33,0x6f14,0x6efe,
+ 0x6f13,0x6ef4,0x6f29,0x6f3e,0x6f20,0x6f2c,0x6f0f,0x6f02,0x6f22,0x6eff,
+ 0x6eef,0x6f06,0x6f31,0x6f38,0x6f32,0x6f23,0x6f15,0x6f2b,0x6f2f,0x6f88,
+ 0x6f2a,0x6eec,0x6f01,0x6ef2,0x6ecc,0x6ef7,0x7194,0x7199,0x717d,0x718a,
+ 0x7184,0x7192,0x723e,0x7292,0x7296,0x7344,0x7350,0x7464,0x7463,0x746a,
+ 0x7470,0x746d,0x7504,0x7591,0x7627,0x760d,0x760b,0x7609,0x7613,0x76e1,
+ 0x76e3,0x7784,0x777d,0x777f,0x7761,0x78c1,0x789f,0x78a7,0x78b3,0x78a9,
+ 0x78a3,0x798e,0x798f,0x798d
+ },
+ { /* ku 4a */
+ 0x7a2e,0x7a31,0x7aaa,0x7aa9,0x7aed,0x7aef,0x7ba1,0x7b95,0x7b8b,0x7b75,
+ 0x7b97,0x7b9d,0x7b94,0x7b8f,0x7bb8,0x7b87,0x7b84,0x7cb9,0x7cbd,0x7cbe,
+ 0x7dbb,0x7db0,0x7d9c,0x7dbd,0x7dbe,0x7da0,0x7dca,0x7db4,0x7db2,0x7db1,
+ 0x7dba,0x7da2,0x7dbf,0x7db5,0x7db8,0x7dad,0x7dd2,0x7dc7,0x7dac,0x7f70,
+ 0x7fe0,0x7fe1,0x7fdf,0x805e,0x805a,0x8087,0x8150,0x8180,0x818f,0x8188,
+ 0x818a,0x817f,0x8182,0x81e7,0x81fa,0x8207,0x8214,0x821e,0x824b,0x84c9,
+ 0x84bf,0x84c6,0x84c4,0x8499,0x849e,0x84b2,0x849c,0x84cb,0x84b8,0x84c0,
+ 0x84d3,0x8490,0x84bc,0x84d1,0x84ca,0x873f,0x871c,0x873b,0x8722,0x8725,
+ 0x8734,0x8718,0x8755,0x8737,0x8729,0x88f3,0x8902,0x88f4,0x88f9,0x88f8,
+ 0x88fd,0x88e8,0x891a,0x88ef
+ },
+ { /* ku 4b */
+ 0x8aa6,0x8a8c,0x8a9e,0x8aa3,0x8a8d,0x8aa1,0x8a93,0x8aa4,0x8aaa,0x8aa5,
+ 0x8aa8,0x8a98,0x8a91,0x8a9a,0x8aa7,0x8c6a,0x8c8d,0x8c8c,0x8cd3,0x8cd1,
+ 0x8cd2,0x8d6b,0x8d99,0x8d95,0x8dfc,0x8f14,0x8f12,0x8f15,0x8f13,0x8fa3,
+ 0x9060,0x9058,0x905c,0x9063,0x9059,0x905e,0x9062,0x905d,0x905b,0x9119,
+ 0x9118,0x911e,0x9175,0x9178,0x9177,0x9174,0x9278,0x92ac,0x9280,0x9285,
+ 0x9298,0x9296,0x927b,0x9293,0x929c,0x92a8,0x927c,0x9291,0x95a1,0x95a8,
+ 0x95a9,0x95a3,0x95a5,0x95a4,0x9699,0x969c,0x969b,0x96cc,0x96d2,0x9700,
+ 0x977c,0x9785,0x97f6,0x9817,0x9818,0x98af,0x98b1,0x9903,0x9905,0x990c,
+ 0x9909,0x99c1,0x9aaf,0x9ab0,0x9ae6,0x9b41,0x9b42,0x9cf4,0x9cf6,0x9cf3,
+ 0x9ebc,0x9f3b,0x9f4a,0x5104
+ },
+ { /* ku 4c */
+ 0x5100,0x50fb,0x50f5,0x50f9,0x5102,0x5108,0x5109,0x5105,0x51dc,0x5287,
+ 0x5288,0x5289,0x528d,0x528a,0x52f0,0x53b2,0x562e,0x563b,0x5639,0x5632,
+ 0x563f,0x5634,0x5629,0x5653,0x564e,0x5657,0x5674,0x5636,0x562f,0x5630,
+ 0x5880,0x589f,0x589e,0x58b3,0x589c,0x58ae,0x58a9,0x58a6,0x596d,0x5b09,
+ 0x5afb,0x5b0b,0x5af5,0x5b0c,0x5b08,0x5bee,0x5bec,0x5be9,0x5beb,0x5c64,
+ 0x5c65,0x5d9d,0x5d94,0x5e62,0x5e5f,0x5e61,0x5ee2,0x5eda,0x5edf,0x5edd,
+ 0x5ee3,0x5ee0,0x5f48,0x5f71,0x5fb7,0x5fb5,0x6176,0x6167,0x616e,0x615d,
+ 0x6155,0x6182,0x617c,0x6170,0x616b,0x617e,0x61a7,0x6190,0x61ab,0x618e,
+ 0x61ac,0x619a,0x61a4,0x6194,0x61ae,0x622e,0x6469,0x646f,0x6479,0x649e,
+ 0x64b2,0x6488,0x6490,0x64b0
+ },
+ { /* ku 4d */
+ 0x64a5,0x6493,0x6495,0x64a9,0x6492,0x64ae,0x64ad,0x64ab,0x649a,0x64ac,
+ 0x6499,0x64a2,0x64b3,0x6575,0x6577,0x6578,0x66ae,0x66ab,0x66b4,0x66b1,
+ 0x6a23,0x6a1f,0x69e8,0x6a01,0x6a1e,0x6a19,0x69fd,0x6a21,0x6a13,0x6a0a,
+ 0x69f3,0x6a02,0x6a05,0x69ed,0x6a11,0x6b50,0x6b4e,0x6ba4,0x6bc5,0x6bc6,
+ 0x6f3f,0x6f7c,0x6f84,0x6f51,0x6f66,0x6f54,0x6f86,0x6f6d,0x6f5b,0x6f78,
+ 0x6f6e,0x6f8e,0x6f7a,0x6f70,0x6f64,0x6f97,0x6f58,0x6ed5,0x6f6f,0x6f60,
+ 0x6f5f,0x719f,0x71ac,0x71b1,0x71a8,0x7256,0x729b,0x734e,0x7357,0x7469,
+ 0x748b,0x7483,0x747e,0x7480,0x757f,0x7620,0x7629,0x761f,0x7624,0x7626,
+ 0x7621,0x7622,0x769a,0x76ba,0x76e4,0x778e,0x7787,0x778c,0x7791,0x778b,
+ 0x78cb,0x78c5,0x78ba,0x78ca
+ },
+ { /* ku 4e */
+ 0x78be,0x78d5,0x78bc,0x78d0,0x7a3f,0x7a3c,0x7a40,0x7a3d,0x7a37,0x7a3b,
+ 0x7aaf,0x7aae,0x7bad,0x7bb1,0x7bc4,0x7bb4,0x7bc6,0x7bc7,0x7bc1,0x7ba0,
+ 0x7bcc,0x7cca,0x7de0,0x7df4,0x7def,0x7dfb,0x7dd8,0x7dec,0x7ddd,0x7de8,
+ 0x7de3,0x7dda,0x7dde,0x7de9,0x7d9e,0x7dd9,0x7df2,0x7df9,0x7f75,0x7f77,
+ 0x7faf,0x7fe9,0x8026,0x819b,0x819c,0x819d,0x81a0,0x819a,0x8198,0x8517,
+ 0x853d,0x851a,0x84ee,0x852c,0x852d,0x8513,0x8511,0x8523,0x8521,0x8514,
+ 0x84ec,0x8525,0x84ff,0x8506,0x8782,0x8774,0x8776,0x8760,0x8766,0x8778,
+ 0x8768,0x8759,0x8757,0x874c,0x8753,0x885b,0x885d,0x8910,0x8907,0x8912,
+ 0x8913,0x8915,0x890a,0x8abc,0x8ad2,0x8ac7,0x8ac4,0x8a95,0x8acb,0x8af8,
+ 0x8ab2,0x8ac9,0x8ac2,0x8abf
+ },
+ { /* ku 4f */
+ 0x8ab0,0x8ad6,0x8acd,0x8ab6,0x8ab9,0x8adb,0x8c4c,0x8c4e,0x8c6c,0x8ce0,
+ 0x8cde,0x8ce6,0x8ce4,0x8cec,0x8ced,0x8ce2,0x8ce3,0x8cdc,0x8cea,0x8ce1,
+ 0x8d6d,0x8d9f,0x8da3,0x8e2b,0x8e10,0x8e1d,0x8e22,0x8e0f,0x8e29,0x8e1f,
+ 0x8e21,0x8e1e,0x8eba,0x8f1d,0x8f1b,0x8f1f,0x8f29,0x8f26,0x8f2a,0x8f1c,
+ 0x8f1e,0x8f25,0x9069,0x906e,0x9068,0x906d,0x9077,0x9130,0x912d,0x9127,
+ 0x9131,0x9187,0x9189,0x918b,0x9183,0x92c5,0x92bb,0x92b7,0x92ea,0x92e4,
+ 0x92c1,0x92b3,0x92bc,0x92d2,0x92c7,0x92f0,0x92b2,0x95ad,0x95b1,0x9704,
+ 0x9706,0x9707,0x9709,0x9760,0x978d,0x978b,0x978f,0x9821,0x982b,0x981c,
+ 0x98b3,0x990a,0x9913,0x9912,0x9918,0x99dd,0x99d0,0x99df,0x99db,0x99d1,
+ 0x99d5,0x99d2,0x99d9,0x9ab7
+ },
+ { /* ku 50 */
+ 0x9aee,0x9aef,0x9b27,0x9b45,0x9b44,0x9b77,0x9b6f,0x9d06,0x9d09,0x9d03,
+ 0x9ea9,0x9ebe,0x9ece,0x58a8,0x9f52,0x5112,0x5118,0x5114,0x5110,0x5115,
+ 0x5180,0x51aa,0x51dd,0x5291,0x5293,0x52f3,0x5659,0x566b,0x5679,0x5669,
+ 0x5664,0x5678,0x566a,0x5668,0x5665,0x5671,0x566f,0x566c,0x5662,0x5676,
+ 0x58c1,0x58be,0x58c7,0x58c5,0x596e,0x5b1d,0x5b34,0x5b78,0x5bf0,0x5c0e,
+ 0x5f4a,0x61b2,0x6191,0x61a9,0x618a,0x61cd,0x61b6,0x61be,0x61ca,0x61c8,
+ 0x6230,0x64c5,0x64c1,0x64cb,0x64bb,0x64bc,0x64da,0x64c4,0x64c7,0x64c2,
+ 0x64cd,0x64bf,0x64d2,0x64d4,0x64be,0x6574,0x66c6,0x66c9,0x66b9,0x66c4,
+ 0x66c7,0x66b8,0x6a3d,0x6a38,0x6a3a,0x6a59,0x6a6b,0x6a58,0x6a39,0x6a44,
+ 0x6a62,0x6a61,0x6a4b,0x6a47
+ },
+ { /* ku 51 */
+ 0x6a35,0x6a5f,0x6a48,0x6b59,0x6b77,0x6c05,0x6fc2,0x6fb1,0x6fa1,0x6fc3,
+ 0x6fa4,0x6fc1,0x6fa7,0x6fb3,0x6fc0,0x6fb9,0x6fb6,0x6fa6,0x6fa0,0x6fb4,
+ 0x71be,0x71c9,0x71d0,0x71d2,0x71c8,0x71d5,0x71b9,0x71ce,0x71d9,0x71dc,
+ 0x71c3,0x71c4,0x7368,0x749c,0x74a3,0x7498,0x749f,0x749e,0x74e2,0x750c,
+ 0x750d,0x7634,0x7638,0x763a,0x76e7,0x76e5,0x77a0,0x779e,0x779f,0x77a5,
+ 0x78e8,0x78da,0x78ec,0x78e7,0x79a6,0x7a4d,0x7a4e,0x7a46,0x7a4c,0x7a4b,
+ 0x7aba,0x7bd9,0x7c11,0x7bc9,0x7be4,0x7bdb,0x7be1,0x7be9,0x7be6,0x7cd5,
+ 0x7cd6,0x7e0a,0x7e11,0x7e08,0x7e1b,0x7e23,0x7e1e,0x7e1d,0x7e09,0x7e10,
+ 0x7f79,0x7fb2,0x7ff0,0x7ff1,0x7fee,0x8028,0x81b3,0x81a9,0x81a8,0x81fb,
+ 0x8208,0x8258,0x8259,0x854a
+ },
+ { /* ku 52 */
+ 0x8559,0x8548,0x8568,0x8569,0x8543,0x8549,0x856d,0x856a,0x855e,0x8783,
+ 0x879f,0x879e,0x87a2,0x878d,0x8861,0x892a,0x8932,0x8925,0x892b,0x8921,
+ 0x89aa,0x89a6,0x8ae6,0x8afa,0x8aeb,0x8af1,0x8b00,0x8adc,0x8ae7,0x8aee,
+ 0x8afe,0x8b01,0x8b02,0x8af7,0x8aed,0x8af3,0x8af6,0x8afc,0x8c6b,0x8c6d,
+ 0x8c93,0x8cf4,0x8e44,0x8e31,0x8e34,0x8e42,0x8e39,0x8e35,0x8f3b,0x8f2f,
+ 0x8f38,0x8f33,0x8fa8,0x8fa6,0x9075,0x9074,0x9078,0x9072,0x907c,0x907a,
+ 0x9134,0x9192,0x9320,0x9336,0x92f8,0x9333,0x932f,0x9322,0x92fc,0x932b,
+ 0x9304,0x931a,0x9310,0x9326,0x9321,0x9315,0x932e,0x9319,0x95bb,0x96a7,
+ 0x96a8,0x96aa,0x96d5,0x970e,0x9711,0x9716,0x970d,0x9713,0x970f,0x975b,
+ 0x975c,0x9766,0x9798,0x9830
+ },
+ { /* ku 53 */
+ 0x9838,0x983b,0x9837,0x982d,0x9839,0x9824,0x9910,0x9928,0x991e,0x991b,
+ 0x9921,0x991a,0x99ed,0x99e2,0x99f1,0x9ab8,0x9abc,0x9afb,0x9aed,0x9b28,
+ 0x9b91,0x9d15,0x9d23,0x9d26,0x9d28,0x9d12,0x9d1b,0x9ed8,0x9ed4,0x9f8d,
+ 0x9f9c,0x512a,0x511f,0x5121,0x5132,0x52f5,0x568e,0x5680,0x5690,0x5685,
+ 0x5687,0x568f,0x58d5,0x58d3,0x58d1,0x58ce,0x5b30,0x5b2a,0x5b24,0x5b7a,
+ 0x5c37,0x5c68,0x5dbc,0x5dba,0x5dbd,0x5db8,0x5e6b,0x5f4c,0x5fbd,0x61c9,
+ 0x61c2,0x61c7,0x61e6,0x61cb,0x6232,0x6234,0x64ce,0x64ca,0x64d8,0x64e0,
+ 0x64f0,0x64e6,0x64ec,0x64f1,0x64e2,0x64ed,0x6582,0x6583,0x66d9,0x66d6,
+ 0x6a80,0x6a94,0x6a84,0x6aa2,0x6a9c,0x6adb,0x6aa3,0x6a7e,0x6a97,0x6a90,
+ 0x6aa0,0x6b5c,0x6bae,0x6bda
+ },
+ { /* ku 54 */
+ 0x6c08,0x6fd8,0x6ff1,0x6fdf,0x6fe0,0x6fdb,0x6fe4,0x6feb,0x6fef,0x6f80,
+ 0x6fec,0x6fe1,0x6fe9,0x6fd5,0x6fee,0x6ff0,0x71e7,0x71df,0x71ee,0x71e6,
+ 0x71e5,0x71ed,0x71ec,0x71f4,0x71e0,0x7235,0x7246,0x7370,0x7372,0x74a9,
+ 0x74b0,0x74a6,0x74a8,0x7646,0x7642,0x764c,0x76ea,0x77b3,0x77aa,0x77b0,
+ 0x77ac,0x77a7,0x77ad,0x77ef,0x78f7,0x78fa,0x78f4,0x78ef,0x7901,0x79a7,
+ 0x79aa,0x7a57,0x7abf,0x7c07,0x7c0d,0x7bfe,0x7bf7,0x7c0c,0x7be0,0x7ce0,
+ 0x7cdc,0x7cde,0x7ce2,0x7cdf,0x7cd9,0x7cdd,0x7e2e,0x7e3e,0x7e46,0x7e37,
+ 0x7e32,0x7e43,0x7e2b,0x7e3d,0x7e31,0x7e45,0x7e41,0x7e34,0x7e39,0x7e48,
+ 0x7e35,0x7e3f,0x7e2f,0x7f44,0x7ff3,0x7ffc,0x8071,0x8072,0x8070,0x806f,
+ 0x8073,0x81c6,0x81c3,0x81ba
+ },
+ { /* ku 55 */
+ 0x81c2,0x81c0,0x81bf,0x81bd,0x81c9,0x81be,0x81e8,0x8209,0x8271,0x85aa,
+ 0x8584,0x857e,0x859c,0x8591,0x8594,0x85af,0x859b,0x8587,0x85a8,0x858a,
+ 0x85a6,0x8667,0x87c0,0x87d1,0x87b3,0x87d2,0x87c6,0x87ab,0x87bb,0x87ba,
+ 0x87c8,0x87cb,0x893b,0x8936,0x8944,0x8938,0x893d,0x89ac,0x8b0e,0x8b17,
+ 0x8b19,0x8b1b,0x8b0a,0x8b20,0x8b1d,0x8b04,0x8b10,0x8c41,0x8c3f,0x8c73,
+ 0x8cfa,0x8cfd,0x8cfc,0x8cf8,0x8cfb,0x8da8,0x8e49,0x8e4b,0x8e48,0x8e4a,
+ 0x8f44,0x8f3e,0x8f42,0x8f45,0x8f3f,0x907f,0x907d,0x9084,0x9081,0x9082,
+ 0x9080,0x9139,0x91a3,0x919e,0x919c,0x934d,0x9382,0x9328,0x9375,0x934a,
+ 0x9365,0x934b,0x9318,0x937e,0x936c,0x935b,0x9370,0x935a,0x9354,0x95ca,
+ 0x95cb,0x95cc,0x95c8,0x95c6
+ },
+ { /* ku 56 */
+ 0x96b1,0x96b8,0x96d6,0x971c,0x971e,0x97a0,0x97d3,0x9846,0x98b6,0x9935,
+ 0x9a01,0x99ff,0x9bae,0x9bab,0x9baa,0x9bad,0x9d3b,0x9d3f,0x9e8b,0x9ecf,
+ 0x9ede,0x9edc,0x9edd,0x9edb,0x9f3e,0x9f4b,0x53e2,0x5695,0x56ae,0x58d9,
+ 0x58d8,0x5b38,0x5f5e,0x61e3,0x6233,0x64f4,0x64f2,0x64fe,0x6506,0x64fa,
+ 0x64fb,0x64f7,0x65b7,0x66dc,0x6726,0x6ab3,0x6aac,0x6ac3,0x6abb,0x6ab8,
+ 0x6ac2,0x6aae,0x6aaf,0x6b5f,0x6b78,0x6baf,0x7009,0x700b,0x6ffe,0x7006,
+ 0x6ffa,0x7011,0x700f,0x71fb,0x71fc,0x71fe,0x71f8,0x7377,0x7375,0x74a7,
+ 0x74bf,0x7515,0x7656,0x7658,0x7652,0x77bd,0x77bf,0x77bb,0x77bc,0x790e,
+ 0x79ae,0x7a61,0x7a62,0x7a60,0x7ac4,0x7ac5,0x7c2b,0x7c27,0x7c2a,0x7c1e,
+ 0x7c23,0x7c21,0x7ce7,0x7e54
+ },
+ { /* ku 57 */
+ 0x7e55,0x7e5e,0x7e5a,0x7e61,0x7e52,0x7e59,0x7f48,0x7ff9,0x7ffb,0x8077,
+ 0x8076,0x81cd,0x81cf,0x820a,0x85cf,0x85a9,0x85cd,0x85d0,0x85c9,0x85b0,
+ 0x85ba,0x85b9,0x87ef,0x87ec,0x87f2,0x87e0,0x8986,0x89b2,0x89f4,0x8b28,
+ 0x8b39,0x8b2c,0x8b2b,0x8c50,0x8d05,0x8e59,0x8e63,0x8e66,0x8e64,0x8e5f,
+ 0x8e55,0x8ec0,0x8f49,0x8f4d,0x9087,0x9083,0x9088,0x91ab,0x91ac,0x91d0,
+ 0x9394,0x938a,0x9396,0x93a2,0x93b3,0x93ae,0x93ac,0x93b0,0x9398,0x939a,
+ 0x9397,0x95d4,0x95d6,0x95d0,0x95d5,0x96e2,0x96dc,0x96d9,0x96db,0x96de,
+ 0x9724,0x97a3,0x97a6,0x97ad,0x97f9,0x984d,0x984f,0x984c,0x984e,0x9853,
+ 0x98ba,0x993e,0x993f,0x993d,0x992e,0x99a5,0x9a0e,0x9ac1,0x9b03,0x9b06,
+ 0x9b4f,0x9b4e,0x9b4d,0x9bca
+ },
+ { /* ku 58 */
+ 0x9bc9,0x9bfd,0x9bc8,0x9bc0,0x9d51,0x9d5d,0x9d60,0x9ee0,0x9f15,0x9f2c,
+ 0x5133,0x56a5,0x56a8,0x58de,0x58df,0x58e2,0x5bf5,0x9f90,0x5eec,0x61f2,
+ 0x61f7,0x61f6,0x61f5,0x6500,0x650f,0x66e0,0x66dd,0x6ae5,0x6add,0x6ada,
+ 0x6ad3,0x701b,0x701f,0x7028,0x701a,0x701d,0x7015,0x7018,0x7206,0x720d,
+ 0x7258,0x72a2,0x7378,0x737a,0x74bd,0x74ca,0x74e3,0x7587,0x7586,0x765f,
+ 0x7661,0x77c7,0x7919,0x79b1,0x7a6b,0x7a69,0x7c3e,0x7c3f,0x7c38,0x7c3d,
+ 0x7c37,0x7c40,0x7e6b,0x7e6d,0x7e79,0x7e69,0x7e6a,0x7e73,0x7f85,0x7fb6,
+ 0x7fb9,0x7fb8,0x81d8,0x85e9,0x85dd,0x85ea,0x85d5,0x85e4,0x85e5,0x85f7,
+ 0x87fb,0x8805,0x880d,0x87f9,0x87fe,0x8960,0x895f,0x8956,0x895e,0x8b41,
+ 0x8b5c,0x8b58,0x8b49,0x8b5a
+ },
+ { /* ku 59 */
+ 0x8b4e,0x8b4f,0x8b46,0x8b59,0x8d08,0x8d0a,0x8e7c,0x8e72,0x8e87,0x8e76,
+ 0x8e6c,0x8e7a,0x8e74,0x8f54,0x8f4e,0x8fad,0x908a,0x908b,0x91b1,0x91ae,
+ 0x93e1,0x93d1,0x93df,0x93c3,0x93c8,0x93dc,0x93dd,0x93d6,0x93e2,0x93cd,
+ 0x93d8,0x93e4,0x93d7,0x93e8,0x95dc,0x96b4,0x96e3,0x972a,0x9727,0x9761,
+ 0x97dc,0x97fb,0x985e,0x9858,0x985b,0x98bc,0x9945,0x9949,0x9a16,0x9a19,
+ 0x9b0d,0x9be8,0x9be7,0x9bd6,0x9bdb,0x9d89,0x9d61,0x9d72,0x9d6a,0x9d6c,
+ 0x9e92,0x9e97,0x9e93,0x9eb4,0x52f8,0x56b7,0x56b6,0x56b4,0x56bc,0x58e4,
+ 0x5b40,0x5b43,0x5b7d,0x5bf6,0x5dc9,0x61f8,0x61fa,0x6518,0x6514,0x6519,
+ 0x66e6,0x6727,0x6aec,0x703e,0x7030,0x7032,0x7210,0x737b,0x74cf,0x7662,
+ 0x7665,0x7926,0x792a,0x792c
+ },
+ { /* ku 5a */
+ 0x792b,0x7ac7,0x7af6,0x7c4c,0x7c43,0x7c4d,0x7cef,0x7cf0,0x8fae,0x7e7d,
+ 0x7e7c,0x7e82,0x7f4c,0x8000,0x81da,0x8266,0x85fb,0x85f9,0x8611,0x85fa,
+ 0x8606,0x860b,0x8607,0x860a,0x8814,0x8815,0x8964,0x89ba,0x89f8,0x8b70,
+ 0x8b6c,0x8b66,0x8b6f,0x8b5f,0x8b6b,0x8d0f,0x8d0d,0x8e89,0x8e81,0x8e85,
+ 0x8e82,0x91b4,0x91cb,0x9418,0x9403,0x93fd,0x95e1,0x9730,0x98c4,0x9952,
+ 0x9951,0x99a8,0x9a2b,0x9a30,0x9a37,0x9a35,0x9c13,0x9c0d,0x9e79,0x9eb5,
+ 0x9ee8,0x9f2f,0x9f5f,0x9f63,0x9f61,0x5137,0x5138,0x56c1,0x56c0,0x56c2,
+ 0x5914,0x5c6c,0x5dcd,0x61fc,0x61fe,0x651d,0x651c,0x6595,0x66e9,0x6afb,
+ 0x6b04,0x6afa,0x6bb2,0x704c,0x721b,0x72a7,0x74d6,0x74d4,0x7669,0x77d3,
+ 0x7c50,0x7e8f,0x7e8c,0x7fbc
+ },
+ { /* ku 5b */
+ 0x8617,0x862d,0x861a,0x8823,0x8822,0x8821,0x881f,0x896a,0x896c,0x89bd,
+ 0x8b74,0x8b77,0x8b7d,0x8d13,0x8e8a,0x8e8d,0x8e8b,0x8f5f,0x8faf,0x91ba,
+ 0x942e,0x9433,0x9435,0x943a,0x9438,0x9432,0x942b,0x95e2,0x9738,0x9739,
+ 0x9732,0x97ff,0x9867,0x9865,0x9957,0x9a45,0x9a43,0x9a40,0x9a3e,0x9acf,
+ 0x9b54,0x9b51,0x9c2d,0x9c25,0x9daf,0x9db4,0x9dc2,0x9db8,0x9e9d,0x9eef,
+ 0x9f19,0x9f5c,0x9f66,0x9f67,0x513c,0x513b,0x56c8,0x56ca,0x56c9,0x5b7f,
+ 0x5dd4,0x5dd2,0x5f4e,0x61ff,0x6524,0x6b0a,0x6b61,0x7051,0x7058,0x7380,
+ 0x74e4,0x758a,0x766e,0x766c,0x79b3,0x7c60,0x7c5f,0x807e,0x807d,0x81df,
+ 0x8972,0x896f,0x89fc,0x8b80,0x8d16,0x8d17,0x8e91,0x8e93,0x8f61,0x9148,
+ 0x9444,0x9451,0x9452,0x973d
+ },
+ { /* ku 5c */
+ 0x973e,0x97c3,0x97c1,0x986b,0x9955,0x9a55,0x9a4d,0x9ad2,0x9b1a,0x9c49,
+ 0x9c31,0x9c3e,0x9c3b,0x9dd3,0x9dd7,0x9f34,0x9f6c,0x9f6a,0x9f94,0x56cc,
+ 0x5dd6,0x6200,0x6523,0x652b,0x652a,0x66ec,0x6b10,0x74da,0x7aca,0x7c64,
+ 0x7c63,0x7c65,0x7e93,0x7e96,0x7e94,0x81e2,0x8638,0x863f,0x8831,0x8b8a,
+ 0x9090,0x908f,0x9463,0x9460,0x9464,0x9768,0x986f,0x995c,0x9a5a,0x9a5b,
+ 0x9a57,0x9ad3,0x9ad4,0x9ad1,0x9c54,0x9c57,0x9c56,0x9de5,0x9e9f,0x9ef4,
+ 0x56d1,0x58e9,0x652c,0x705e,0x7671,0x7672,0x77d7,0x7f50,0x7f88,0x8836,
+ 0x8839,0x8862,0x8b93,0x8b92,0x8b96,0x8277,0x8d1b,0x91c0,0x946a,0x9742,
+ 0x9748,0x9744,0x97c6,0x9870,0x9a5f,0x9b22,0x9b58,0x9c5f,0x9df9,0x9dfa,
+ 0x9e7c,0x9e7d,0x9f07,0x9f77
+ },
+ { /* ku 5d */
+ 0x9f72,0x5ef3,0x6b16,0x7063,0x7c6c,0x7c6e,0x883b,0x89c0,0x8ea1,0x91c1,
+ 0x9472,0x9470,0x9871,0x995e,0x9ad6,0x9b23,0x9ecc,0x7064,0x77da,0x8b9a,
+ 0x9477,0x97c9,0x9a62,0x9a65,0x7e9c,0x8b9c,0x8eaa,0x91c5,0x947d,0x947e,
+ 0x947c,0x9c77,0x9c78,0x9ef7,0x8c54,0x947f,0x9e1a,0x7228,0x9a6a,0x9b31,
+ 0x9e1b,0x9e1e,0x7c72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 2 conversion table */
+
+static const unsigned short
+ cns11643_2tab[MAX_CNS11643_KU_2][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e42,0x4e5c,0x51f5,0x531a,0x5382,0x4e07,0x4e0c,0x4e47,0x4e8d,0x56d7,
+ 0x5c6e,0x5f73,0x4e0f,0x5187,0x4e0e,0x4e2e,0x4e93,0x4ec2,0x4ec9,0x4ec8,
+ 0x5198,0x52fc,0x536c,0x53b9,0x5720,0x5903,0x592c,0x5c10,0x5dff,0x65e1,
+ 0x6bb3,0x6bcc,0x6c14,0x723f,0x4e31,0x4e3c,0x4ee8,0x4edc,0x4ee9,0x4ee1,
+ 0x4edd,0x4eda,0x520c,0x5209,0x531c,0x534c,0x5722,0x5723,0x5917,0x592f,
+ 0x5b81,0x5b84,0x5c12,0x5c3b,0x5c74,0x5c73,0x5e04,0x5e80,0x5e82,0x5fc9,
+ 0x6209,0x6250,0x6c15,0x6c36,0x6c43,0x6c3f,0x6c3b,0x72ae,0x72b0,0x738a,
+ 0x79b8,0x808a,0x961e,0x4f0e,0x4f18,0x4f2c,0x4ef5,0x4f14,0x4ef1,0x4f00,
+ 0x4ef7,0x4f08,0x4f1d,0x4f02,0x4f05,0x4f22,0x4f13,0x4f04,0x4ef4,0x4f12,
+ 0x51b1,0x5213,0x5210,0x52a6
+ },
+ { /* ku 02 */
+ 0x5322,0x531f,0x534d,0x538a,0x5407,0x56e1,0x56df,0x572e,0x572a,0x5734,
+ 0x593c,0x5980,0x597c,0x5985,0x597b,0x597e,0x5977,0x597f,0x5b56,0x5c15,
+ 0x5c25,0x5c7c,0x5c7a,0x5c7b,0x5c7e,0x5ddf,0x5e75,0x5e84,0x5f02,0x5f1a,
+ 0x5f74,0x5fd5,0x5fd4,0x5fcf,0x625c,0x625e,0x6264,0x6261,0x6266,0x6262,
+ 0x6259,0x6260,0x625a,0x6265,0x6537,0x65ef,0x65ee,0x673e,0x6739,0x6738,
+ 0x673b,0x673a,0x673f,0x673c,0x6733,0x6c18,0x6c46,0x6c52,0x6c5c,0x6c4f,
+ 0x6c4a,0x6c54,0x6c4b,0x6c4c,0x7071,0x725e,0x72b4,0x72b5,0x738e,0x752a,
+ 0x767f,0x7a75,0x7f51,0x8278,0x827c,0x8280,0x827d,0x827f,0x864d,0x897e,
+ 0x9099,0x9097,0x9098,0x909b,0x9094,0x9622,0x9624,0x9620,0x9623,0x4f56,
+ 0x4f3b,0x4f62,0x4f49,0x4f53
+ },
+ { /* ku 03 */
+ 0x4f64,0x4f3e,0x4f67,0x4f52,0x4f5f,0x4f41,0x4f58,0x4f2d,0x4f33,0x4f3f,
+ 0x4f61,0x518f,0x51b9,0x521c,0x521e,0x5221,0x52ad,0x52ae,0x5309,0x5363,
+ 0x5372,0x538e,0x538f,0x5430,0x5437,0x542a,0x5454,0x5445,0x5419,0x541c,
+ 0x5425,0x5418,0x543d,0x544f,0x5441,0x5428,0x5424,0x5447,0x56ee,0x56e7,
+ 0x56e5,0x5741,0x5745,0x574c,0x5749,0x574b,0x5752,0x5906,0x5940,0x59a6,
+ 0x5998,0x59a0,0x5997,0x598e,0x59a2,0x5990,0x598f,0x59a7,0x59a1,0x5b8e,
+ 0x5b92,0x5c28,0x5c2a,0x5c8d,0x5c8f,0x5c88,0x5c8b,0x5c89,0x5c92,0x5c8a,
+ 0x5c86,0x5c93,0x5c95,0x5de0,0x5e0a,0x5e0e,0x5e8b,0x5e89,0x5e8c,0x5e88,
+ 0x5e8d,0x5f05,0x5f1d,0x5f78,0x5f76,0x5fd2,0x5fd1,0x5fd0,0x5fed,0x5fe8,
+ 0x5fee,0x5ff3,0x5fe1,0x5fe4
+ },
+ { /* ku 04 */
+ 0x5fe3,0x5ffa,0x5fef,0x5ff7,0x5ffb,0x6000,0x5ff4,0x623a,0x6283,0x628c,
+ 0x628e,0x628f,0x6294,0x6287,0x6271,0x627b,0x627a,0x6270,0x6281,0x6288,
+ 0x6277,0x627d,0x6272,0x6274,0x65f0,0x65f4,0x65f3,0x65f2,0x65f5,0x6745,
+ 0x6747,0x6759,0x6755,0x674c,0x6748,0x675d,0x674d,0x675a,0x674b,0x6bd0,
+ 0x6c19,0x6c1a,0x6c78,0x6c67,0x6c6b,0x6c84,0x6c8b,0x6c8f,0x6c71,0x6c6f,
+ 0x6c69,0x6c9a,0x6c6d,0x6c87,0x6c95,0x6c9c,0x6c66,0x6c73,0x6c65,0x6c7b,
+ 0x6c8e,0x7074,0x707a,0x7263,0x72bf,0x72bd,0x72c3,0x72c6,0x72c1,0x72ba,
+ 0x72c5,0x7395,0x7397,0x7393,0x7394,0x7392,0x753a,0x7539,0x7594,0x7595,
+ 0x7681,0x793d,0x8034,0x8095,0x8099,0x8090,0x8092,0x809c,0x8290,0x828f,
+ 0x8285,0x828e,0x8291,0x8293
+ },
+ { /* ku 05 */
+ 0x828a,0x8283,0x8284,0x8c78,0x8fc9,0x8fbf,0x909f,0x90a1,0x90a5,0x909e,
+ 0x90a7,0x90a0,0x9630,0x9628,0x962f,0x962d,0x4e33,0x4f98,0x4f7c,0x4f85,
+ 0x4f7d,0x4f80,0x4f87,0x4f76,0x4f74,0x4f89,0x4f84,0x4f77,0x4f4c,0x4f97,
+ 0x4f6a,0x4f9a,0x4f79,0x4f81,0x4f78,0x4f90,0x4f9c,0x4f94,0x4f9e,0x4f92,
+ 0x4f82,0x4f95,0x4f6b,0x4f6e,0x519e,0x51bc,0x51be,0x5235,0x5232,0x5233,
+ 0x5246,0x5231,0x52bc,0x530a,0x530b,0x533c,0x5392,0x5394,0x5487,0x547f,
+ 0x5481,0x5491,0x5482,0x5488,0x546b,0x547a,0x547e,0x5465,0x546c,0x5474,
+ 0x5466,0x548d,0x546f,0x5461,0x5460,0x5498,0x5463,0x5467,0x5464,0x56f7,
+ 0x56f9,0x576f,0x5772,0x576d,0x576b,0x5771,0x5770,0x5776,0x5780,0x5775,
+ 0x577b,0x5773,0x5774,0x5762
+ },
+ { /* ku 06 */
+ 0x5768,0x577d,0x590c,0x5945,0x59b5,0x59ba,0x59cf,0x59ce,0x59b2,0x59cc,
+ 0x59c1,0x59b6,0x59bc,0x59c3,0x59d6,0x59b1,0x59bd,0x59c0,0x59c8,0x59b4,
+ 0x59c7,0x5b62,0x5b65,0x5b93,0x5b95,0x5c44,0x5c47,0x5cae,0x5ca4,0x5ca0,
+ 0x5cb5,0x5caf,0x5ca8,0x5cac,0x5c9f,0x5ca3,0x5cad,0x5ca2,0x5caa,0x5ca7,
+ 0x5c9d,0x5ca5,0x5cb6,0x5cb0,0x5ca6,0x5e17,0x5e14,0x5e19,0x5f28,0x5f22,
+ 0x5f23,0x5f24,0x5f54,0x5f82,0x5f7e,0x5f7d,0x5fde,0x5fe5,0x602d,0x6026,
+ 0x6019,0x6032,0x600b,0x6034,0x600a,0x6017,0x6033,0x601a,0x601e,0x602c,
+ 0x6022,0x600d,0x6010,0x602e,0x6013,0x6011,0x600c,0x6009,0x601c,0x6214,
+ 0x623d,0x62ad,0x62b4,0x62d1,0x62be,0x62aa,0x62b6,0x62ca,0x62ae,0x62b3,
+ 0x62af,0x62bb,0x62a9,0x62b0
+ },
+ { /* ku 07 */
+ 0x62b8,0x653d,0x65a8,0x65bb,0x6609,0x65fc,0x6604,0x6612,0x6608,0x65fb,
+ 0x6603,0x660b,0x660d,0x6605,0x65fd,0x6611,0x6610,0x66f6,0x670a,0x6785,
+ 0x676c,0x678e,0x6792,0x6776,0x677b,0x6798,0x6786,0x6784,0x6774,0x678d,
+ 0x678c,0x677a,0x679f,0x6791,0x6799,0x6783,0x677d,0x6781,0x6778,0x6779,
+ 0x6794,0x6b25,0x6b80,0x6b7e,0x6bde,0x6c1d,0x6c93,0x6cec,0x6ceb,0x6cee,
+ 0x6cd9,0x6cb6,0x6cd4,0x6cad,0x6ce7,0x6cb7,0x6cd0,0x6cc2,0x6cba,0x6cc3,
+ 0x6cc6,0x6ced,0x6cf2,0x6cd2,0x6cdd,0x6cb4,0x6c8a,0x6c9d,0x6c80,0x6cde,
+ 0x6cc0,0x6d30,0x6ccd,0x6cc7,0x6cb0,0x6cf9,0x6ccf,0x6ce9,0x6cd1,0x7094,
+ 0x7098,0x7085,0x7093,0x7086,0x7084,0x7091,0x7096,0x7082,0x709a,0x7083,
+ 0x726a,0x72d6,0x72cb,0x72d8
+ },
+ { /* ku 08 */
+ 0x72c9,0x72dc,0x72d2,0x72d4,0x72da,0x72cc,0x72d1,0x73a4,0x73a1,0x73ad,
+ 0x73a6,0x73a2,0x73a0,0x73ac,0x739d,0x74dd,0x74e8,0x753f,0x7540,0x753e,
+ 0x758c,0x7598,0x76af,0x76f3,0x76f1,0x76f0,0x76f5,0x77f8,0x77fc,0x77f9,
+ 0x77fb,0x77fa,0x77f7,0x7942,0x793f,0x79c5,0x7a78,0x7a7b,0x7afb,0x7c75,
+ 0x7cfd,0x8035,0x808f,0x80ae,0x80a3,0x80b8,0x80b5,0x80ad,0x8220,0x82a0,
+ 0x82c0,0x82ab,0x829a,0x8298,0x829b,0x82b5,0x82a7,0x82ae,0x82bc,0x829e,
+ 0x82ba,0x82b4,0x82a8,0x82a1,0x82a9,0x82c2,0x82a4,0x82c3,0x82b6,0x82a2,
+ 0x8670,0x866f,0x866d,0x866e,0x8c56,0x8fd2,0x8fcb,0x8fd3,0x8fcd,0x8fd6,
+ 0x8fd5,0x8fd7,0x90b2,0x90b4,0x90af,0x90b3,0x90b0,0x9639,0x963d,0x963c,
+ 0x963a,0x9643,0x4fcd,0x4fc5
+ },
+ { /* ku 09 */
+ 0x4fd3,0x4fb2,0x4fc9,0x4fcb,0x4fc1,0x4fd4,0x4fdc,0x4fd9,0x4fbb,0x4fb3,
+ 0x4fdb,0x4fc7,0x4fd6,0x4fba,0x4fc0,0x4fb9,0x4fec,0x5244,0x5249,0x52c0,
+ 0x52c2,0x533d,0x537c,0x5397,0x5396,0x5399,0x5398,0x54ba,0x54a1,0x54ad,
+ 0x54a5,0x54cf,0x54c3,0x830d,0x54b7,0x54ae,0x54d6,0x54b6,0x54c5,0x54c6,
+ 0x54a0,0x5470,0x54bc,0x54a2,0x54be,0x5472,0x54de,0x54b0,0x57b5,0x579e,
+ 0x579f,0x57a4,0x578c,0x5797,0x579d,0x579b,0x5794,0x5798,0x578f,0x5799,
+ 0x57a5,0x579a,0x5795,0x58f4,0x590d,0x5953,0x59e1,0x59de,0x59ee,0x5a00,
+ 0x59f1,0x59dd,0x59fa,0x59fd,0x59fc,0x59f6,0x59e4,0x59f2,0x59f7,0x59db,
+ 0x59e9,0x59f3,0x59f5,0x59e0,0x59fe,0x59f4,0x59ed,0x5ba8,0x5c4c,0x5cd0,
+ 0x5cd8,0x5ccc,0x5cd7,0x5ccb
+ },
+ { /* ku 0a */
+ 0x5cdb,0x5cde,0x5cda,0x5cc9,0x5cc7,0x5cca,0x5cd6,0x5cd3,0x5cd4,0x5ccf,
+ 0x5cc8,0x5cc6,0x5cce,0x5cdf,0x5cf8,0x5df9,0x5e21,0x5e22,0x5e23,0x5e20,
+ 0x5e24,0x5eb0,0x5ea4,0x5ea2,0x5e9b,0x5ea3,0x5ea5,0x5f07,0x5f2e,0x5f56,
+ 0x5f86,0x6037,0x6039,0x6054,0x6072,0x605e,0x6045,0x6053,0x6047,0x6049,
+ 0x605b,0x604c,0x6040,0x6042,0x605f,0x6024,0x6044,0x6058,0x6066,0x606e,
+ 0x6242,0x6243,0x62cf,0x630d,0x630b,0x62f5,0x630e,0x6303,0x62eb,0x62f9,
+ 0x630f,0x630c,0x62f8,0x62f6,0x6300,0x6313,0x6314,0x62fa,0x6315,0x62fb,
+ 0x62f0,0x6541,0x6543,0x65aa,0x65bf,0x6636,0x6621,0x6632,0x6635,0x661c,
+ 0x6626,0x6622,0x6633,0x662b,0x663a,0x661d,0x6634,0x6639,0x662e,0x670f,
+ 0x6710,0x67c1,0x67f2,0x67c8
+ },
+ { /* ku 0b */
+ 0x67ba,0x67dc,0x67bb,0x67f8,0x67d8,0x67c0,0x67b7,0x67c5,0x67eb,0x67e4,
+ 0x67df,0x67b5,0x67cd,0x67b3,0x67f7,0x67f6,0x67ee,0x67e3,0x67c2,0x67b9,
+ 0x67ce,0x67e7,0x67f0,0x67b2,0x67fc,0x67c6,0x67ed,0x67cc,0x67ae,0x67e6,
+ 0x67db,0x67fa,0x67c9,0x67ca,0x67c3,0x67ea,0x67cb,0x6b28,0x6b82,0x6b84,
+ 0x6bb6,0x6bd6,0x6bd8,0x6be0,0x6c20,0x6c21,0x6d28,0x6d34,0x6d2d,0x6d1f,
+ 0x6d3c,0x6d3f,0x6d12,0x6d0a,0x6cda,0x6d33,0x6d04,0x6d19,0x6d3a,0x6d1a,
+ 0x6d11,0x6d00,0x6d1d,0x6d42,0x6d01,0x6d18,0x6d37,0x6d03,0x6d0f,0x6d40,
+ 0x6d07,0x6d20,0x6d2c,0x6d08,0x6d22,0x6d09,0x6d10,0x70b7,0x709f,0x70be,
+ 0x70b1,0x70b0,0x70a1,0x70b4,0x70b5,0x70a9,0x7241,0x7249,0x724a,0x726c,
+ 0x7270,0x7273,0x726e,0x72ca
+ },
+ { /* ku 0c */
+ 0x72e4,0x72e8,0x72eb,0x72df,0x72ea,0x72e6,0x72e3,0x7385,0x73cc,0x73c2,
+ 0x73c8,0x73c5,0x73b9,0x73b6,0x73b5,0x73b4,0x73eb,0x73bf,0x73c7,0x73be,
+ 0x73c3,0x73c6,0x73b8,0x73cb,0x74ec,0x74ee,0x752e,0x7547,0x7548,0x75a7,
+ 0x75aa,0x7679,0x76c4,0x7708,0x7703,0x7704,0x7705,0x770a,0x76f7,0x76fb,
+ 0x76fa,0x77e7,0x77e8,0x7806,0x7811,0x7812,0x7805,0x7810,0x780f,0x780e,
+ 0x7809,0x7803,0x7813,0x794a,0x794c,0x794b,0x7945,0x7944,0x79d5,0x79cd,
+ 0x79cf,0x79d6,0x79ce,0x7a80,0x7a7e,0x7ad1,0x7b00,0x7b01,0x7c7a,0x7c78,
+ 0x7c79,0x7c7f,0x7c80,0x7c81,0x7d03,0x7d08,0x7d01,0x7f58,0x7f91,0x7f8d,
+ 0x7fbe,0x8007,0x800e,0x800f,0x8014,0x8037,0x80d8,0x80c7,0x80e0,0x80d1,
+ 0x80c8,0x80c2,0x80d0,0x80c5
+ },
+ { /* ku 0d */
+ 0x80e3,0x80d9,0x80dc,0x80ca,0x80d5,0x80c9,0x80cf,0x80d7,0x80e6,0x80cd,
+ 0x81ff,0x8221,0x8294,0x82d9,0x82fe,0x82f9,0x8307,0x82e8,0x8300,0x82d5,
+ 0x833a,0x82eb,0x82d6,0x82f4,0x82ec,0x82e1,0x82f2,0x82f5,0x830c,0x82fb,
+ 0x82f6,0x82f0,0x82ea,0x82e4,0x82e0,0x82fa,0x82f3,0x82ed,0x8677,0x8674,
+ 0x867c,0x8673,0x8841,0x884e,0x8867,0x886a,0x8869,0x89d3,0x8a04,0x8a07,
+ 0x8d72,0x8fe3,0x8fe1,0x8fee,0x8fe0,0x90f1,0x90bd,0x90bf,0x90d5,0x90c5,
+ 0x90be,0x90c7,0x90cb,0x90c8,0x91d4,0x91d3,0x9654,0x964f,0x9651,0x9653,
+ 0x964a,0x964e,0x501e,0x5005,0x5007,0x5013,0x5022,0x5030,0x501b,0x4ff5,
+ 0x4ff4,0x5033,0x5037,0x502c,0x4ff6,0x4ff7,0x5017,0x501c,0x5020,0x5027,
+ 0x5035,0x502f,0x5031,0x500e
+ },
+ { /* ku 0e */
+ 0x515a,0x5194,0x5193,0x51ca,0x51c4,0x51c5,0x51c8,0x51ce,0x5261,0x525a,
+ 0x5252,0x525e,0x525f,0x5255,0x5262,0x52cd,0x530e,0x539e,0x5526,0x54e2,
+ 0x5517,0x5512,0x54e7,0x54f3,0x54e4,0x551a,0x54ff,0x5504,0x5508,0x54eb,
+ 0x5511,0x5505,0x54f1,0x550a,0x54fb,0x54f7,0x54f8,0x54e0,0x550e,0x5503,
+ 0x550b,0x5701,0x5702,0x57cc,0x5832,0x57d5,0x57d2,0x57ba,0x57c6,0x57bd,
+ 0x57bc,0x57b8,0x57b6,0x57bf,0x57c7,0x57d0,0x57b9,0x57c1,0x590e,0x594a,
+ 0x5a19,0x5a16,0x5a2d,0x5a2e,0x5a15,0x5a0f,0x5a17,0x5a0a,0x5a1e,0x5a33,
+ 0x5b6c,0x5ba7,0x5bad,0x5bac,0x5c03,0x5c56,0x5c54,0x5cec,0x5cff,0x5cee,
+ 0x5cf1,0x5cf7,0x5d00,0x5cf9,0x5e29,0x5e28,0x5ea8,0x5eae,0x5eaa,0x5eac,
+ 0x5f33,0x5f30,0x5f67,0x605d
+ },
+ { /* ku 0f */
+ 0x605a,0x6067,0x6041,0x60a2,0x6088,0x6080,0x6092,0x6081,0x609d,0x6083,
+ 0x6095,0x609b,0x6097,0x6087,0x609c,0x608e,0x6219,0x6246,0x62f2,0x6310,
+ 0x6356,0x632c,0x6344,0x6345,0x6336,0x6343,0x63e4,0x6339,0x634b,0x634a,
+ 0x633c,0x6329,0x6341,0x6334,0x6358,0x6354,0x6359,0x632d,0x6347,0x6333,
+ 0x635a,0x6351,0x6338,0x6357,0x6340,0x6348,0x654a,0x6546,0x65c6,0x65c3,
+ 0x65c4,0x65c2,0x664a,0x665f,0x6647,0x6651,0x6712,0x6713,0x681f,0x681a,
+ 0x6849,0x6832,0x6833,0x683b,0x684b,0x684f,0x6816,0x6831,0x681c,0x6835,
+ 0x682b,0x682d,0x682f,0x684e,0x6844,0x6834,0x681d,0x6812,0x6814,0x6826,
+ 0x6828,0x682e,0x684d,0x683a,0x6825,0x6820,0x6b2c,0x6b2f,0x6b2d,0x6b31,
+ 0x6b34,0x6b6d,0x8082,0x6b88
+ },
+ { /* ku 10 */
+ 0x6be6,0x6be4,0x6be8,0x6be3,0x6be2,0x6be7,0x6c25,0x6d7a,0x6d63,0x6d64,
+ 0x6d76,0x6d0d,0x6d61,0x6d92,0x6d58,0x6d62,0x6d6d,0x6d6f,0x6d91,0x6d8d,
+ 0x6def,0x6d7f,0x6d86,0x6d5e,0x6d67,0x6d60,0x6d97,0x6d70,0x6d7c,0x6d5f,
+ 0x6d82,0x6d98,0x6d2f,0x6d68,0x6d8b,0x6d7e,0x6d80,0x6d84,0x6d16,0x6d83,
+ 0x6d7b,0x6d7d,0x6d75,0x6d90,0x70dc,0x70d3,0x70d1,0x70dd,0x70cb,0x7f39,
+ 0x70e2,0x70d7,0x70d2,0x70de,0x70e0,0x70d4,0x70cd,0x70c5,0x70c6,0x70c7,
+ 0x70da,0x70ce,0x70e1,0x7242,0x7278,0x7277,0x7276,0x7300,0x72fa,0x72f4,
+ 0x72fe,0x72f6,0x72f3,0x72fb,0x7301,0x73d3,0x73d9,0x73e5,0x73d6,0x73bc,
+ 0x73e7,0x73e3,0x73e9,0x73dc,0x73d2,0x73db,0x73d4,0x73dd,0x73da,0x73d7,
+ 0x73d8,0x73e8,0x74de,0x74df
+ },
+ { /* ku 11 */
+ 0x74f4,0x74f5,0x7521,0x755b,0x755f,0x75b0,0x75c1,0x75bb,0x75c4,0x75c0,
+ 0x75bf,0x75b6,0x75ba,0x768a,0x76c9,0x771d,0x771b,0x7710,0x7713,0x7712,
+ 0x7723,0x7711,0x7715,0x7719,0x771a,0x7722,0x7727,0x7823,0x782c,0x7822,
+ 0x7835,0x782f,0x7828,0x782e,0x782b,0x7821,0x7829,0x7833,0x782a,0x7831,
+ 0x7954,0x795b,0x794f,0x795c,0x7953,0x7952,0x7951,0x79eb,0x79ec,0x79e0,
+ 0x79ee,0x79ed,0x79ea,0x79dc,0x79de,0x79dd,0x7a86,0x7a89,0x7a85,0x7a8b,
+ 0x7a8c,0x7a8a,0x7a87,0x7ad8,0x7b10,0x7b04,0x7b13,0x7b05,0x7b0f,0x7b08,
+ 0x7b0a,0x7b0e,0x7b09,0x7b12,0x7c84,0x7c91,0x7c8a,0x7c8c,0x7c88,0x7c8d,
+ 0x7c85,0x7d1e,0x7d1d,0x7d11,0x7d0e,0x7d18,0x7d16,0x7d13,0x7d1f,0x7d12,
+ 0x7d0f,0x7d0c,0x7f5c,0x7f61
+ },
+ { /* ku 12 */
+ 0x7f5e,0x7f60,0x7f5d,0x7f5b,0x7f96,0x7f92,0x7fc3,0x7fc2,0x7fc0,0x8016,
+ 0x803e,0x8039,0x80fa,0x80f2,0x80f9,0x80f5,0x8101,0x80fb,0x8100,0x8201,
+ 0x822f,0x8225,0x8333,0x832d,0x8344,0x8319,0x8351,0x8325,0x8356,0x833f,
+ 0x8341,0x8326,0x831c,0x8322,0x8342,0x834e,0x831b,0x832a,0x8308,0x833c,
+ 0x834d,0x8316,0x8324,0x8320,0x8337,0x832f,0x8329,0x8347,0x8345,0x834c,
+ 0x8353,0x831e,0x832c,0x834b,0x8327,0x8348,0x8653,0x8652,0x86a2,0x86a8,
+ 0x8696,0x868d,0x8691,0x869e,0x8687,0x8697,0x8686,0x868b,0x869a,0x8685,
+ 0x86a5,0x8699,0x86a1,0x86a7,0x8695,0x8698,0x868e,0x869d,0x8690,0x8694,
+ 0x8843,0x8844,0x886d,0x8875,0x8876,0x8872,0x8880,0x8871,0x887f,0x886f,
+ 0x8883,0x887e,0x8874,0x887c
+ },
+ { /* ku 13 */
+ 0x8a12,0x8c47,0x8c57,0x8c7b,0x8ca4,0x8ca3,0x8d76,0x8d78,0x8db5,0x8db7,
+ 0x8db6,0x8ed1,0x8ed3,0x8ffe,0x8ff5,0x9002,0x8fff,0x8ffb,0x9004,0x8ffc,
+ 0x8ff6,0x90d6,0x90e0,0x90d9,0x90da,0x90e3,0x90df,0x90e5,0x90d8,0x90db,
+ 0x90d7,0x90dc,0x90e4,0x9150,0x914e,0x914f,0x91d5,0x91e2,0x91da,0x965c,
+ 0x965f,0x96bc,0x98e3,0x9adf,0x9b2f,0x4e7f,0x5070,0x506a,0x5061,0x505e,
+ 0x5060,0x5053,0x504b,0x505d,0x5072,0x5048,0x504d,0x5041,0x505b,0x504a,
+ 0x5062,0x5015,0x5045,0x505f,0x5069,0x506b,0x5063,0x5064,0x5046,0x5040,
+ 0x506e,0x5073,0x5057,0x5051,0x51d0,0x526b,0x526d,0x526c,0x526e,0x52d6,
+ 0x52d3,0x532d,0x539c,0x5575,0x5576,0x553c,0x554d,0x5550,0x5534,0x552a,
+ 0x5551,0x5562,0x5536,0x5535
+ },
+ { /* ku 14 */
+ 0x5530,0x5552,0x5545,0x550c,0x5532,0x5565,0x554e,0x5539,0x5548,0x552d,
+ 0x553b,0x5540,0x554b,0x570a,0x5707,0x57fb,0x5814,0x57e2,0x57f6,0x57dc,
+ 0x57f4,0x5800,0x57ed,0x57fd,0x5808,0x57f8,0x580b,0x57f3,0x57cf,0x5807,
+ 0x57ee,0x57e3,0x57f2,0x57e5,0x57ec,0x57e1,0x580e,0x57fc,0x5810,0x57e7,
+ 0x5801,0x580c,0x57f1,0x57e9,0x57f0,0x580d,0x5804,0x595c,0x5a60,0x5a58,
+ 0x5a55,0x5a67,0x5a5e,0x5a38,0x5a35,0x5a6d,0x5a50,0x5a5f,0x5a65,0x5a6c,
+ 0x5a53,0x5a64,0x5a57,0x5a43,0x5a5d,0x5a52,0x5a44,0x5a5b,0x5a48,0x5a8e,
+ 0x5a3e,0x5a4d,0x5a39,0x5a4c,0x5a70,0x5a69,0x5a47,0x5a51,0x5a56,0x5a42,
+ 0x5a5c,0x5b72,0x5b6e,0x5bc1,0x5bc0,0x5c59,0x5d1e,0x5d0b,0x5d1d,0x5d1a,
+ 0x5d20,0x5d0c,0x5d28,0x5d0d
+ },
+ { /* ku 15 */
+ 0x5d26,0x5d25,0x5d0f,0x5d30,0x5d12,0x5d23,0x5d1f,0x5d2e,0x5e3e,0x5e34,
+ 0x5eb1,0x5eb4,0x5eb9,0x5eb2,0x5eb3,0x5f36,0x5f38,0x5f9b,0x5f96,0x5f9f,
+ 0x608a,0x6090,0x6086,0x60be,0x60b0,0x60ba,0x60d3,0x60d4,0x60cf,0x60e4,
+ 0x60d9,0x60dd,0x60c8,0x60b1,0x60db,0x60b7,0x60ca,0x60bf,0x60c3,0x60cd,
+ 0x60c0,0x6332,0x6365,0x638a,0x6382,0x637d,0x63bd,0x639e,0x63ad,0x639d,
+ 0x6397,0x63ab,0x638e,0x636f,0x6387,0x6390,0x636e,0x63af,0x6375,0x639c,
+ 0x636d,0x63ae,0x637c,0x63a4,0x633b,0x639f,0x6378,0x6385,0x6381,0x6391,
+ 0x638d,0x6370,0x6553,0x65cd,0x6665,0x6661,0x665b,0x6659,0x665c,0x6662,
+ 0x6718,0x6879,0x6887,0x6890,0x689c,0x686d,0x686e,0x68ae,0x68ab,0x6956,
+ 0x686f,0x68a3,0x68ac,0x68a9
+ },
+ { /* ku 16 */
+ 0x6875,0x6874,0x68b2,0x688f,0x6877,0x6892,0x687c,0x686b,0x6872,0x68aa,
+ 0x6880,0x6871,0x687e,0x689b,0x6896,0x688b,0x68a0,0x6889,0x68a4,0x6878,
+ 0x687b,0x6891,0x688c,0x688a,0x687d,0x6b36,0x6b33,0x6b37,0x6b38,0x6b91,
+ 0x6b8f,0x6b8d,0x6b8e,0x6b8c,0x6c2a,0x6dc0,0x6dab,0x6db4,0x6db3,0x6e74,
+ 0x6dac,0x6de9,0x6de2,0x6db7,0x6df6,0x6dd4,0x6e00,0x6dc8,0x6de0,0x6ddf,
+ 0x6dd6,0x6dbe,0x6de5,0x6ddc,0x6ddd,0x6ddb,0x6df4,0x6dca,0x6dbd,0x6ded,
+ 0x6df0,0x6dba,0x6dd5,0x6dc2,0x6dcf,0x6dc9,0x6dd0,0x6df2,0x6dd3,0x6dfd,
+ 0x6dd7,0x6dcd,0x6de3,0x6dbb,0x70fa,0x710d,0x70f7,0x7117,0x70f4,0x710c,
+ 0x70f0,0x7104,0x70f3,0x7110,0x70fc,0x70ff,0x7106,0x7113,0x7100,0x70f8,
+ 0x70f6,0x710b,0x7102,0x710e
+ },
+ { /* ku 17 */
+ 0x727e,0x727b,0x727c,0x727f,0x731d,0x7317,0x7307,0x7311,0x7318,0x730a,
+ 0x7308,0x72ff,0x730f,0x731e,0x7388,0x73f6,0x73f8,0x73f5,0x7404,0x7401,
+ 0x73fd,0x7407,0x7400,0x73fa,0x73fc,0x73ff,0x740c,0x740b,0x73f4,0x7408,
+ 0x7564,0x7563,0x75ce,0x75d2,0x75cf,0x75cb,0x75cc,0x75d1,0x75d0,0x768f,
+ 0x7689,0x76d3,0x7739,0x772f,0x772d,0x7731,0x7732,0x7734,0x7733,0x773d,
+ 0x7725,0x773b,0x7735,0x7848,0x7852,0x7849,0x784d,0x784a,0x784c,0x7826,
+ 0x7845,0x7850,0x7964,0x7967,0x7969,0x796a,0x7963,0x796b,0x7961,0x79bb,
+ 0x79fa,0x79f8,0x79f6,0x79f7,0x7a8f,0x7a94,0x7a90,0x7b35,0x7b3b,0x7b34,
+ 0x7b25,0x7b30,0x7b22,0x7b24,0x7b33,0x7b18,0x7b2a,0x7b1d,0x7b31,0x7b2b,
+ 0x7b2d,0x7b2f,0x7b32,0x7b38
+ },
+ { /* ku 18 */
+ 0x7b1a,0x7b23,0x7c94,0x7c98,0x7c96,0x7ca3,0x7d35,0x7d3d,0x7d38,0x7d36,
+ 0x7d3a,0x7d45,0x7d2c,0x7d29,0x7d41,0x7d47,0x7d3e,0x7d3f,0x7d4a,0x7d3b,
+ 0x7d28,0x7f63,0x7f95,0x7f9c,0x7f9d,0x7f9b,0x7fca,0x7fcb,0x7fcd,0x7fd0,
+ 0x7fd1,0x7fc7,0x7fcf,0x7fc9,0x801f,0x801e,0x801b,0x8047,0x8043,0x8048,
+ 0x8118,0x8125,0x8119,0x811b,0x812d,0x811f,0x812c,0x811e,0x8121,0x8115,
+ 0x8127,0x811d,0x8122,0x8211,0x8238,0x8233,0x823a,0x8234,0x8232,0x8274,
+ 0x8390,0x83a3,0x83a8,0x838d,0x837a,0x8373,0x83a4,0x8374,0x838f,0x8381,
+ 0x8395,0x8399,0x8375,0x8394,0x83a9,0x837d,0x8383,0x838c,0x839d,0x839b,
+ 0x83aa,0x838b,0x837e,0x83a5,0x83af,0x8388,0x8397,0x83b0,0x837f,0x83a6,
+ 0x8387,0x83ae,0x8376,0x8659
+ },
+ { /* ku 19 */
+ 0x8656,0x86bf,0x86b7,0x86c2,0x86c1,0x86c5,0x86ba,0x86b0,0x86c8,0x86b9,
+ 0x86b3,0x86b8,0x86cc,0x86b4,0x86bb,0x86bc,0x86c3,0x86bd,0x86be,0x8852,
+ 0x8889,0x8895,0x88a8,0x88a2,0x88aa,0x889a,0x8891,0x88a1,0x889f,0x8898,
+ 0x88a7,0x8899,0x889b,0x8897,0x88a4,0x88ac,0x888c,0x8893,0x888e,0x8982,
+ 0x89d6,0x89d9,0x89d5,0x8a30,0x8a27,0x8a2c,0x8a1e,0x8c39,0x8c3b,0x8c5c,
+ 0x8c5d,0x8c7d,0x8ca5,0x8d7d,0x8d7b,0x8d79,0x8dbc,0x8dc2,0x8db9,0x8dbf,
+ 0x8dc1,0x8ed8,0x8ede,0x8edd,0x8edc,0x8ed7,0x8ee0,0x8ee1,0x9024,0x900b,
+ 0x9011,0x901c,0x900c,0x9021,0x90ef,0x90ea,0x90f0,0x90f4,0x90f2,0x90f3,
+ 0x90d4,0x90eb,0x90ec,0x90e9,0x9156,0x9158,0x915a,0x9153,0x9155,0x91ec,
+ 0x91f4,0x91f1,0x91f3,0x91f8
+ },
+ { /* ku 1a */
+ 0x91e4,0x91f9,0x91ea,0x91eb,0x91f7,0x91e8,0x91ee,0x957a,0x9586,0x9588,
+ 0x967c,0x966d,0x966b,0x9671,0x966f,0x96bf,0x976a,0x9804,0x98e5,0x9997,
+ 0x509b,0x5095,0x5094,0x509e,0x508b,0x50a3,0x5083,0x508c,0x508e,0x509d,
+ 0x5068,0x509c,0x5092,0x5082,0x5087,0x515f,0x51d4,0x5312,0x5311,0x53a4,
+ 0x53a7,0x5591,0x55a8,0x55a5,0x55ad,0x5577,0x5645,0x55a2,0x5593,0x5588,
+ 0x558f,0x55b5,0x5581,0x55a3,0x5592,0x55a4,0x557d,0x558c,0x55a6,0x557f,
+ 0x5595,0x55a1,0x558e,0x570c,0x5829,0x5837,0x5819,0x581e,0x5827,0x5823,
+ 0x5828,0x57f5,0x5848,0x5825,0x581c,0x581b,0x5833,0x583f,0x5836,0x582e,
+ 0x5839,0x5838,0x582d,0x582c,0x583b,0x5961,0x5aaf,0x5a94,0x5a9f,0x5a7a,
+ 0x5aa2,0x5a9e,0x5a78,0x5aa6
+ },
+ { /* ku 1b */
+ 0x5a7c,0x5aa5,0x5aac,0x5a95,0x5aae,0x5a37,0x5a84,0x5a8a,0x5a97,0x5a83,
+ 0x5a8b,0x5aa9,0x5a7b,0x5a7d,0x5a8c,0x5a9c,0x5a8f,0x5a93,0x5a9d,0x5bea,
+ 0x5bcd,0x5bcb,0x5bd4,0x5bd1,0x5bca,0x5bce,0x5c0c,0x5c30,0x5d37,0x5d43,
+ 0x5d6b,0x5d41,0x5d4b,0x5d3f,0x5d35,0x5d51,0x5d4e,0x5d55,0x5d33,0x5d3a,
+ 0x5d52,0x5d3d,0x5d31,0x5d59,0x5d42,0x5d39,0x5d49,0x5d38,0x5d3c,0x5d32,
+ 0x5d36,0x5d40,0x5d45,0x5e44,0x5e41,0x5f58,0x5fa6,0x5fa5,0x5fab,0x60c9,
+ 0x60b9,0x60cc,0x60e2,0x60ce,0x60c4,0x6114,0x60f2,0x610a,0x6116,0x6105,
+ 0x60f5,0x6113,0x60f8,0x60fc,0x60fe,0x60c1,0x6103,0x6118,0x611d,0x6110,
+ 0x60ff,0x6104,0x610b,0x624a,0x6394,0x63b1,0x63b0,0x63ce,0x63e5,0x63e8,
+ 0x63ef,0x63c3,0x649d,0x63f3
+ },
+ { /* ku 1c */
+ 0x63ca,0x63e0,0x63f6,0x63d5,0x63f2,0x63f5,0x6461,0x63df,0x63be,0x63dd,
+ 0x63dc,0x63c4,0x63d8,0x63d3,0x63c2,0x63c7,0x63cc,0x63cb,0x63c8,0x63f0,
+ 0x63d7,0x63d9,0x6532,0x6567,0x656a,0x6564,0x655c,0x6568,0x6565,0x658c,
+ 0x659d,0x659e,0x65ae,0x65d0,0x65d2,0x667c,0x666c,0x667b,0x6680,0x6671,
+ 0x6679,0x666a,0x6672,0x6701,0x690c,0x68d3,0x6904,0x68dc,0x692a,0x68ec,
+ 0x68ea,0x68f1,0x690f,0x68d6,0x68f7,0x68eb,0x68e4,0x68f6,0x6913,0x6910,
+ 0x68f3,0x68e1,0x6907,0x68cc,0x6908,0x6970,0x68b4,0x6911,0x68ef,0x68c6,
+ 0x6914,0x68f8,0x68d0,0x68fd,0x68fc,0x68e8,0x690b,0x690a,0x6917,0x68ce,
+ 0x68c8,0x68dd,0x68de,0x68e6,0x68f4,0x68d1,0x6906,0x68d4,0x68e9,0x6915,
+ 0x6925,0x68c7,0x6b39,0x6b3b
+ },
+ { /* ku 1d */
+ 0x6b3f,0x6b3c,0x6b94,0x6b97,0x6b99,0x6b95,0x6bbd,0x6bf0,0x6bf2,0x6bf3,
+ 0x6c30,0x6dfc,0x6e46,0x6e47,0x6e1f,0x6e49,0x6e88,0x6e3c,0x6e3d,0x6e45,
+ 0x6e62,0x6e2b,0x6e3f,0x6e41,0x6e5d,0x6e73,0x6e1c,0x6e33,0x6e4b,0x6e40,
+ 0x6e51,0x6e3b,0x6e03,0x6e2e,0x6e5e,0x6e68,0x6e5c,0x6e61,0x6e31,0x6e28,
+ 0x6e60,0x6e71,0x6e6b,0x6e39,0x6e22,0x6e30,0x6e53,0x6e65,0x6e27,0x6e78,
+ 0x6e64,0x6e77,0x6e55,0x6e79,0x6e52,0x6e66,0x6e35,0x6e36,0x6e5a,0x7120,
+ 0x711e,0x712f,0x70fb,0x712e,0x7131,0x7123,0x7125,0x7122,0x7132,0x711f,
+ 0x7128,0x713a,0x711b,0x724b,0x725a,0x7288,0x7289,0x7286,0x7285,0x728b,
+ 0x7312,0x730b,0x7330,0x7322,0x7331,0x7333,0x7327,0x7332,0x732d,0x7326,
+ 0x7323,0x7335,0x730c,0x742e
+ },
+ { /* ku 1e */
+ 0x742c,0x7430,0x742b,0x7416,0x741a,0x7421,0x742d,0x7431,0x7424,0x7423,
+ 0x741d,0x7429,0x7420,0x7432,0x74fb,0x752f,0x756f,0x756c,0x75e7,0x75da,
+ 0x75e1,0x75e6,0x75dd,0x75df,0x75e4,0x75d7,0x7695,0x7692,0x76da,0x7746,
+ 0x7747,0x7744,0x774d,0x7745,0x774a,0x774e,0x774b,0x774c,0x77de,0x77ec,
+ 0x7860,0x7864,0x7865,0x785c,0x786d,0x7871,0x786a,0x786e,0x7870,0x7869,
+ 0x7868,0x785e,0x7862,0x7974,0x7973,0x7972,0x7970,0x7a02,0x7a0a,0x7a03,
+ 0x7a0c,0x7a04,0x7a99,0x7ae6,0x7ae4,0x7b4a,0x7b47,0x7b44,0x7b48,0x7b4c,
+ 0x7b4e,0x7b40,0x7b58,0x7b45,0x7ca2,0x7c9e,0x7ca8,0x7ca1,0x7d58,0x7d6f,
+ 0x7d63,0x7d53,0x7d56,0x7d67,0x7d6a,0x7d4f,0x7d6d,0x7d5c,0x7d6b,0x7d52,
+ 0x7d54,0x7d69,0x7d51,0x7d5f
+ },
+ { /* ku 1f */
+ 0x7d4e,0x7f3e,0x7f3f,0x7f65,0x7f66,0x7fa2,0x7fa0,0x7fa1,0x7fd7,0x8051,
+ 0x804f,0x8050,0x80fe,0x80d4,0x8143,0x814a,0x8152,0x814f,0x8147,0x813d,
+ 0x814d,0x813a,0x81e6,0x81ee,0x81f7,0x81f8,0x81f9,0x8204,0x823c,0x823d,
+ 0x823f,0x8275,0x833b,0x83cf,0x83f9,0x8423,0x83c0,0x83e8,0x8412,0x83e7,
+ 0x83e4,0x83fc,0x83f6,0x8410,0x83c6,0x83c8,0x83eb,0x83e3,0x83bf,0x8401,
+ 0x83dd,0x83e5,0x83d8,0x83ff,0x83e1,0x83cb,0x83ce,0x83d6,0x83f5,0x83c9,
+ 0x8409,0x840f,0x83de,0x8411,0x8406,0x83c2,0x83f3,0x83d5,0x83fa,0x83c7,
+ 0x83d1,0x83ea,0x8413,0x839a,0x83c3,0x83ec,0x83ee,0x83c4,0x83fb,0x83d7,
+ 0x83e2,0x841b,0x83db,0x83fe,0x86d8,0x86e2,0x86e6,0x86d3,0x86e3,0x86da,
+ 0x86ea,0x86dd,0x86eb,0x86dc
+ },
+ { /* ku 20 */
+ 0x86ec,0x86e9,0x86d7,0x86e8,0x86d1,0x8848,0x8856,0x8855,0x88ba,0x88d7,
+ 0x88b9,0x88b8,0x88c0,0x88be,0x88b6,0x88bc,0x88b7,0x88bd,0x88b2,0x8901,
+ 0x88c9,0x8995,0x8998,0x8997,0x89dd,0x89da,0x89db,0x8a4e,0x8a4d,0x8a39,
+ 0x8a59,0x8a40,0x8a57,0x8a58,0x8a44,0x8a45,0x8a52,0x8a48,0x8a51,0x8a4a,
+ 0x8a4c,0x8a4f,0x8c5f,0x8c81,0x8c80,0x8cba,0x8cbe,0x8cb0,0x8cb9,0x8cb5,
+ 0x8d84,0x8d80,0x8d89,0x8dd8,0x8dd3,0x8dcd,0x8dc7,0x8dd6,0x8ddc,0x8dcf,
+ 0x8dd5,0x8dd9,0x8dc8,0x8dd7,0x8dc5,0x8eef,0x8ef7,0x8efa,0x8ef9,0x8ee6,
+ 0x8eee,0x8ee5,0x8ef5,0x8ee7,0x8ee8,0x8ef6,0x8eeb,0x8ef1,0x8eec,0x8ef4,
+ 0x8ee9,0x902d,0x9034,0x902f,0x9106,0x912c,0x9104,0x90ff,0x90fc,0x9108,
+ 0x90f9,0x90fb,0x9101,0x9100
+ },
+ { /* ku 21 */
+ 0x9107,0x9105,0x9103,0x9161,0x9164,0x915f,0x9162,0x9160,0x9201,0x920a,
+ 0x9225,0x9203,0x921a,0x9226,0x920f,0x920c,0x9200,0x9212,0x91ff,0x91fd,
+ 0x9206,0x9204,0x9227,0x9202,0x921c,0x9224,0x9219,0x9217,0x9205,0x9216,
+ 0x957b,0x958d,0x958c,0x9590,0x9687,0x967e,0x9688,0x9689,0x9683,0x9680,
+ 0x96c2,0x96c8,0x96c3,0x96f1,0x96f0,0x976c,0x9770,0x976e,0x9807,0x98a9,
+ 0x98eb,0x9ce6,0x9ef9,0x4e83,0x4e84,0x4eb6,0x50bd,0x50bf,0x50c6,0x50ae,
+ 0x50c4,0x50ca,0x50b4,0x50c8,0x50c2,0x50b0,0x50c1,0x50ba,0x50b1,0x50cb,
+ 0x50c9,0x50b6,0x50b8,0x51d7,0x527a,0x5278,0x527b,0x527c,0x55c3,0x55db,
+ 0x55cc,0x55d0,0x55cb,0x55ca,0x55dd,0x55c0,0x55d4,0x55c4,0x55e9,0x55bf,
+ 0x55d2,0x558d,0x55cf,0x55d5
+ },
+ { /* ku 22 */
+ 0x55e2,0x55d6,0x55c8,0x55f2,0x55cd,0x55d9,0x55c2,0x5714,0x5853,0x5868,
+ 0x5864,0x584f,0x584d,0x5849,0x586f,0x5855,0x584e,0x585d,0x5859,0x5865,
+ 0x585b,0x583d,0x5863,0x5871,0x58fc,0x5ac7,0x5ac4,0x5acb,0x5aba,0x5ab8,
+ 0x5ab1,0x5ab5,0x5ab0,0x5abf,0x5ac8,0x5abb,0x5ac6,0x5ab7,0x5ac0,0x5aca,
+ 0x5ab4,0x5ab6,0x5acd,0x5ab9,0x5a90,0x5bd6,0x5bd8,0x5bd9,0x5c1f,0x5c33,
+ 0x5d71,0x5d63,0x5d4a,0x5d65,0x5d72,0x5d6c,0x5d5e,0x5d68,0x5d67,0x5d62,
+ 0x5df0,0x5e4f,0x5e4e,0x5e4a,0x5e4d,0x5e4b,0x5ec5,0x5ecc,0x5ec6,0x5ecb,
+ 0x5ec7,0x5f40,0x5faf,0x5fad,0x60f7,0x6149,0x614a,0x612b,0x6145,0x6136,
+ 0x6132,0x612e,0x6146,0x612f,0x614f,0x6129,0x6140,0x6220,0x9168,0x6223,
+ 0x6225,0x6224,0x63c5,0x63f1
+ },
+ { /* ku 23 */
+ 0x63eb,0x6410,0x6412,0x6409,0x6420,0x6424,0x6433,0x6443,0x641f,0x6415,
+ 0x6418,0x6439,0x6437,0x6422,0x6423,0x640c,0x6426,0x6430,0x6428,0x6441,
+ 0x6435,0x642f,0x640a,0x641a,0x6440,0x6425,0x6427,0x640b,0x63e7,0x641b,
+ 0x642e,0x6421,0x640e,0x656f,0x6592,0x65d3,0x6686,0x668c,0x6695,0x6690,
+ 0x668b,0x668a,0x6699,0x6694,0x6678,0x6720,0x6966,0x695f,0x6938,0x694e,
+ 0x6962,0x6971,0x693f,0x6945,0x696a,0x6939,0x6942,0x6957,0x6959,0x697a,
+ 0x6948,0x6949,0x6935,0x696c,0x6933,0x693d,0x6965,0x68f0,0x6978,0x6934,
+ 0x6969,0x6940,0x696f,0x6944,0x6976,0x6958,0x6941,0x6974,0x694c,0x693b,
+ 0x694b,0x6937,0x695c,0x694f,0x6951,0x6932,0x6952,0x692f,0x697b,0x693c,
+ 0x6b46,0x6b45,0x6b43,0x6b42
+ },
+ { /* ku 24 */
+ 0x6b48,0x6b41,0x6b9b,0x6bfb,0x6bfc,0x6bf9,0x6bf7,0x6bf8,0x6e9b,0x6ed6,
+ 0x6ec8,0x6e8f,0x6ec0,0x6e9f,0x6e93,0x6e94,0x6ea0,0x6eb1,0x6eb9,0x6ec6,
+ 0x6ed2,0x6ebd,0x6ec1,0x6e9e,0x6ec9,0x6eb7,0x6eb0,0x6ecd,0x6ea6,0x6ecf,
+ 0x6eb2,0x6ebe,0x6ec3,0x6edc,0x6ed8,0x6e99,0x6e92,0x6e8e,0x6e8d,0x6ea4,
+ 0x6ea1,0x6ebf,0x6eb3,0x6ed0,0x6eca,0x6e97,0x6eae,0x6ea3,0x7147,0x7154,
+ 0x7152,0x7163,0x7160,0x7141,0x715d,0x7162,0x7172,0x7178,0x716a,0x7161,
+ 0x7142,0x7158,0x7143,0x714b,0x7170,0x715f,0x7150,0x7153,0x7144,0x714d,
+ 0x715a,0x724f,0x728d,0x728c,0x7291,0x7290,0x728e,0x733c,0x7342,0x733b,
+ 0x733a,0x7340,0x734a,0x7349,0x7444,0x744a,0x744b,0x7452,0x7451,0x7457,
+ 0x7440,0x744f,0x7450,0x744e
+ },
+ { /* ku 25 */
+ 0x7442,0x7446,0x744d,0x7454,0x74e1,0x74ff,0x74fe,0x74fd,0x751d,0x7579,
+ 0x7577,0x6983,0x75ef,0x760f,0x7603,0x75f7,0x75fe,0x75fc,0x75f9,0x75f8,
+ 0x7610,0x75fb,0x75f6,0x75ed,0x75f5,0x75fd,0x7699,0x76b5,0x76dd,0x7755,
+ 0x775f,0x7760,0x7752,0x7756,0x775a,0x7769,0x7767,0x7754,0x7759,0x776d,
+ 0x77e0,0x7887,0x789a,0x7894,0x788f,0x7884,0x7895,0x7885,0x7886,0x78a1,
+ 0x7883,0x7879,0x7899,0x7880,0x7896,0x787b,0x797c,0x7982,0x797d,0x7979,
+ 0x7a11,0x7a18,0x7a19,0x7a12,0x7a17,0x7a15,0x7a22,0x7a13,0x7a1b,0x7a10,
+ 0x7aa3,0x7aa2,0x7a9e,0x7aeb,0x7b66,0x7b64,0x7b6d,0x7b74,0x7b69,0x7b72,
+ 0x7b65,0x7b73,0x7b71,0x7b70,0x7b61,0x7b78,0x7b76,0x7b63,0x7cb2,0x7cb4,
+ 0x7caf,0x7d88,0x7d86,0x7d80
+ },
+ { /* ku 26 */
+ 0x7d8d,0x7d7f,0x7d85,0x7d7a,0x7d8e,0x7d7b,0x7d83,0x7d7c,0x7d8c,0x7d94,
+ 0x7d84,0x7d7d,0x7d92,0x7f6d,0x7f6b,0x7f67,0x7f68,0x7f6c,0x7fa6,0x7fa5,
+ 0x7fa7,0x7fdb,0x7fdc,0x8021,0x8164,0x8160,0x8177,0x815c,0x8169,0x815b,
+ 0x8162,0x8172,0x6721,0x815e,0x8176,0x8167,0x816f,0x8144,0x8161,0x821d,
+ 0x8249,0x8244,0x8240,0x8242,0x8245,0x84f1,0x843f,0x8456,0x8476,0x8479,
+ 0x848f,0x848d,0x8465,0x8451,0x8440,0x8486,0x8467,0x8430,0x844d,0x847d,
+ 0x845a,0x8459,0x8474,0x8473,0x845d,0x8507,0x845e,0x8437,0x843a,0x8434,
+ 0x847a,0x8443,0x8478,0x8432,0x8445,0x8429,0x83d9,0x844b,0x842f,0x8442,
+ 0x842d,0x845f,0x8470,0x8439,0x844e,0x844c,0x8452,0x846f,0x84c5,0x848e,
+ 0x843b,0x8447,0x8436,0x8433
+ },
+ { /* ku 27 */
+ 0x8468,0x847e,0x8444,0x842b,0x8460,0x8454,0x846e,0x8450,0x870b,0x8704,
+ 0x86f7,0x870c,0x86fa,0x86d6,0x86f5,0x874d,0x86f8,0x870e,0x8709,0x8701,
+ 0x86f6,0x870d,0x8705,0x88d6,0x88cb,0x88cd,0x88ce,0x88de,0x88db,0x88da,
+ 0x88cc,0x88d0,0x8985,0x899b,0x89df,0x89e5,0x89e4,0x89e1,0x89e0,0x89e2,
+ 0x89dc,0x89e6,0x8a76,0x8a86,0x8a7f,0x8a61,0x8a3f,0x8a77,0x8a82,0x8a84,
+ 0x8a75,0x8a83,0x8a81,0x8a74,0x8a7a,0x8c3c,0x8c4b,0x8c4a,0x8c65,0x8c64,
+ 0x8c66,0x8c86,0x8c84,0x8c85,0x8ccc,0x8d68,0x8d69,0x8d91,0x8d8c,0x8d8e,
+ 0x8d8f,0x8d8d,0x8d93,0x8d94,0x8d90,0x8d92,0x8df0,0x8de0,0x8dec,0x8df1,
+ 0x8dee,0x8dd0,0x8de9,0x8de3,0x8de2,0x8de7,0x8df2,0x8deb,0x8df4,0x8f06,
+ 0x8eff,0x8f01,0x8f00,0x8f05
+ },
+ { /* ku 28 */
+ 0x8f07,0x8f08,0x8f02,0x8f0b,0x9052,0x903f,0x9044,0x9049,0x903d,0x9110,
+ 0x910d,0x910f,0x9111,0x9116,0x9114,0x910b,0x910e,0x916e,0x916f,0x9248,
+ 0x9252,0x9230,0x923a,0x9266,0x9233,0x9265,0x925e,0x9283,0x922e,0x924a,
+ 0x9246,0x926d,0x926c,0x924f,0x9260,0x9267,0x926f,0x9236,0x9261,0x9270,
+ 0x9231,0x9254,0x9263,0x9250,0x9272,0x924e,0x9253,0x924c,0x9256,0x9232,
+ 0x959f,0x959c,0x959e,0x959b,0x9692,0x9693,0x9691,0x9697,0x96ce,0x96fa,
+ 0x96fd,0x96f8,0x96f5,0x9773,0x9777,0x9778,0x9772,0x980f,0x980d,0x980e,
+ 0x98ac,0x98f6,0x98f9,0x99af,0x99b2,0x99b0,0x99b5,0x9aad,0x9aab,0x9b5b,
+ 0x9cea,0x9ced,0x9ce7,0x9e80,0x9efd,0x50e6,0x50d4,0x50d7,0x50e8,0x50f3,
+ 0x50db,0x50ea,0x50dd,0x50e4
+ },
+ { /* ku 29 */
+ 0x50d3,0x50ec,0x50f0,0x50ef,0x50e3,0x50e0,0x51d8,0x5280,0x5281,0x52e9,
+ 0x52eb,0x5330,0x53ac,0x5627,0x5615,0x560c,0x5612,0x55fc,0x560f,0x561c,
+ 0x5601,0x5613,0x5602,0x55fa,0x561d,0x5604,0x55ff,0x55f9,0x5889,0x587c,
+ 0x5890,0x5898,0x5886,0x5881,0x587f,0x5874,0x588b,0x587a,0x5887,0x5891,
+ 0x588e,0x5876,0x5882,0x5888,0x587b,0x5894,0x588f,0x58fe,0x596b,0x5adc,
+ 0x5aee,0x5ae5,0x5ad5,0x5aea,0x5ada,0x5aed,0x5aeb,0x5af3,0x5ae2,0x5ae0,
+ 0x5adb,0x5aec,0x5ade,0x5add,0x5ad9,0x5ae8,0x5adf,0x5b77,0x5be0,0x5be3,
+ 0x5c63,0x5d82,0x5d80,0x5d7d,0x5d86,0x5d7a,0x5d81,0x5d77,0x5d8a,0x5d89,
+ 0x5d88,0x5d7e,0x5d7c,0x5d8d,0x5d79,0x5d7f,0x5e58,0x5e59,0x5e53,0x5ed8,
+ 0x5ed1,0x5ed7,0x5ece,0x5edc
+ },
+ { /* ku 2a */
+ 0x5ed5,0x5ed9,0x5ed2,0x5ed4,0x5f44,0x5f43,0x5f6f,0x5fb6,0x612c,0x6128,
+ 0x6141,0x615e,0x6171,0x6173,0x6152,0x6153,0x6172,0x616c,0x6180,0x6174,
+ 0x6154,0x617a,0x615b,0x6165,0x613b,0x616a,0x6161,0x6156,0x6229,0x6227,
+ 0x622b,0x642b,0x644d,0x645b,0x645d,0x6474,0x6476,0x6472,0x6473,0x647d,
+ 0x6475,0x6466,0x64a6,0x644e,0x6482,0x645e,0x645c,0x644b,0x6453,0x6460,
+ 0x6450,0x647f,0x643f,0x646c,0x646b,0x6459,0x6465,0x6477,0x6573,0x65a0,
+ 0x66a1,0x66a0,0x669f,0x6705,0x6704,0x6722,0x69b1,0x69b6,0x69c9,0x69a0,
+ 0x69ce,0x6996,0x69b0,0x69ac,0x69bc,0x6991,0x6999,0x698e,0x69a7,0x698d,
+ 0x69a9,0x69be,0x69af,0x69bf,0x69c4,0x69bd,0x69a4,0x69d4,0x69b9,0x69ca,
+ 0x699a,0x69cf,0x69b3,0x6993
+ },
+ { /* ku 2b */
+ 0x69aa,0x69a1,0x699e,0x69d9,0x6997,0x6990,0x69c2,0x69b5,0x69a5,0x69c6,
+ 0x6b4a,0x6b4d,0x6b4b,0x6b9e,0x6b9f,0x6ba0,0x6bc3,0x6bc4,0x6bfe,0x6ece,
+ 0x6ef5,0x6ef1,0x6f03,0x6f25,0x6ef8,0x6f37,0x6efb,0x6f2e,0x6f09,0x6f4e,
+ 0x6f19,0x6f1a,0x6f27,0x6f18,0x6f3b,0x6f12,0x6eed,0x6f0a,0x6f36,0x6f73,
+ 0x6ef9,0x6eee,0x6f2d,0x6f40,0x6f30,0x6f3c,0x6f35,0x6eeb,0x6f07,0x6f0e,
+ 0x6f43,0x6f05,0x6efd,0x6ef6,0x6f39,0x6f1c,0x6efc,0x6f3a,0x6f1f,0x6f0d,
+ 0x6f1e,0x6f08,0x6f21,0x7187,0x7190,0x7189,0x7180,0x7185,0x7182,0x718f,
+ 0x717b,0x7186,0x7181,0x7197,0x7244,0x7253,0x7297,0x7295,0x7293,0x7343,
+ 0x734d,0x7351,0x734c,0x7462,0x7473,0x7471,0x7475,0x7472,0x7467,0x746e,
+ 0x7500,0x7502,0x7503,0x757d
+ },
+ { /* ku 2c */
+ 0x7590,0x7616,0x7608,0x760c,0x7615,0x7611,0x760a,0x7614,0x76b8,0x7781,
+ 0x777c,0x7785,0x7782,0x776e,0x7780,0x776f,0x777e,0x7783,0x78b2,0x78aa,
+ 0x78b4,0x78ad,0x78a8,0x787e,0x78ab,0x789e,0x78a5,0x78a0,0x78ac,0x78a2,
+ 0x78a4,0x7998,0x798a,0x798b,0x7996,0x7995,0x7994,0x7993,0x7997,0x7988,
+ 0x7992,0x7990,0x7a2b,0x7a4a,0x7a30,0x7a2f,0x7a28,0x7a26,0x7aa8,0x7aab,
+ 0x7aac,0x7aee,0x7b88,0x7b9c,0x7b8a,0x7b91,0x7b90,0x7b96,0x7b8d,0x7b8c,
+ 0x7b9b,0x7b8e,0x7b85,0x7b98,0x5284,0x7b99,0x7ba4,0x7b82,0x7cbb,0x7cbf,
+ 0x7cbc,0x7cba,0x7da7,0x7db7,0x7dc2,0x7da3,0x7daa,0x7dc1,0x7dc0,0x7dc5,
+ 0x7d9d,0x7dce,0x7dc4,0x7dc6,0x7dcb,0x7dcc,0x7daf,0x7db9,0x7d96,0x7dbc,
+ 0x7d9f,0x7da6,0x7dae,0x7da9
+ },
+ { /* ku 2d */
+ 0x7da1,0x7dc9,0x7f73,0x7fe2,0x7fe3,0x7fe5,0x7fde,0x8024,0x805d,0x805c,
+ 0x8189,0x8186,0x8183,0x8187,0x818d,0x818c,0x818b,0x8215,0x8497,0x84a4,
+ 0x84a1,0x849f,0x84ba,0x84ce,0x84c2,0x84ac,0x84ae,0x84ab,0x84b9,0x84b4,
+ 0x84c1,0x84cd,0x84aa,0x849a,0x84b1,0x84d0,0x849d,0x84a7,0x84bb,0x84a2,
+ 0x8494,0x84c7,0x84cc,0x849b,0x84a9,0x84af,0x84a8,0x84d6,0x8498,0x84b6,
+ 0x84cf,0x84a0,0x84d7,0x84d4,0x84d2,0x84db,0x84b0,0x8491,0x8661,0x8733,
+ 0x8723,0x8728,0x876b,0x8740,0x872e,0x871e,0x8721,0x8719,0x871b,0x8743,
+ 0x872c,0x8741,0x873e,0x8746,0x8720,0x8732,0x872a,0x872d,0x873c,0x8712,
+ 0x873a,0x8731,0x8735,0x8742,0x8726,0x8727,0x8738,0x8724,0x871a,0x8730,
+ 0x8711,0x88f7,0x88e7,0x88f1
+ },
+ { /* ku 2e */
+ 0x88f2,0x88fa,0x88fe,0x88ee,0x88fc,0x88f6,0x88fb,0x88f0,0x88ec,0x88eb,
+ 0x899d,0x89a1,0x899f,0x899e,0x89e9,0x89eb,0x89e8,0x8aab,0x8a99,0x8a8b,
+ 0x8a92,0x8a8f,0x8a96,0x8c3d,0x8c68,0x8c69,0x8cd5,0x8ccf,0x8cd7,0x8d96,
+ 0x8e09,0x8e02,0x8dff,0x8e0d,0x8dfd,0x8e0a,0x8e03,0x8e07,0x8e06,0x8e05,
+ 0x8dfe,0x8e00,0x8e04,0x8f10,0x8f11,0x8f0e,0x8f0d,0x9123,0x911c,0x9120,
+ 0x9122,0x911f,0x911d,0x911a,0x9124,0x9121,0x911b,0x917a,0x9172,0x9179,
+ 0x9173,0x92a5,0x92a4,0x9276,0x929b,0x927a,0x92a0,0x9294,0x92aa,0x928d,
+ 0x92a6,0x929a,0x92ab,0x9279,0x9297,0x927f,0x92a3,0x92ee,0x928e,0x9282,
+ 0x9295,0x92a2,0x927d,0x9288,0x92a1,0x928a,0x9286,0x928c,0x9299,0x92a7,
+ 0x927e,0x9287,0x92a9,0x929d
+ },
+ { /* ku 2f */
+ 0x928b,0x922d,0x969e,0x96a1,0x96ff,0x9758,0x977d,0x977a,0x977e,0x9783,
+ 0x9780,0x9782,0x977b,0x9784,0x9781,0x977f,0x97ce,0x97cd,0x9816,0x98ad,
+ 0x98ae,0x9902,0x9900,0x9907,0x999d,0x999c,0x99c3,0x99b9,0x99bb,0x99ba,
+ 0x99c2,0x99bd,0x99c7,0x9ab1,0x9ae3,0x9ae7,0x9b3e,0x9b3f,0x9b60,0x9b61,
+ 0x9b5f,0x9cf1,0x9cf2,0x9cf5,0x9ea7,0x50ff,0x5103,0x5130,0x50f8,0x5106,
+ 0x5107,0x50f6,0x50fe,0x510b,0x510c,0x50fd,0x510a,0x528b,0x528c,0x52f1,
+ 0x52ef,0x5648,0x5642,0x564c,0x5635,0x5641,0x564a,0x5649,0x5646,0x5658,
+ 0x565a,0x5640,0x5633,0x563d,0x562c,0x563e,0x5638,0x562a,0x563a,0x571a,
+ 0x58ab,0x589d,0x58b1,0x58a0,0x58a3,0x58af,0x58ac,0x58a5,0x58a1,0x58ff,
+ 0x5aff,0x5af4,0x5afd,0x5af7
+ },
+ { /* ku 30 */
+ 0x5af6,0x5b03,0x5af8,0x5b02,0x5af9,0x5b01,0x5b07,0x5b05,0x5b0f,0x5c67,
+ 0x5d99,0x5d97,0x5d9f,0x5d92,0x5da2,0x5d93,0x5d95,0x5da0,0x5d9c,0x5da1,
+ 0x5d9a,0x5d9e,0x5e69,0x5e5d,0x5e60,0x5e5c,0x7df3,0x5edb,0x5ede,0x5ee1,
+ 0x5f49,0x5fb2,0x618b,0x6183,0x6179,0x61b1,0x61b0,0x61a2,0x6189,0x619b,
+ 0x6193,0x61af,0x61ad,0x619f,0x6192,0x61aa,0x61a1,0x618d,0x6166,0x61b3,
+ 0x622d,0x646e,0x6470,0x6496,0x64a0,0x6485,0x6497,0x649c,0x648f,0x648b,
+ 0x648a,0x648c,0x64a3,0x649f,0x6468,0x64b1,0x6498,0x6576,0x657a,0x6579,
+ 0x657b,0x65b2,0x65b3,0x66b5,0x66b0,0x66a9,0x66b2,0x66b7,0x66aa,0x66af,
+ 0x6a00,0x6a06,0x6a17,0x69e5,0x69f8,0x6a15,0x69f1,0x69e4,0x6a20,0x69ff,
+ 0x69ec,0x69e2,0x6a1b,0x6a1d
+ },
+ { /* ku 31 */
+ 0x69fe,0x6a27,0x69f2,0x69ee,0x6a14,0x69f7,0x69e7,0x6a40,0x6a08,0x69e6,
+ 0x69fb,0x6a0d,0x69fc,0x69eb,0x6a09,0x6a04,0x6a18,0x6a25,0x6a0f,0x69f6,
+ 0x6a26,0x6a07,0x69f4,0x6a16,0x6b51,0x6ba5,0x6ba3,0x6ba2,0x6ba6,0x6c01,
+ 0x6c00,0x6bff,0x6c02,0x6f41,0x6f26,0x6f7e,0x6f87,0x6fc6,0x6f92,0x6f8d,
+ 0x6f89,0x6f8c,0x6f62,0x6f4f,0x6f85,0x6f5a,0x6f96,0x6f76,0x6f6c,0x6f82,
+ 0x6f55,0x6f72,0x6f52,0x6f50,0x6f57,0x6f94,0x6f93,0x6f5d,0x6f00,0x6f61,
+ 0x6f6b,0x6f7d,0x6f67,0x6f90,0x6f53,0x6f8b,0x6f69,0x6f7f,0x6f95,0x6f63,
+ 0x6f77,0x6f6a,0x6f7b,0x71b2,0x71af,0x719b,0x71b0,0x71a0,0x719a,0x71a9,
+ 0x71b5,0x719d,0x71a5,0x719e,0x71a4,0x71a1,0x71aa,0x719c,0x71a7,0x71b3,
+ 0x7298,0x729a,0x7358,0x7352
+ },
+ { /* ku 32 */
+ 0x735e,0x735f,0x7360,0x735d,0x735b,0x7361,0x735a,0x7359,0x7362,0x7487,
+ 0x7489,0x748a,0x7486,0x7481,0x747d,0x7485,0x7488,0x747c,0x7479,0x7508,
+ 0x7507,0x757e,0x7625,0x761e,0x7619,0x761d,0x761c,0x7623,0x761a,0x7628,
+ 0x761b,0x769c,0x769d,0x769e,0x769b,0x778d,0x778f,0x7789,0x7788,0x78cd,
+ 0x78bb,0x78cf,0x78cc,0x78d1,0x78ce,0x78d4,0x78c8,0x78c3,0x78c4,0x78c9,
+ 0x799a,0x79a1,0x79a0,0x799c,0x79a2,0x799b,0x6b76,0x7a39,0x7ab2,0x7ab4,
+ 0x7ab3,0x7bb7,0x7bcb,0x7bbe,0x7bac,0x7bce,0x7baf,0x7bb9,0x7bca,0x7bb5,
+ 0x7cc5,0x7cc8,0x7ccc,0x7ccb,0x7df7,0x7ddb,0x7dea,0x7de7,0x7dd7,0x7de1,
+ 0x7e03,0x7dfa,0x7de6,0x7df6,0x7df1,0x7df0,0x7dee,0x7ddf,0x7f76,0x7fac,
+ 0x7fb0,0x7fad,0x7fed,0x7feb
+ },
+ { /* ku 33 */
+ 0x7fea,0x7fec,0x7fe6,0x7fe8,0x8064,0x8067,0x81a3,0x819f,0x819e,0x8195,
+ 0x81a2,0x8199,0x8197,0x8216,0x824f,0x8253,0x8252,0x8250,0x824e,0x8251,
+ 0x8524,0x853b,0x850f,0x8500,0x8529,0x850e,0x8509,0x850d,0x851f,0x850a,
+ 0x8527,0x851c,0x84fb,0x852b,0x84fa,0x8508,0x850c,0x84f4,0x852a,0x84f2,
+ 0x8515,0x84f7,0x84eb,0x84f3,0x84fc,0x8512,0x84ea,0x84e9,0x8516,0x84fe,
+ 0x8528,0x851d,0x852e,0x8502,0x84fd,0x851e,0x84f6,0x8531,0x8526,0x84e7,
+ 0x84e8,0x84f0,0x84ef,0x84f9,0x8518,0x8520,0x8530,0x850b,0x8519,0x852f,
+ 0x8662,0x8756,0x8763,0x8764,0x8777,0x87e1,0x8773,0x8758,0x8754,0x875b,
+ 0x8752,0x8761,0x875a,0x8751,0x875e,0x876d,0x876a,0x8750,0x874e,0x875f,
+ 0x875d,0x876f,0x876c,0x877a
+ },
+ { /* ku 34 */
+ 0x876e,0x875c,0x8765,0x874f,0x877b,0x8775,0x8762,0x8767,0x8769,0x885a,
+ 0x8905,0x890c,0x8914,0x890b,0x8917,0x8918,0x8919,0x8906,0x8916,0x8911,
+ 0x890e,0x8909,0x89a2,0x89a4,0x89a3,0x89ed,0x89f0,0x89ec,0x8acf,0x8ac6,
+ 0x8ab8,0x8ad3,0x8ad1,0x8ad4,0x8ad5,0x8abb,0x8ad7,0x8abe,0x8ac0,0x8ac5,
+ 0x8ad8,0x8ac3,0x8aba,0x8abd,0x8ad9,0x8c3e,0x8c4d,0x8c8f,0x8ce5,0x8cdf,
+ 0x8cd9,0x8ce8,0x8cda,0x8cdd,0x8ce7,0x8da0,0x8d9c,0x8da1,0x8d9b,0x8e20,
+ 0x8e23,0x8e25,0x8e24,0x8e2e,0x8e15,0x8e1b,0x8e16,0x8e11,0x8e19,0x8e26,
+ 0x8e27,0x8e14,0x8e12,0x8e18,0x8e13,0x8e1c,0x8e17,0x8e1a,0x8f2c,0x8f24,
+ 0x8f18,0x8f1a,0x8f20,0x8f23,0x8f16,0x8f17,0x9073,0x9070,0x906f,0x9067,
+ 0x906b,0x912f,0x912b,0x9129
+ },
+ { /* ku 35 */
+ 0x912a,0x9132,0x9126,0x912e,0x9185,0x9186,0x918a,0x9181,0x9182,0x9184,
+ 0x9180,0x92d0,0x92c3,0x92c4,0x92c0,0x92d9,0x92b6,0x92cf,0x92f1,0x92df,
+ 0x92d8,0x92e9,0x92d7,0x92dd,0x92cc,0x92ef,0x92c2,0x92e8,0x92ca,0x92c8,
+ 0x92ce,0x92e6,0x92cd,0x92d5,0x92c9,0x92e0,0x92de,0x92e7,0x92d1,0x92d3,
+ 0x92b5,0x92e1,0x9325,0x92c6,0x92b4,0x957c,0x95ac,0x95ab,0x95ae,0x95b0,
+ 0x96a4,0x96a2,0x96d3,0x9705,0x9708,0x9702,0x975a,0x978a,0x978e,0x9788,
+ 0x97d0,0x97cf,0x981e,0x981d,0x9826,0x9829,0x9828,0x9820,0x981b,0x9827,
+ 0x98b2,0x9908,0x98fa,0x9911,0x9914,0x9916,0x9917,0x9915,0x99dc,0x99cd,
+ 0x99cf,0x99d3,0x99d4,0x99ce,0x99c9,0x99d6,0x99d8,0x99cb,0x99d7,0x99cc,
+ 0x9ab3,0x9aec,0x9aeb,0x9af3
+ },
+ { /* ku 36 */
+ 0x9af2,0x9af1,0x9b46,0x9b43,0x9b67,0x9b74,0x9b71,0x9b66,0x9b76,0x9b75,
+ 0x9b70,0x9b68,0x9b64,0x9b6c,0x9cfc,0x9cfa,0x9cfd,0x9cff,0x9cf7,0x9d07,
+ 0x9d00,0x9cf9,0x9cfb,0x9d08,0x9d05,0x9d04,0x9e83,0x9ed3,0x9f0f,0x9f10,
+ 0x511c,0x5113,0x5117,0x511a,0x5111,0x51de,0x5334,0x53e1,0x5670,0x5660,
+ 0x566e,0x5673,0x5666,0x5663,0x566d,0x5672,0x565e,0x5677,0x571c,0x571b,
+ 0x58c8,0x58bd,0x58c9,0x58bf,0x58ba,0x58c2,0x58bc,0x58c6,0x5b17,0x5b19,
+ 0x5b1b,0x5b21,0x5b14,0x5b13,0x5b10,0x5b16,0x5b28,0x5b1a,0x5b20,0x5b1e,
+ 0x5bef,0x5dac,0x5db1,0x5da9,0x5da7,0x5db5,0x5db0,0x5dae,0x5daa,0x5da8,
+ 0x5db2,0x5dad,0x5daf,0x5db4,0x5e67,0x5e68,0x5e66,0x5e6f,0x5ee9,0x5ee7,
+ 0x5ee6,0x5ee8,0x5ee5,0x5f4b
+ },
+ { /* ku 37 */
+ 0x5fbc,0x5fbb,0x619d,0x61a8,0x6196,0x61c5,0x61b4,0x61c6,0x61c1,0x61cc,
+ 0x61ba,0x61bf,0x61b8,0x618c,0x64d7,0x64d6,0x64d0,0x64cf,0x64c9,0x64bd,
+ 0x6489,0x64c3,0x64db,0x64f3,0x64d9,0x6533,0x657f,0x657c,0x65a2,0x66c8,
+ 0x66be,0x66c0,0x66ca,0x66cb,0x66cf,0x66bd,0x66bb,0x66ba,0x66cc,0x6723,
+ 0x6a34,0x6a66,0x6a49,0x6a67,0x6a32,0x6a68,0x6a3e,0x6a5d,0x6a6d,0x6a76,
+ 0x6a5b,0x6a51,0x6a28,0x6a5a,0x6a3b,0x6a3f,0x6a41,0x6a6a,0x6a64,0x6a50,
+ 0x6a4f,0x6a54,0x6a6f,0x6a69,0x6a60,0x6a3c,0x6a5e,0x6a56,0x6a55,0x6a4d,
+ 0x6a4e,0x6a46,0x6b55,0x6b54,0x6b56,0x6ba7,0x6baa,0x6bab,0x6bc8,0x6bc7,
+ 0x6c04,0x6c03,0x6c06,0x6fad,0x6fcb,0x6fa3,0x6fc7,0x6fbc,0x6fce,0x6fc8,
+ 0x6f5e,0x6fc4,0x6fbd,0x6f9e
+ },
+ { /* ku 38 */
+ 0x6fca,0x6fa8,0x7004,0x6fa5,0x6fae,0x6fba,0x6fac,0x6faa,0x6fcf,0x6fbf,
+ 0x6fb8,0x6fa2,0x6fc9,0x6fab,0x6fcd,0x6faf,0x6fb2,0x6fb0,0x71c5,0x71c2,
+ 0x71bf,0x71b8,0x71d6,0x71c0,0x71c1,0x71cb,0x71d4,0x71ca,0x71c7,0x71cf,
+ 0x71bd,0x71d8,0x71bc,0x71c6,0x71da,0x71db,0x729d,0x729e,0x7369,0x7366,
+ 0x7367,0x736c,0x7365,0x736b,0x736a,0x747f,0x749a,0x74a0,0x7494,0x7492,
+ 0x7495,0x74a1,0x750b,0x7580,0x762f,0x762d,0x7631,0x763d,0x7633,0x763c,
+ 0x7635,0x7632,0x7630,0x76bb,0x76e6,0x779a,0x779d,0x77a1,0x779c,0x779b,
+ 0x77a2,0x77a3,0x7795,0x7799,0x7797,0x78dd,0x78e9,0x78e5,0x78ea,0x78de,
+ 0x78e3,0x78db,0x78e1,0x78e2,0x78ed,0x78df,0x78e0,0x79a4,0x7a44,0x7a48,
+ 0x7a47,0x7ab6,0x7ab8,0x7ab5
+ },
+ { /* ku 39 */
+ 0x7ab1,0x7ab7,0x7bde,0x7be3,0x7be7,0x7bdd,0x7bd5,0x7be5,0x7bda,0x7be8,
+ 0x7bf9,0x7bd4,0x7bea,0x7be2,0x7bdc,0x7beb,0x7bd8,0x7bdf,0x7cd2,0x7cd4,
+ 0x7cd7,0x7cd0,0x7cd1,0x7e12,0x7e21,0x7e17,0x7e0c,0x7e1f,0x7e20,0x7e13,
+ 0x7e0e,0x7e1c,0x7e15,0x7e1a,0x7e22,0x7e0b,0x7e0f,0x7e16,0x7e0d,0x7e14,
+ 0x7e25,0x7e24,0x7f43,0x7f7b,0x7f7c,0x7f7a,0x7fb1,0x7fef,0x802a,0x8029,
+ 0x806c,0x81b1,0x81a6,0x81ae,0x81b9,0x81b5,0x81ab,0x81b0,0x81ac,0x81b4,
+ 0x81b2,0x81b7,0x81a7,0x81f2,0x8255,0x8256,0x8257,0x8556,0x8545,0x856b,
+ 0x854d,0x8553,0x8561,0x8558,0x8540,0x8546,0x8564,0x8541,0x8562,0x8544,
+ 0x8551,0x8547,0x8563,0x853e,0x855b,0x8571,0x854e,0x856e,0x8575,0x8555,
+ 0x8567,0x8560,0x858c,0x8566
+ },
+ { /* ku 3a */
+ 0x855d,0x8554,0x8565,0x856c,0x8663,0x8665,0x8664,0x87a4,0x879b,0x878f,
+ 0x8797,0x8793,0x8792,0x8788,0x8781,0x8796,0x8798,0x8779,0x8787,0x87a3,
+ 0x8785,0x8790,0x8791,0x879d,0x8784,0x8794,0x879c,0x879a,0x8789,0x891e,
+ 0x8926,0x8930,0x892d,0x892e,0x8927,0x8931,0x8922,0x8929,0x8923,0x892f,
+ 0x892c,0x891f,0x89f1,0x8ae0,0x8ae2,0x8af2,0x8af4,0x8af5,0x8add,0x8b14,
+ 0x8ae4,0x8adf,0x8af0,0x8ac8,0x8ade,0x8ae1,0x8ae8,0x8aff,0x8aef,0x8afb,
+ 0x8c91,0x8c92,0x8c90,0x8cf5,0x8cee,0x8cf1,0x8cf0,0x8cf3,0x8d6c,0x8d6e,
+ 0x8da5,0x8da7,0x8e33,0x8e3e,0x8e38,0x8e40,0x8e45,0x8e36,0x8e3c,0x8e3d,
+ 0x8e41,0x8e30,0x8e3f,0x8ebd,0x8f36,0x8f2e,0x8f35,0x8f32,0x8f39,0x8f37,
+ 0x8f34,0x9076,0x9079,0x907b
+ },
+ { /* ku 3b */
+ 0x9086,0x90fa,0x9133,0x9135,0x9136,0x9193,0x9190,0x9191,0x918d,0x918f,
+ 0x9327,0x931e,0x9308,0x931f,0x9306,0x930f,0x937a,0x9338,0x933c,0x931b,
+ 0x9323,0x9312,0x9301,0x9346,0x932d,0x930e,0x930d,0x92cb,0x931d,0x92fa,
+ 0x9313,0x92f9,0x92f7,0x9334,0x9302,0x9324,0x92ff,0x9329,0x9339,0x9335,
+ 0x932a,0x9314,0x930c,0x930b,0x92fe,0x9309,0x9300,0x92fb,0x9316,0x95bc,
+ 0x95cd,0x95be,0x95b9,0x95ba,0x95b6,0x95bf,0x95b5,0x95bd,0x96a9,0x96d4,
+ 0x970b,0x9712,0x9710,0x9799,0x9797,0x9794,0x97f0,0x97f8,0x9835,0x982f,
+ 0x9832,0x9924,0x991f,0x9927,0x9929,0x999e,0x99ee,0x99ec,0x99e5,0x99e4,
+ 0x99f0,0x99e3,0x99ea,0x99e9,0x99e7,0x9ab9,0x9abf,0x9ab4,0x9abb,0x9af6,
+ 0x9afa,0x9af9,0x9af7,0x9b33
+ },
+ { /* ku 3c */
+ 0x9b80,0x9b85,0x9b87,0x9b7c,0x9b7e,0x9b7b,0x9b82,0x9b93,0x9b92,0x9b90,
+ 0x9b7a,0x9b95,0x9b7d,0x9b88,0x9d25,0x9d17,0x9d20,0x9d1e,0x9d14,0x9d29,
+ 0x9d1d,0x9d18,0x9d22,0x9d10,0x9d19,0x9d1f,0x9e88,0x9e86,0x9e87,0x9eae,
+ 0x9ead,0x9ed5,0x9ed6,0x9efa,0x9f12,0x9f3d,0x5126,0x5125,0x5122,0x5124,
+ 0x5120,0x5129,0x52f4,0x5693,0x568c,0x568d,0x5686,0x5684,0x5683,0x567e,
+ 0x5682,0x567f,0x5681,0x58d6,0x58d4,0x58cf,0x58d2,0x5b2d,0x5b25,0x5b32,
+ 0x5b23,0x5b2c,0x5b27,0x5b26,0x5b2f,0x5b2e,0x5b7b,0x5bf1,0x5bf2,0x5db7,
+ 0x5e6c,0x5e6a,0x5fbe,0x61c3,0x61b5,0x61bc,0x61e7,0x61e0,0x61e5,0x61e4,
+ 0x61e8,0x61de,0x64ef,0x64e9,0x64e3,0x64eb,0x64e4,0x64e8,0x6581,0x6580,
+ 0x65b6,0x65da,0x66d2,0x6a8d
+ },
+ { /* ku 3d */
+ 0x6a96,0x6a81,0x6aa5,0x6a89,0x6a9f,0x6a9b,0x6aa1,0x6a9e,0x6a87,0x6a93,
+ 0x6a8e,0x6a95,0x6a83,0x6aa8,0x6aa4,0x6a91,0x6a7f,0x6aa6,0x6a9a,0x6a85,
+ 0x6a8c,0x6a92,0x6b5b,0x6bad,0x6c09,0x6fcc,0x6fa9,0x6ff4,0x6fd4,0x6fe3,
+ 0x6fdc,0x6fed,0x6fe7,0x6fe6,0x6fde,0x6ff2,0x6fdd,0x6fe2,0x6fe8,0x71e1,
+ 0x71f1,0x71e8,0x71f2,0x71e4,0x71f0,0x71e2,0x7373,0x736e,0x736f,0x7497,
+ 0x74b2,0x74ab,0x7490,0x74aa,0x74ad,0x74b1,0x74a5,0x74af,0x7510,0x7511,
+ 0x7512,0x750f,0x7584,0x7643,0x7648,0x7649,0x7647,0x76a4,0x76e9,0x77b5,
+ 0x77ab,0x77b2,0x77b7,0x77b6,0x77b4,0x77b1,0x77a8,0x77f0,0x78f3,0x78fd,
+ 0x7902,0x78fb,0x78fc,0x78ff,0x78f2,0x7905,0x78f9,0x78fe,0x7904,0x79ab,
+ 0x79a8,0x7a5c,0x7a5b,0x7a56
+ },
+ { /* ku 3e */
+ 0x7a58,0x7a54,0x7a5a,0x7abe,0x7ac0,0x7ac1,0x7c05,0x7c0f,0x7bf2,0x7c00,
+ 0x7bff,0x7bfb,0x7c0e,0x7bf4,0x7c0b,0x7bf3,0x7c02,0x7c09,0x7c03,0x7c01,
+ 0x7bf8,0x7bfd,0x7c06,0x7bf0,0x7bf1,0x7c10,0x7c0a,0x7ce8,0x7e2d,0x7e3c,
+ 0x7e42,0x7e33,0x9848,0x7e38,0x7e2a,0x7e49,0x7e40,0x7e47,0x7e29,0x7e4c,
+ 0x7e30,0x7e3b,0x7e36,0x7e44,0x7e3a,0x7f45,0x7f7f,0x7f7e,0x7f7d,0x7ff4,
+ 0x7ff2,0x802c,0x81bb,0x81c4,0x81cc,0x81ca,0x81c5,0x81c7,0x81bc,0x81e9,
+ 0x825b,0x825a,0x825c,0x8583,0x8580,0x858f,0x85a7,0x8595,0x85a0,0x858b,
+ 0x85a3,0x857b,0x85a4,0x859a,0x859e,0x8577,0x857c,0x8589,0x85a1,0x857a,
+ 0x8578,0x8557,0x858e,0x8596,0x8586,0x858d,0x8599,0x859d,0x8581,0x85a2,
+ 0x8582,0x8588,0x8585,0x8579
+ },
+ { /* ku 3f */
+ 0x8576,0x8598,0x8590,0x859f,0x8668,0x87be,0x87aa,0x87ad,0x87c5,0x87b0,
+ 0x87ac,0x87b9,0x87b5,0x87bc,0x87ae,0x87c9,0x87c3,0x87c2,0x87cc,0x87b7,
+ 0x87af,0x87c4,0x87ca,0x87b4,0x87b6,0x87bf,0x87b8,0x87bd,0x87de,0x87b2,
+ 0x8935,0x8933,0x893c,0x893e,0x8941,0x8952,0x8937,0x8942,0x89ad,0x89af,
+ 0x89ae,0x89f2,0x89f3,0x8b1e,0x8b18,0x8b16,0x8b11,0x8b05,0x8b0b,0x8b22,
+ 0x8b0f,0x8b12,0x8b15,0x8b07,0x8b0d,0x8b08,0x8b06,0x8b1c,0x8b13,0x8b1a,
+ 0x8c4f,0x8c70,0x8c72,0x8c71,0x8c6f,0x8c95,0x8c94,0x8cf9,0x8d6f,0x8e4e,
+ 0x8e4d,0x8e53,0x8e50,0x8e4c,0x8e47,0x8f43,0x8f40,0x9085,0x907e,0x9138,
+ 0x919a,0x91a2,0x919b,0x9199,0x919f,0x91a1,0x919d,0x91a0,0x93a1,0x9383,
+ 0x93af,0x9364,0x9356,0x9347
+ },
+ { /* ku 40 */
+ 0x937c,0x9358,0x935c,0x9376,0x9349,0x9350,0x9351,0x9360,0x936d,0x938f,
+ 0x934c,0x936a,0x9379,0x9357,0x9355,0x9352,0x934f,0x9371,0x9377,0x937b,
+ 0x9361,0x935e,0x9363,0x9367,0x934e,0x9359,0x95c7,0x95c0,0x95c9,0x95c3,
+ 0x95c5,0x95b7,0x96ae,0x96b0,0x96ac,0x9720,0x971f,0x9718,0x971d,0x9719,
+ 0x979a,0x97a1,0x979c,0x979e,0x979d,0x97d5,0x97d4,0x97f1,0x9841,0x9844,
+ 0x984a,0x9849,0x9845,0x9843,0x9925,0x992b,0x992c,0x992a,0x9933,0x9932,
+ 0x992f,0x992d,0x9931,0x9930,0x9998,0x99a3,0x99a1,0x9a02,0x99fa,0x99f4,
+ 0x99f7,0x99f9,0x99f8,0x99f6,0x99fb,0x99fd,0x99fe,0x99fc,0x9a03,0x9abe,
+ 0x9afe,0x9afd,0x9b01,0x9afc,0x9b48,0x9b9a,0x9ba8,0x9b9e,0x9b9b,0x9ba6,
+ 0x9ba1,0x9ba5,0x9ba4,0x9b86
+ },
+ { /* ku 41 */
+ 0x9ba2,0x9ba0,0x9baf,0x9d33,0x9d41,0x9d67,0x9d36,0x9d2e,0x9d2f,0x9d31,
+ 0x9d38,0x9d30,0x9d45,0x9d42,0x9d43,0x9d3e,0x9d37,0x9d40,0x9d3d,0x7ff5,
+ 0x9d2d,0x9e8a,0x9e89,0x9e8d,0x9eb0,0x9ec8,0x9eda,0x9efb,0x9eff,0x9f24,
+ 0x9f23,0x9f22,0x9f54,0x9fa0,0x5131,0x512d,0x512e,0x5698,0x569c,0x5697,
+ 0x569a,0x569d,0x5699,0x5970,0x5b3c,0x5c69,0x5c6a,0x5dc0,0x5e6d,0x5e6e,
+ 0x61d8,0x61df,0x61ed,0x61ee,0x61f1,0x61ea,0x61f0,0x61eb,0x61d6,0x61e9,
+ 0x64ff,0x6504,0x64fd,0x64f8,0x6501,0x6503,0x64fc,0x6594,0x65db,0x66da,
+ 0x66db,0x66d8,0x6ac5,0x6ab9,0x6abd,0x6ae1,0x6ac6,0x6aba,0x6ab6,0x6ab7,
+ 0x6ac7,0x6ab4,0x6aad,0x6b5e,0x6bc9,0x6c0b,0x7007,0x700c,0x700d,0x7001,
+ 0x7005,0x7014,0x700e,0x6fff
+ },
+ { /* ku 42 */
+ 0x7000,0x6ffb,0x7026,0x6ffc,0x6ff7,0x700a,0x7201,0x71ff,0x71f9,0x7203,
+ 0x71fd,0x7376,0x74b8,0x74c0,0x74b5,0x74c1,0x74be,0x74b6,0x74bb,0x74c2,
+ 0x7514,0x7513,0x765c,0x7664,0x7659,0x7650,0x7653,0x7657,0x765a,0x76a6,
+ 0x76bd,0x76ec,0x77c2,0x77ba,0x790c,0x7913,0x7914,0x7909,0x7910,0x7912,
+ 0x7911,0x79ad,0x79ac,0x7a5f,0x7c1c,0x7c29,0x7c19,0x7c20,0x7c1f,0x7c2d,
+ 0x7c1d,0x7c26,0x7c28,0x7c22,0x7c25,0x7c30,0x7e5c,0x7e50,0x7e56,0x7e63,
+ 0x7e58,0x7e62,0x7e5f,0x7e51,0x7e60,0x7e57,0x7e53,0x7fb5,0x7fb3,0x7ff7,
+ 0x7ff8,0x8075,0x81d1,0x81d2,0x81d0,0x825f,0x825e,0x85b4,0x85c6,0x85c0,
+ 0x85c3,0x85c2,0x85b3,0x85b5,0x85bd,0x85c7,0x85c4,0x85bf,0x85cb,0x85ce,
+ 0x85c8,0x85c5,0x85b1,0x85b6
+ },
+ { /* ku 43 */
+ 0x85d2,0x8624,0x85b8,0x85b7,0x85be,0x8669,0x87e7,0x87e6,0x87e2,0x87db,
+ 0x87eb,0x87ea,0x87e5,0x87df,0x87f3,0x87e4,0x87d4,0x87dc,0x87d3,0x87ed,
+ 0x87d8,0x87e3,0x87d7,0x87d9,0x8801,0x87f4,0x87e8,0x87dd,0x8953,0x894b,
+ 0x894f,0x894c,0x8946,0x8950,0x8951,0x8949,0x8b2a,0x8b27,0x8b23,0x8b33,
+ 0x8b30,0x8b35,0x8b47,0x8b2f,0x8b3c,0x8b3e,0x8b31,0x8b25,0x8b37,0x8b26,
+ 0x8b36,0x8b2e,0x8b24,0x8b3b,0x8b3d,0x8b3a,0x8c42,0x8c75,0x8c99,0x8c98,
+ 0x8c97,0x8cfe,0x8d04,0x8d02,0x8d00,0x8e5c,0x8e62,0x8e60,0x8e57,0x8e56,
+ 0x8e5e,0x8e65,0x8e67,0x8e5b,0x8e5a,0x8e61,0x8e5d,0x8e69,0x8e54,0x8f46,
+ 0x8f47,0x8f48,0x8f4b,0x9128,0x913a,0x913b,0x913e,0x91a8,0x91a5,0x91a7,
+ 0x91af,0x91aa,0x93b5,0x938c
+ },
+ { /* ku 44 */
+ 0x9392,0x93b7,0x939b,0x939d,0x9389,0x93a7,0x938e,0x93aa,0x939e,0x93a6,
+ 0x9395,0x9388,0x9399,0x939f,0x9380,0x938d,0x93b1,0x9391,0x93b2,0x93a4,
+ 0x93a8,0x93b4,0x93a3,0x95d2,0x95d3,0x95d1,0x96b3,0x96d7,0x96da,0x5dc2,
+ 0x96df,0x96d8,0x96dd,0x9723,0x9722,0x9725,0x97ac,0x97ae,0x97a8,0x97ab,
+ 0x97a4,0x97aa,0x97a2,0x97a5,0x97d7,0x97d9,0x97d6,0x97d8,0x97fa,0x9850,
+ 0x9851,0x9852,0x98b8,0x9941,0x993c,0x993a,0x9a0f,0x9a0b,0x9a09,0x9a0d,
+ 0x9a04,0x9a11,0x9a0a,0x9a05,0x9a07,0x9a06,0x9ac0,0x9adc,0x9b08,0x9b04,
+ 0x9b05,0x9b29,0x9b35,0x9b4a,0x9b4c,0x9b4b,0x9bc7,0x9bc6,0x9bc3,0x9bbf,
+ 0x9bc1,0x9bb5,0x9bb8,0x9bd3,0x9bb6,0x9bc4,0x9bb9,0x9bbd,0x9d5c,0x9d53,
+ 0x9d4f,0x9d4a,0x9d5b,0x9d4b
+ },
+ { /* ku 45 */
+ 0x9d59,0x9d56,0x9d4c,0x9d57,0x9d52,0x9d54,0x9d5f,0x9d58,0x9d5a,0x9e8e,
+ 0x9e8c,0x9edf,0x9f01,0x9f00,0x9f16,0x9f25,0x9f2b,0x9f2a,0x9f29,0x9f28,
+ 0x9f4c,0x9f55,0x5134,0x5135,0x5296,0x52f7,0x53b4,0x56ab,0x56ad,0x56a6,
+ 0x56a7,0x56aa,0x56ac,0x58da,0x58dd,0x58db,0x5912,0x5b3d,0x5b3e,0x5b3f,
+ 0x5dc3,0x5e70,0x5fbf,0x61fb,0x6507,0x6510,0x650d,0x6509,0x650c,0x650e,
+ 0x6584,0x65de,0x65dd,0x66de,0x6ae7,0x6ae0,0x6acc,0x6ad1,0x6ad9,0x6acb,
+ 0x6adf,0x6adc,0x6ad0,0x6aeb,0x6acf,0x6acd,0x6ade,0x6b60,0x6bb0,0x6c0c,
+ 0x7019,0x7027,0x7020,0x7016,0x702b,0x7021,0x7022,0x7023,0x7029,0x7017,
+ 0x7024,0x701c,0x720c,0x720a,0x7207,0x7202,0x7205,0x72a5,0x72a6,0x72a4,
+ 0x72a3,0x72a1,0x74cb,0x74c5
+ },
+ { /* ku 46 */
+ 0x74b7,0x74c3,0x7516,0x7660,0x77c9,0x77ca,0x77c4,0x77f1,0x791d,0x791b,
+ 0x7921,0x791c,0x7917,0x791e,0x79b0,0x7a67,0x7a68,0x7c33,0x7c3c,0x7c39,
+ 0x7c2c,0x7c3b,0x7cec,0x7cea,0x7e76,0x7e75,0x7e78,0x7e70,0x7e77,0x7e6f,
+ 0x7e7a,0x7e72,0x7e74,0x7e68,0x7f4b,0x7f4a,0x7f83,0x7f86,0x7fb7,0x7ffd,
+ 0x7ffe,0x8078,0x81d7,0x81d5,0x820b,0x8264,0x8261,0x8263,0x85eb,0x85f1,
+ 0x85ed,0x85d9,0x85e1,0x85e8,0x85da,0x85d7,0x85ec,0x85f2,0x85f8,0x85d8,
+ 0x85df,0x85e3,0x85dc,0x85d1,0x85f0,0x85e6,0x85ef,0x85de,0x85e2,0x8800,
+ 0x87fa,0x8803,0x87f6,0x87f7,0x8809,0x880c,0x880b,0x8806,0x87fc,0x8808,
+ 0x87ff,0x880a,0x8802,0x8962,0x895a,0x895b,0x8957,0x8961,0x895c,0x8958,
+ 0x895d,0x8959,0x8988,0x89b7
+ },
+ { /* ku 47 */
+ 0x89b6,0x89f6,0x8b50,0x8b48,0x8b4a,0x8b40,0x8b53,0x8b56,0x8b54,0x8b4b,
+ 0x8b55,0x8b51,0x8b42,0x8b52,0x8b57,0x8c43,0x8c77,0x8c76,0x8c9a,0x8d06,
+ 0x8d07,0x8d09,0x8dac,0x8daa,0x8dad,0x8dab,0x8e6d,0x8e78,0x8e73,0x8e6a,
+ 0x8e6f,0x8e7b,0x8ec2,0x8f52,0x8f51,0x8f4f,0x8f50,0x8f53,0x8fb4,0x9140,
+ 0x913f,0x91b0,0x91ad,0x93de,0x93c7,0x93cf,0x93c2,0x93da,0x93d0,0x93f9,
+ 0x93ec,0x93cc,0x93d9,0x93a9,0x93e6,0x93ca,0x93d4,0x93ee,0x93e3,0x93d5,
+ 0x93c4,0x93ce,0x93c0,0x93d2,0x93a5,0x93e7,0x957d,0x95da,0x95db,0x96e1,
+ 0x9729,0x972b,0x972c,0x9728,0x9726,0x97b3,0x97b7,0x97b6,0x97dd,0x97de,
+ 0x97df,0x985c,0x9859,0x985d,0x9857,0x98bf,0x98bd,0x98bb,0x98be,0x9948,
+ 0x9947,0x9943,0x99a6,0x99a7
+ },
+ { /* ku 48 */
+ 0x9a1a,0x9a15,0x9a25,0x9a1d,0x9a24,0x9a1b,0x9a22,0x9a20,0x9a27,0x9a23,
+ 0x9a1e,0x9a1c,0x9a14,0x9ac2,0x9b0b,0x9b0a,0x9b0e,0x9b0c,0x9b37,0x9bea,
+ 0x9beb,0x9be0,0x9bde,0x9be4,0x9be6,0x9be2,0x9bf0,0x9bd4,0x9bd7,0x9bec,
+ 0x9bdc,0x9bd9,0x9be5,0x9bd5,0x9be1,0x9bda,0x9d77,0x9d81,0x9d8a,0x9d84,
+ 0x9d88,0x9d71,0x9d80,0x9d78,0x9d86,0x9d8b,0x9d8c,0x9d7d,0x9d6b,0x9d74,
+ 0x9d75,0x9d70,0x9d69,0x9d85,0x9d73,0x9d7b,0x9d82,0x9d6f,0x9d79,0x9d7f,
+ 0x9d87,0x9d68,0x9e94,0x9e91,0x9ec0,0x9efc,0x9f2d,0x9f40,0x9f41,0x9f4d,
+ 0x9f56,0x9f57,0x9f58,0x5337,0x56b2,0x56b5,0x56b3,0x58e3,0x5b45,0x5dc6,
+ 0x5dc7,0x5eee,0x5eef,0x5fc0,0x5fc1,0x61f9,0x6517,0x6516,0x6515,0x6513,
+ 0x65df,0x66e8,0x66e3,0x66e4
+ },
+ { /* ku 49 */
+ 0x6af3,0x6af0,0x6aea,0x6ae8,0x6af9,0x6af1,0x6aee,0x6aef,0x703c,0x7035,
+ 0x702f,0x7037,0x7034,0x7031,0x7042,0x7038,0x703f,0x703a,0x7039,0x702a,
+ 0x7040,0x703b,0x7033,0x7041,0x7213,0x7214,0x72a8,0x737d,0x737c,0x74ba,
+ 0x76ab,0x76aa,0x76be,0x76ed,0x77cc,0x77ce,0x77cf,0x77cd,0x77f2,0x7925,
+ 0x7923,0x7927,0x7928,0x7924,0x7929,0x79b2,0x7a6e,0x7a6c,0x7a6d,0x7af7,
+ 0x7c49,0x7c48,0x7c4a,0x7c47,0x7c45,0x7cee,0x7e7b,0x7e7e,0x7e81,0x7e80,
+ 0x7fba,0x7fff,0x8079,0x81db,0x81d9,0x8268,0x8269,0x8622,0x85ff,0x8601,
+ 0x85fe,0x861b,0x8600,0x85f6,0x8604,0x8609,0x8605,0x860c,0x85fd,0x8819,
+ 0x8810,0x8811,0x8817,0x8813,0x8816,0x8963,0x8966,0x89b9,0x89f7,0x8b60,
+ 0x8b6a,0x8b5d,0x8b68,0x8b63
+ },
+ { /* ku 4a */
+ 0x8b65,0x8b67,0x8b6d,0x8dae,0x8e86,0x8e88,0x8e84,0x8f59,0x8f56,0x8f57,
+ 0x8f55,0x8f58,0x8f5a,0x908d,0x9143,0x9141,0x91b7,0x91b5,0x91b2,0x91b3,
+ 0x940b,0x9413,0x93fb,0x9420,0x940f,0x9414,0x93fe,0x9415,0x9410,0x9428,
+ 0x9419,0x940d,0x93f5,0x9400,0x93f7,0x9407,0x940e,0x9416,0x9412,0x93fa,
+ 0x9409,0x93f8,0x943c,0x940a,0x93ff,0x93fc,0x940c,0x93f6,0x9411,0x9406,
+ 0x95de,0x95e0,0x95df,0x972e,0x972f,0x97b9,0x97bb,0x97fd,0x97fe,0x9860,
+ 0x9862,0x9863,0x985f,0x98c1,0x98c2,0x9950,0x994e,0x9959,0x994c,0x994b,
+ 0x9953,0x9a32,0x9a34,0x9a31,0x9a2c,0x9a2a,0x9a36,0x9a29,0x9a2e,0x9a38,
+ 0x9a2d,0x9ac7,0x9aca,0x9ac6,0x9b10,0x9b12,0x9b11,0x9c0b,0x9c08,0x9bf7,
+ 0x9c05,0x9c12,0x9bf8,0x9c40
+ },
+ { /* ku 4b */
+ 0x9c07,0x9c0e,0x9c06,0x9c17,0x9c14,0x9c09,0x9d9f,0x9d99,0x9da4,0x9d9d,
+ 0x9d92,0x9d98,0x9d90,0x9d9b,0x9da0,0x9d94,0x9d9c,0x9daa,0x9d97,0x9da1,
+ 0x9d9a,0x9da2,0x9da8,0x9d9e,0x9da3,0x9dbf,0x9da9,0x9d96,0x9da6,0x9da7,
+ 0x9e99,0x9e9b,0x9e9a,0x9ee5,0x9ee4,0x9ee7,0x9ee6,0x9f30,0x9f2e,0x9f5b,
+ 0x9f60,0x9f5e,0x9f5d,0x9f59,0x9f91,0x513a,0x5139,0x5298,0x5297,0x56c3,
+ 0x56bd,0x56be,0x5b48,0x5b47,0x5dcb,0x5dcf,0x5ef1,0x61fd,0x651b,0x6b02,
+ 0x6afc,0x6b03,0x6af8,0x6b00,0x7043,0x7044,0x704a,0x7048,0x7049,0x7045,
+ 0x7046,0x721d,0x721a,0x7219,0x737e,0x7517,0x766a,0x77d0,0x792d,0x7931,
+ 0x792f,0x7c54,0x7c53,0x7cf2,0x7e8a,0x7e87,0x7e88,0x7e8b,0x7e86,0x7e8d,
+ 0x7f4d,0x7fbb,0x8030,0x81dd
+ },
+ { /* ku 4c */
+ 0x8618,0x862a,0x8626,0x861f,0x8623,0x861c,0x8619,0x8627,0x862e,0x8621,
+ 0x8620,0x8629,0x861e,0x8625,0x8829,0x881d,0x881b,0x8820,0x8824,0x881c,
+ 0x882b,0x884a,0x896d,0x8969,0x896e,0x896b,0x89fa,0x8b79,0x8b78,0x8b45,
+ 0x8b7a,0x8b7b,0x8d10,0x8d14,0x8daf,0x8e8e,0x8e8c,0x8f5e,0x8f5b,0x8f5d,
+ 0x9146,0x9144,0x9145,0x91b9,0x943f,0x943b,0x9436,0x9429,0x943d,0x9430,
+ 0x9439,0x942a,0x9437,0x942c,0x9440,0x9431,0x95e5,0x95e4,0x95e3,0x9735,
+ 0x973a,0x97bf,0x97e1,0x9864,0x98c9,0x98c6,0x98c0,0x9958,0x9956,0x9a39,
+ 0x9a3d,0x9a46,0x9a44,0x9a42,0x9a41,0x9a3a,0x9a3f,0x9acd,0x9b15,0x9b17,
+ 0x9b18,0x9b16,0x9b3a,0x9b52,0x9c2b,0x9c1d,0x9c1c,0x9c2c,0x9c23,0x9c28,
+ 0x9c29,0x9c24,0x9c21,0x9db7
+ },
+ { /* ku 4d */
+ 0x9db6,0x9dbc,0x9dc1,0x9dc7,0x9dca,0x9dcf,0x9dbe,0x9dc5,0x9dc3,0x9dbb,
+ 0x9db5,0x9dce,0x9db9,0x9dba,0x9dac,0x9dc8,0x9db1,0x9dad,0x9dcc,0x9db3,
+ 0x9dcd,0x9db2,0x9e7a,0x9e9c,0x9eeb,0x9eee,0x9eed,0x9f1b,0x9f18,0x9f1a,
+ 0x9f31,0x9f4e,0x9f65,0x9f64,0x9f92,0x4eb9,0x56c6,0x56c5,0x56cb,0x5971,
+ 0x5b4b,0x5b4c,0x5dd5,0x5dd1,0x5ef2,0x6521,0x6520,0x6526,0x6522,0x6b0b,
+ 0x6b08,0x6b09,0x6c0d,0x7055,0x7056,0x7057,0x7052,0x721e,0x721f,0x72a9,
+ 0x737f,0x74d8,0x74d5,0x74d9,0x74d7,0x766d,0x76ad,0x7935,0x79b4,0x7a70,
+ 0x7a71,0x7c57,0x7c5c,0x7c59,0x7c5b,0x7c5a,0x7cf4,0x7cf1,0x7e91,0x7f4f,
+ 0x7f87,0x81de,0x826b,0x8634,0x8635,0x8633,0x862c,0x8632,0x8636,0x882c,
+ 0x8828,0x8826,0x882a,0x8825
+ },
+ { /* ku 4e */
+ 0x8971,0x89bf,0x89be,0x89fb,0x8b7e,0x8b84,0x8b82,0x8b86,0x8b85,0x8b7f,
+ 0x8d15,0x8e95,0x8e94,0x8e9a,0x8e92,0x8e90,0x8e96,0x8e97,0x8f60,0x8f62,
+ 0x9147,0x944c,0x9450,0x944a,0x944b,0x944f,0x9447,0x9445,0x9448,0x9449,
+ 0x9446,0x973f,0x97e3,0x986a,0x9869,0x98cb,0x9954,0x995b,0x9a4e,0x9a53,
+ 0x9a54,0x9a4c,0x9a4f,0x9a48,0x9a4a,0x9a49,0x9a52,0x9a50,0x9ad0,0x9b19,
+ 0x9b2b,0x9b3b,0x9b56,0x9b55,0x9c46,0x9c48,0x9c3f,0x9c44,0x9c39,0x9c33,
+ 0x9c41,0x9c3c,0x9c37,0x9c34,0x9c32,0x9c3d,0x9c36,0x9ddb,0x9dd2,0x9dde,
+ 0x9dda,0x9dcb,0x9dd0,0x9ddc,0x9dd1,0x9ddf,0x9de9,0x9dd9,0x9dd8,0x9dd6,
+ 0x9df5,0x9dd5,0x9ddd,0x9eb6,0x9ef0,0x9f35,0x9f33,0x9f32,0x9f42,0x9f6b,
+ 0x9f95,0x9fa2,0x513d,0x5299
+ },
+ { /* ku 4f */
+ 0x58e8,0x58e7,0x5972,0x5b4d,0x5dd8,0x882f,0x5f4f,0x6201,0x6203,0x6204,
+ 0x6529,0x6525,0x6596,0x66eb,0x6b11,0x6b12,0x6b0f,0x6bca,0x705b,0x705a,
+ 0x7222,0x7382,0x7381,0x7383,0x7670,0x77d4,0x7c67,0x7c66,0x7e95,0x826c,
+ 0x863a,0x8640,0x8639,0x863c,0x8631,0x863b,0x863e,0x8830,0x8832,0x882e,
+ 0x8833,0x8976,0x8974,0x8973,0x89fe,0x8b8c,0x8b8e,0x8b8b,0x8b88,0x8c45,
+ 0x8d19,0x8e98,0x8f64,0x8f63,0x91bc,0x9462,0x9455,0x945d,0x9457,0x945e,
+ 0x97c4,0x97c5,0x9800,0x9a56,0x9a59,0x9b1e,0x9b1f,0x9b20,0x9c52,0x9c58,
+ 0x9c50,0x9c4a,0x9c4d,0x9c4b,0x9c55,0x9c59,0x9c4c,0x9c4e,0x9dfb,0x9df7,
+ 0x9def,0x9de3,0x9deb,0x9df8,0x9de4,0x9df6,0x9de1,0x9dee,0x9de6,0x9df2,
+ 0x9df0,0x9de2,0x9dec,0x9df4
+ },
+ { /* ku 50 */
+ 0x9df3,0x9de8,0x9ded,0x9ec2,0x9ed0,0x9ef2,0x9ef3,0x9f06,0x9f1c,0x9f38,
+ 0x9f37,0x9f36,0x9f43,0x9f4f,0x9f71,0x9f70,0x9f6e,0x9f6f,0x56d3,0x56cd,
+ 0x5b4e,0x5c6d,0x652d,0x66ed,0x66ee,0x6b13,0x705f,0x7061,0x705d,0x7060,
+ 0x7223,0x74db,0x74e5,0x77d5,0x7938,0x79b7,0x79b6,0x7c6a,0x7e97,0x7f89,
+ 0x826d,0x8643,0x8838,0x8837,0x8835,0x884b,0x8b94,0x8b95,0x8e9e,0x8e9f,
+ 0x8ea0,0x8e9d,0x91be,0x91bd,0x91c2,0x946b,0x9468,0x9469,0x96e5,0x9746,
+ 0x9743,0x9747,0x97c7,0x97e5,0x9a5e,0x9ad5,0x9b59,0x9c63,0x9c67,0x9c66,
+ 0x9c62,0x9c5e,0x9c60,0x9e02,0x9dfe,0x9e07,0x9e03,0x9e06,0x9e05,0x9e00,
+ 0x9e01,0x9e09,0x9dff,0x9dfd,0x9e04,0x9ea0,0x9f1e,0x9f46,0x9f74,0x9f75,
+ 0x9f76,0x56d4,0x652e,0x65b8
+ },
+ { /* ku 51 */
+ 0x6b18,0x6b19,0x6b17,0x6b1a,0x7062,0x7226,0x72aa,0x77d8,0x77d9,0x7939,
+ 0x7c69,0x7c6b,0x7cf6,0x7e9a,0x7e98,0x7e9b,0x7e99,0x81e0,0x81e1,0x8646,
+ 0x8647,0x8648,0x8979,0x897a,0x897c,0x897b,0x89ff,0x8b98,0x8b99,0x8ea5,
+ 0x8ea4,0x8ea3,0x946e,0x946d,0x946f,0x9471,0x9473,0x9749,0x9872,0x995f,
+ 0x9c68,0x9c6e,0x9c6d,0x9e0b,0x9e0d,0x9e10,0x9e0f,0x9e12,0x9e11,0x9ea1,
+ 0x9ef5,0x9f09,0x9f47,0x9f78,0x9f7b,0x9f7a,0x9f79,0x571e,0x7066,0x7c6f,
+ 0x883c,0x8db2,0x8ea6,0x91c3,0x9474,0x9478,0x9476,0x9475,0x9a60,0x9b2e,
+ 0x9c74,0x9c73,0x9c71,0x9c75,0x9e14,0x9e13,0x9ef6,0x9f0a,0x9fa4,0x7068,
+ 0x7065,0x7cf7,0x866a,0x883e,0x883d,0x883f,0x8b9e,0x8c9c,0x8ea9,0x8ec9,
+ 0x974b,0x9873,0x9874,0x98cc
+ },
+ { /* ku 52 */
+ 0x9961,0x99ab,0x9a64,0x9a66,0x9a67,0x9b24,0x9e15,0x9e17,0x9f48,0x6207,
+ 0x6b1e,0x7227,0x864c,0x8ea8,0x9482,0x9480,0x9481,0x9a69,0x9a68,0x9e19,
+ 0x864b,0x8b9f,0x9483,0x9c79,0x9eb7,0x7675,0x9a6b,0x9c7a,0x9e1d,0x7069,
+ 0x706a,0x7229,0x9ea4,0x9f7e,0x9f49,0x9f98,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+#if CNS_EXTENDED
+/* CNS 11643 plane 3 conversion table */
+
+static const unsigned short
+ cns11643_3tab[MAX_CNS11643_KU_3][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e28,0x4e36,0x4e3f,0x4e85,0x4e05,0x4e04,0x5182,0x5196,0x5338,0x5369,
+ 0x53b6,0x4e2a,0x4e87,0x4e49,0x51e2,0x4e46,0x4e8f,0x4ebc,0x4ebe,0x5166,
+ 0x51e3,0x5204,0x529c,0x5344,0x5902,0x590a,0x5b80,0x5ddb,0x5e7a,0x5e7f,
+ 0x5ef4,0x5f50,0x5f51,0x5f61,0x961d,UBOGON,0x4e63,0x4e62,0x4ea3,0x5185,
+ 0x4ec5,0x4ecf,0x4ece,0x4ecc,0x5184,0x5186,UBOGON,0x34c5,0x51e4,0x5205,
+ 0x529e,0x529d,0x52fd,0x5300,0x533a,0x3539,0x5346,0x535d,0x5386,0x53b7,
+ 0x3555,0x53cc,0x355b,0x53ce,0x5721,0x37a2,0x5e00,0x5f0c,0x6237,0x6238,
+ 0x6534,0x6535,0x65e0,0x3e26,0x738d,0x4e97,0x4ee0,0x3432,UBOGON,0x4ee7,
+ 0x3433,0x4ee6,0x3434,0x36a2,0x3431,0x34b0,0x56d8,0x518b,0x518c,0x5199,
+ 0x51e5,UBOGON,0x520b,0x34dc
+ },
+ { /* ku 02 */
+ 0x361e,0x5304,0x5303,0x5307,UBOGON,0x531e,0x535f,0x536d,0x5389,0x53ba,
+ 0x53d0,0x3565,0x53f6,0x53f7,0x53f9,0x3564,0x53f4,0x361d,0x3626,0x5724,
+ 0x5904,0x5918,0x5932,0x5930,0x5934,0x368e,0x5975,0x374a,0x5b82,0x5bf9,
+ 0x5c14,0x378b,0x37a6,0x37a4,0x37a5,0x37a7,0x382f,0x3832,0x5e81,0x5e83,
+ 0x5f0d,0x5f52,0x38d4,0x5fca,0x5fc7,0x6239,0x39c5,0x624f,0x65e7,0x672f,
+ 0x6b7a,0x6c39,0x3cba,0x3cb9,0x6c37,0x6c44,0x6c45,0x738c,0x7592,0x7676,
+ 0x9093,0x9092,0x48b3,0x49ba,0x4e21,0x4e20,0x4e22,0x4e68,0x4e89,0x4e98,
+ 0x4ef9,0x4eef,0x343b,0x343c,0x4ef8,0x4f06,0x4f03,0x4efc,0x4eee,0x4f16,
+ 0x3439,0x4f28,0x4f1c,0x4f07,0x4f1a,0x4efa,0x4f17,0x514a,0x34b2,0x5172,
+ UBOGON,0x51b4,0x51b3,0x51b2
+ },
+ { /* ku 03 */
+ 0x34c7,0x51e8,0x342b,0x5214,0x520f,0x5215,0x5218,0x52a8,UBOGON,0x534b,
+ 0x534f,0x353b,0x5350,0x3544,0x538b,0x3542,0x53be,0x355c,0x53d2,0x5416,
+ 0x53ff,0x3567,0x5400,0x3566,0x5405,0x5413,0x5415,UBOGON,0x361f,0x56e3,
+ 0x5735,0x5736,0x5731,0x5732,0x58ee,0x5905,0x4e54,0x368f,0x5936,0x3690,
+ 0x36a8,0x36a4,0x597a,0x36a3,0x5986,0x373d,0x374c,0x5b86,0x5f53,0x5c18,
+ 0x378c,0x5c3d,0x5c78,0x37a8,0x37ad,0x37af,UBOGON,0x5c80,0x3829,0x5e08,
+ 0x3836,0x3871,0x3870,0x386f,0x5ef5,0x5f0e,0x38a9,0x38aa,0x38fb,0x5fd3,
+ 0x5fda,0x38fc,0x5fdb,0x39ae,0x620f,0x625d,0x625f,0x6267,0x6257,0x9f50,
+ 0x3ac3,0x65eb,0x65ea,0x3b30,0x6737,0x3b41,0x6732,0x6736,0x6b22,0x6bce,
+ 0x3c8c,0x6c58,0x6c51,0x6c77
+ },
+ { /* ku 04 */
+ 0x6c3c,0x3cbb,0x6c5a,UBOGON,0x6c53,0x706f,0x7072,0x706e,UBOGON,0x3da1,
+ 0x7073,0x72b1,0x72b2,0x3ea8,0x738f,0x3eaa,0x3eab,0x4096,0x793c,0x41c2,
+ 0x808d,0x808e,0x4493,0x827b,0x4494,0x8d71,0x8fb9,0x9096,0x909a,0x49bb,
+ 0x4e24,0x4e71,UBOGON,0x4e9c,0x4f45,0x4f4a,0x4f39,0x4f37,0x3443,0x4f32,
+ 0x4f42,0x3442,0x4f44,0x4f4b,0x3444,0x4f40,0x4f35,0x4f31,0x5151,UBOGON,
+ 0x5150,0x514e,0x34b3,0x34b7,0x519d,0x34c8,0x51b5,0x51b8,0x51ec,0x5223,
+ 0x5227,0x5226,0x521f,0x522b,0x5220,0x52b4,0x52b3,0x3518,0x5325,0x533b,
+ 0x5374,0x3547,0x3546,0x3545,0x356b,0x3569,0x544d,0x3572,0x3571,0x543a,
+ 0x356c,0x356f,0x5444,0x544c,0x5423,0x541a,0x5432,0x544b,0x5421,0x3573,
+ 0x5434,0x5449,0x5450,0x5422
+ },
+ { /* ku 05 */
+ 0x543f,0x5451,0x545a,0x542f,0x3576,0x56e9,0x56f2,0x56f3,0x56ef,0x56ed,
+ 0x56ec,0x56e6,0x5748,0x3627,0x5744,0x573f,0x573c,0x5753,0x5756,0x3630,
+ 0x575f,0x5743,0x5758,0x5757,0x3629,0x362a,0x362f,0x5746,0x362c,0x573d,
+ 0x362d,0x5742,0x5754,0x5755,0x58f1,0x58f2,0x58f0,0x590b,0x9ea6,0x56f1,
+ 0x593d,0x3693,0x5994,0x598c,0x36ad,0x599c,0x36ac,0x36ab,0x599f,0x36a9,
+ 0x599b,0x36ae,0x5989,0x599a,0x36aa,0x6588,0x374e,0x5b8d,0x3750,0x5bfe,
+ 0x5bff,0x5bfd,0x5c2b,0x37b2,0x5c84,0x5c8e,0x5c9c,0x37b5,0x37b6,0x5c85,
+ 0x5df5,0x5e09,0x3839,0x383b,0x5e0b,0x3872,0x5e92,0x5e90,0x5f03,0x38ac,
+ 0x5f1e,0x5f63,0x3908,0x5fe7,0x5ffe,0x5fe6,0x5fdc,0x5fce,0x3903,0x5ffc,
+ 0x5fdf,0x5fec,0x5ff6,UBOGON
+ },
+ { /* ku 06 */
+ 0x5ff2,0x5ff0,0x5ff9,0x390b,0x6213,0x39af,UBOGON,0x623b,0x623c,0x6282,
+ 0x39ce,0x39cb,0x39cc,0x6278,0x628b,0x39cd,0x629e,0x62a5,0x629b,0x629c,
+ 0x6299,0x628d,0x6285,0x629d,0x6275,0x3a80,0x3aaf,0x3ad3,0x65f6,0x3ad5,
+ 0x3ad4,0x3ad7,0x66f5,0x675b,0x3b42,0x6754,0x6752,0x3b44,0x6758,0x6744,
+ 0x674a,0x6761,0x3cc6,0x6c7f,0x6c91,0x6c9e,0x3cc0,0x6c6e,0x6c7c,0x6c9f,
+ 0x6c75,0x3cbe,0x6c56,0x6ca2,0x6c79,0x3cca,0x6ca1,0x3cc4,0x6caa,0x6ca0,
+ 0x3cc2,0x7079,0x7077,0x707e,0x3da4,0x7075,0x707b,0x7264,0x3e29,0x72bb,
+ 0x72bc,0x72c7,0x72b9,0x72be,0x72b6,0x3e60,0x3e5e,0x7398,0x3ead,0x3eae,
+ 0x3eac,0x3f57,0x7593,0x7680,0x3fdd,0x7683,0x76c0,0x76c1,0x400e,0x4097,
+ 0x77f4,0x77f5,0x4127,0x7acc
+ },
+ { /* ku 07 */
+ 0x7acd,0x7cfa,0x809f,0x8091,0x8097,0x8094,0x4495,0x8286,0x828c,UBOGON,
+ 0x8295,0x4498,0x866c,0x459d,0x8fb5,0x8fbe,0x8fc7,0x488a,0x8fc1,0x90a9,
+ 0x90a4,0x48b5,0x48b6,0x48b7,0x90a8,0x9627,0x9626,0x962b,0x9633,0x9634,
+ 0x9629,0x4e3d,0x3428,0x4e9d,0x4f93,0x4f8a,0x344d,0x3449,0x4f6d,0x4f8e,
+ 0x4fa0,0x4fa2,0x4fa1,0x4f9f,0x4fa3,UBOGON,0x4f72,0x3451,0x4f8c,0x5156,
+ UBOGON,UBOGON,0x5190,0x34cb,0x34ca,0x34cc,0x51ed,0x51fe,0x522f,UBOGON,
+ 0x523c,0x5234,0x5239,0x52b9,0x52b5,0x52bf,0x5355,0x353d,0x5376,0x537a,
+ 0x5393,0x3548,0x53c1,0x53c2,0x53d5,0x5485,0x3578,0x545f,0x5493,0x5489,
+ 0x5479,0x9efe,0x548f,0x5469,0x546d,0x357a,0x5494,0x546a,0x548a,0x3577,
+ 0x56fd,0x56fb,0x56f8,0x3621
+ },
+ { /* ku 08 */
+ 0x56fc,0x56f6,0x5765,0x5781,0x5763,0x5767,0x3631,0x576e,0x5778,0x577f,
+ 0x3633,0x3634,0x58f3,0x594b,0x594c,0x36c1,0x36b0,0x36b4,0x59ad,0x36b8,
+ 0x59c4,0x36bc,0x59c2,0x59b0,0x36bf,0x36b5,0x36b1,0x36bd,0x59bf,0x36bb,
+ 0x59c9,0x59b8,0x59ac,0x36b3,0x36b6,0x36ba,0x59b7,0x59d7,0x36b7,0x5b60,
+ 0x3740,0x5b96,0x5b9e,0x5b94,0x5b9f,0x5b9d,0x3752,0x5c00,0x5c19,0x3790,
+ 0x3791,0x5c49,0x5c4a,0x37be,0x5cbb,0x5cc1,0x37c0,0x37c1,0x37b9,0x5cb9,
+ 0x5c9e,0x5cb4,0x5cba,0x5df6,0x5e13,0x5e12,0x5e77,0x3879,0x5e98,0x387b,
+ 0x5e99,0x5e9d,0x5ef8,0x38a0,0x5ef9,0x3429,0x5f06,0x5f21,0x38ae,0x5f25,
+ 0x5f55,0x38cd,0x38cb,0x38d9,0x5f84,0x5f83,0x6030,0x6007,0x390c,0x6036,
+ 0x3901,0x3905,0x3902,0x5fe9
+ },
+ { /* ku 09 */
+ 0x603d,0x6008,0x3913,0x3911,0x62ba,0x62b2,0x39e4,0x62b7,0x62e4,0x62a7,
+ 0x39da,0x39d5,0x39d3,0x62d5,0x62e1,0x62dd,0x62a6,0x62c1,0x62c5,0x62c0,
+ 0x62df,0x62e0,0x62de,0x39d6,0x6589,0x3ab4,0x65a6,0x65ba,0x3ad9,0x65ff,
+ 0x3ad8,0x6617,0x6618,0x6601,0x65fe,0x3b33,0x670c,0x3b48,0x676b,0x6796,
+ 0x6782,0x678a,0x3b47,0x67a3,0x3b4b,0x67a2,0x678f,0x3b4a,0x67f9,0x6780,
+ 0x6b26,0x6b27,0x6b68,0x6b69,0x3c5a,0x6b81,0x6bb4,0x6bd1,0x3c8e,0x3cb4,
+ 0x6c1c,0x3ccd,0x3ccc,0x3ccf,0x3ccb,0x3cce,0x6c97,0x6c6c,0x6cdf,0x3cd2,
+ 0x6cea,0x3cd1,0x6ce4,0x6cd8,0x6cb2,0x6cce,0x6cc8,0x3da6,0x708b,0x7088,
+ 0x7090,0x708f,0x3daa,0x7087,0x7089,0x708d,0x7081,0x3da8,0x708c,0x3e13,
+ 0x3e1a,0x7240,0x3e1d,0x3e1e
+ },
+ { /* ku 0a */
+ 0x7265,0x7266,0x7268,0x3e65,0x3e66,0x72cd,0x72d3,0x72db,0x3e64,0x72cf,
+ 0x73a7,0x73a3,0x739e,0x3eb0,0x73af,0x3eb3,0x3eb5,0x73aa,0x739c,0x3f19,
+ 0x7542,0x7544,0x753b,0x7541,UBOGON,0x759b,0x759e,0x3f75,0x79c4,0x79c3,
+ 0x79c6,0x412b,0x412c,0x79c7,0x412d,0x79ca,UBOGON,0x41c3,0x7acf,0x7c76,
+ 0x7c74,0x7cff,0x7cfc,0x34ba,0x4350,0x7f59,0x80a8,0x43d3,0x43d0,0x80b0,
+ 0x43dc,0x80b3,0x43d2,0x80a4,0x80b6,0x80a7,0x80ac,0x43db,0x80a6,0x5367,
+ 0x820e,0x82c4,0x833e,0x829c,0x44a5,0x449f,0x449a,0x449c,0x44a2,0x82aa,
+ 0x449b,0x82c9,0x44a3,0x449d,0x82a6,0x82b2,0x4588,0x461a,0x488d,0x8fcc,
+ 0x8fd9,0x8fca,0x8fd8,0x8fcf,0x90b7,0x48b8,0x90ad,0x90b9,0x9637,0x49c3,
+ 0x9641,0x963e,0x96b6,0x9751
+ },
+ { /* ku 0b */
+ 0x9763,0x4e57,0x4e79,0x4eb2,0x4eb0,0x4eaf,0x4eb1,0x4fd2,0x4fd5,0x345d,
+ 0x4fbe,0x4fb8,0x4fb0,0x4fb1,0x4fc8,0x345a,0x3457,0x4fc6,0x4fcc,0x4fe5,
+ 0x4fe3,0x4fb4,0x516a,0x34b8,0x519f,0x34c2,0x51c1,0x34cf,0x51c2,0x51c3,
+ 0x5245,0x5248,0x34e7,0x34e9,0x524f,0x4452,0x34e8,0x52c5,0x52ca,0x52c4,
+ 0x5327,0x5358,0x537d,0x354a,0x53dd,0x53dc,0x53da,0x53d9,0x54b9,0x3580,
+ 0x54d0,0x54b4,0x54ca,0x3587,0x54a3,0x54da,0x54a4,0x3584,0x54b2,0x549e,
+ 0x549f,0x54b5,0x3582,0x3581,0x54cd,0x3583,0x54cc,0x3622,0x5700,0x57ac,
+ 0x5791,0x578e,0x578d,0x5792,0x57a1,0x5790,0x57a6,0x57a8,0x363b,0x579c,
+ 0x5796,0x57a7,0x363a,0x3638,0x3639,0x3636,0x58f5,0x3685,0x5909,0x5908,
+ 0x3c54,0x5952,0x369a,0x36c4
+ },
+ { /* ku 0c */
+ 0x59df,0x36c5,0x59eb,0x59ef,0x59f0,0x59d5,0x5a0d,0x5a04,0x59f9,0x5a02,
+ 0x59f8,0x59e2,0x59d9,0x59e7,0x5b6a,0x3754,0x3755,0x5bab,0x3756,0x5c1b,
+ 0x5c2f,0x3796,0x663c,0x3795,0x3794,0x37c4,0x5cd1,0x5cdc,0x5ce6,0x5ce1,
+ 0x5ccd,UBOGON,0x5ce2,0x5cdd,0x5ce5,0x5dfb,0x5dfa,0x5e1e,0x3844,0x5ea1,
+ 0x387d,0x387e,0x5efc,0x5efb,0x5f2f,0x38b2,0x38b6,0x5f66,UBOGON,0x38dc,
+ 0x38df,0x605c,0x3928,0x604e,0x6051,0x3919,0x3910,0x6023,0x6031,0x607c,
+ 0x6052,0x392c,0x6060,0x604a,0x6061,0x391b,0x6218,0x39c2,0x39ef,0x39e3,
+ 0x39e5,0x39ea,0x39e6,0x39ee,0x631f,0x6317,0x62ea,0x6321,0x6304,0x6305,
+ 0x39e8,0x6531,0x6544,0x6540,0x3a85,0x6542,0x65be,0x3ae0,0x6629,0x661b,
+ 0x3add,0x6623,0x662c,0x661a
+ },
+ { /* ku 0d */
+ 0x6630,0x663b,0x661e,0x6637,0x6638,0x3ae1,0x670e,0x3b51,0x3b55,0x67e8,
+ 0x67d6,0x3b52,0x67c7,0x67bc,0x6852,0x67bf,0x67d5,0x67fe,0x8363,0x67fb,
+ UBOGON,0x67b1,0x6801,0x6805,0x6800,0x67d7,0x409e,0x6b2a,0x6b6b,0x3c52,
+ 0x3c5e,0x3c60,0x3c5f,0x6be1,0x3c92,0x3cd6,0x6d23,0x6cff,0x6d14,0x6d05,
+ 0x6d13,0x6d06,0x6d21,0x3cde,0x6d15,0x6caf,0x6cf4,0x6d02,0x6d45,UBOGON,
+ 0x6d26,0x3cd9,0x6d44,0x3cdd,0x6d24,0x70a5,0x3dac,0x70a3,0x3db0,0x70a2,
+ 0x70bb,0x70a0,0x70aa,0x3daf,0x3dae,0x70a8,0x70b6,0x70b2,0x70a7,0x3dad,
+ 0x3dab,0x70b9,0x722e,0x3e16,0x723c,0x3e30,0x726d,0x3e33,0x3e31,0x72e7,
+ 0x72ed,0x3e6e,0x72ec,0x72e5,0x72e2,0x3eb1,0x73c4,0x73bd,0x73cf,0x73c9,
+ 0x73c1,0x73d0,0x3eb7,0x73ce
+ },
+ { /* ku 0e */
+ 0x74ed,0x74eb,0x3f1a,0x74ef,0x7549,0x7550,0x7546,0x754a,0x3f59,0x754d,
+ 0x75a6,0x3f7a,0x3f78,0x3f7b,0x75a8,0x3fde,0x3fec,0x76c7,0x76ff,0x401e,
+ 0x76fd,0x77e6,0x780a,0x409b,0x7804,0x780b,0x7807,0x409d,0x7815,0x7808,
+ 0x40fd,0x79d3,0x79d4,0x79d0,0x79d7,0x7a7c,0x4194,0x4193,0x7a7d,0x7a83,
+ 0x7a82,0x41c6,0x7ad4,0x7ad5,0x7ad3,0x7ad0,0x7ad2,0x7afe,0x7afc,0x7c77,
+ 0x7c7c,0x7c7b,0x42b8,UBOGON,0x42b7,0x42b9,0x4353,UBOGON,0x4352,0x4351,
+ 0x7f8f,0x80d3,0x43e3,0x80cb,0x80d2,0x43e2,0x8109,0x80e2,0x80df,0x80c6,
+ 0x4463,0x8224,0x82f7,0x82d8,0x82dd,0x44aa,0x44a6,0x82f8,0x82fc,0x44a8,
+ 0x44a9,0x82e9,0x44ab,0x82ee,0x44ac,0x82d0,0x830e,0x82e2,0x830b,0x82fd,
+ 0x5179,0x8676,0x459e,0x8678
+ },
+ { /* ku 0f */
+ 0x459f,0x45a0,0x8675,0x867d,0x460f,0x8842,0x8866,0x461c,0x898c,0x8a05,
+ 0x46ae,0x8a06,0x46b0,0x8c9f,0x47d4,0x8ff1,0x8fe7,0x8fe9,0x8fef,0x90c2,
+ 0x90bc,0x48bb,0x90c6,0x90c0,0x48c1,0x48c2,0x90cd,0x90c9,0x48be,0x90c4,
+ 0x48e5,0x9581,0x49c6,0x9cec,0x5032,0x4ff9,0x501d,0x4fff,0x5004,0x4ff0,
+ 0x5003,0x462e,0x5002,0x4ffc,0x4ff2,0x5024,0x5008,0x5036,0x502e,0x3465,
+ 0x5010,0x5038,0x5039,0x4ffd,0x5056,0x4ffb,0x51a3,0x51a6,0x51a1,0x34d1,
+ 0x34d0,0x51c7,0x51c9,0x5260,0x5264,0x5259,0x5265,0x5267,0x5257,0x5263,
+ 0x34ee,0x5253,0x34ef,0x52cf,0x351e,0x52ce,0x52d0,0x52d1,0x52cc,0x354b,
+ 0x354d,0x3556,0x550d,0x54f4,0x3592,0x5513,0x54ef,0x54f5,0x54f9,0x5502,
+ 0x5500,0x3593,0x3590,0x5518
+ },
+ { /* ku 10 */
+ 0x54f0,0x54f6,UBOGON,0x3597,0x5519,0x3623,0x5705,0x57c9,0x363f,0x57b7,
+ 0x57cd,0x3643,0x3642,0x3644,0x57be,0x57bb,0x3645,0x57db,0x57c8,0x57c4,
+ 0x57c5,0x57d1,0x57ca,0x57c0,0x36d9,0x36de,0x5a21,0x5a2a,0x36cf,0x5a1d,
+ 0x36cd,0x5a0b,0x36dd,0x36ce,0x36d3,0x36d6,0x5a22,0x36dc,0x36d1,0x5a24,
+ 0x36d0,0x5a14,0x5a31,0x36d5,0x5a2f,0x5a1a,0x5a12,0x36d4,0x36db,0x5a26,
+ UBOGON,0x3743,0x5bbc,0x5bbb,0x5bb7,0x5c05,0x5c06,0x5c52,0x5c53,0x37cd,
+ 0x37d1,0x5cfa,0x5ceb,0x37ca,0x5cf3,0x5cf5,0x5ce9,0x5cef,0x37d4,0x5e2a,
+ 0x5e30,0x5e2e,0x5e2c,0x5e2f,0x5eaf,0x5ea9,0x3886,0x5efd,0x5f32,0x5f8e,
+ 0x5f93,0x5f8f,0x604f,0x6099,0x3933,0x607e,0x3937,0x6074,0x604b,0x6073,
+ 0x6075,0x392a,0x391f,0x6056
+ },
+ { /* ku 11 */
+ 0x60a9,0x608b,0x60a6,0x3939,0x6093,0x60ae,0x609e,0x60a7,0x6245,0x39f2,
+ 0x39f8,0x632e,0x39f7,0x6352,0x6330,0x635b,0x39f4,0x6319,0x631b,0x39f1,
+ 0x6331,0x635d,0x6337,0x6335,0x6353,0x39f5,0x635c,0x633f,0x654b,0x3a87,
+ 0x4369,0x658b,0x3ab6,0x659a,0x6650,0x6646,0x664e,0x6640,0x3ae9,0x664b,
+ 0x6648,0x3aeb,0x6660,0x6644,0x664d,0x3b34,0x6837,0x6824,0x3b62,0x3b5c,
+ 0x681b,0x6836,0x3b60,0x682c,0x6819,0x6856,0x6847,0x683e,0x681e,UBOGON,
+ 0x6815,0x6822,0x6827,0x6859,0x6858,0x6855,0x6830,0x6823,0x6b2e,0x6b2b,
+ 0x6b30,0x6b6c,0x3c61,0x6b8b,0x3c7f,0x6be9,0x6bea,0x6be5,0x6d6b,0x3ce5,
+ 0x3ce6,0x6d73,0x6d57,0x3ce9,0x3cf3,0x6d5d,0x6d56,0x6d8f,0x6d5b,0x6d1c,
+ 0x6d9a,0x6d9b,0x6d99,0x3cee
+ },
+ { /* ku 12 */
+ 0x6d81,0x6d71,0x3ced,0x3cec,0x6d72,0x6d5c,0x6d96,0x70c4,0x70db,0x70cc,
+ 0x70d0,0x70e3,0x70df,0x3db3,0x70d6,0x70ee,0x70d5,0x3db5,0x3e27,0x3e35,
+ 0x3e36,0x727a,0x3e71,0x72f5,0x7302,0x3eb8,0x3ec2,0x73e2,0x73ec,0x73d5,
+ 0x73f9,0x73df,0x73e6,0x3ec8,0x3ec0,0x3ec1,0x3ec4,0x73e4,0x73e1,0x74f3,
+ 0x3f1f,0x3f1c,0x3f1d,0x3f4d,0x7556,0x7555,0x7558,0x7557,0x755e,0x75c3,
+ 0x3f87,0x3f82,0x75b4,0x3f7d,0x75b1,0x3fdf,0x4000,0x76cb,0x76cc,0x772a,
+ 0x4020,0x7716,0x770f,0x4022,0x4024,0x773f,0x772b,0x770e,0x7724,0x4021,
+ 0x7721,0x7718,0x77dd,0x40a4,0x40a5,0x7824,0x7836,0x4101,0x7958,0x7959,
+ 0x4103,0x7962,0x79da,0x79d9,0x4137,0x79e1,0x79e5,0x79e8,0x79db,0x4138,
+ 0x79e2,0x79f0,0x4199,0x4198
+ },
+ { /* ku 13 */
+ 0x4197,0x41c9,0x7ada,0x7add,0x41c7,0x7adb,0x7adc,0x41d9,0x41db,0x7b0d,
+ 0x7b0b,0x7b14,0x7c8e,0x7c86,0x427b,0x7c87,0x7c83,0x7c8b,0x427c,0x42bd,
+ 0x42bc,0x42c3,0x7d24,0x42c1,0x42bf,0x42c4,0x7d25,0x7f62,0x7f93,0x7f99,
+ 0x7f97,0x437e,0x437f,0x7fc4,0x7fc6,0x800a,0x43b4,0x43b3,0x8040,0x803c,
+ 0x803b,0x80f6,0x80ff,0x80ee,0x8104,0x8103,0x8107,UBOGON,0x43e6,0x80f7,
+ 0x4459,0x445a,0x822d,0x4464,0x8227,0x8229,0x831f,0x8357,0x44b4,0x44b9,
+ 0x44b7,0x44b5,0x8321,0x44c1,0x44b1,0x8318,0x8358,0x44b3,0x44ba,0x458c,
+ 0x458b,0x458d,0x8684,0x869f,0x869b,0x8689,0x86a6,0x8692,0x868f,0x86a0,
+ 0x884f,0x8878,0x887a,0x886e,0x887b,0x8884,0x8873,0x4678,0x4677,0x8a0d,
+ 0x8a0b,0x8a19,0x46b2,0x47d6
+ },
+ { /* ku 14 */
+ 0x8ed0,0x4845,0x4892,0x4895,0x8ff9,0x9009,0x9008,0x48c6,0x90de,0x9151,
+ 0x48e7,0x48e8,0x91db,0x91df,0x91de,0x91d6,0x91e0,0x9585,0x9660,0x9659,
+ 0x49cb,0x9656,0x49cd,0x49f1,0x96bd,0x4b22,0x3421,0x5042,0x5059,0x346f,
+ 0x5044,0x5066,0x5052,0x5054,0x5071,0x5050,0x507b,0x507c,0x5058,0x3470,
+ 0x3464,0x5079,0x506c,0x5078,0x51a8,0x51d1,0x51cf,0x5268,0x5276,0x52d4,
+ 0x352d,0x53a0,0x53c4,0x3558,0x5558,0x554c,0x5568,0x35a6,0x5549,0x35a4,
+ 0x359f,0x555d,0x5529,UBOGON,0x5554,0x5553,0x35a3,0x555a,0x35a0,0x553a,
+ 0x553f,0x552b,0x57ea,0x364a,0x57ef,0x3647,0x3648,0x57dd,0x57fe,UBOGON,
+ 0x57de,0x57e6,0x3649,0x57e8,0x57ff,0x5803,0x58f7,0x68a6,0x591f,0x369e,
+ 0x595b,0x595d,0x595e,UBOGON
+ },
+ { /* ku 15 */
+ 0x36e8,0x5a2b,0x36ec,0x5a3b,0x36ed,0x36e6,0x5a61,0x5a3a,0x5a6e,0x5a4b,
+ 0x5a6b,0x36eb,0x36e7,0x5a45,0x5a4e,0x5a68,0x5a3d,0x5a71,0x5a3f,0x5a6f,
+ 0x5a75,0x36e9,0x5a73,0x5a2c,0x5a59,0x5a54,0x5a4f,0x5a63,0x375c,0x375d,
+ 0x5bc8,0x3760,0x5bc3,0x375b,0x5c5b,0x5c61,0x3799,0x5d21,0x5d0a,0x5d09,
+ 0x37d8,0x5d2c,0x5d08,0x37da,0x37dd,0x5d2a,0x5d15,0x37e0,0x5d10,0x5d13,
+ 0x37e5,0x5d2f,0x5d18,0x37d7,0x5de3,0x5e39,0x5e35,0x5e3a,0x5e32,0x384e,
+ 0x388c,0x3888,UBOGON,0x5ebb,0x5eba,0x5f34,0x5f39,0x38ce,UBOGON,0x38e5,
+ 0x38e6,0x6098,0x3932,0x60d0,0x3940,0x3947,0x394c,0x60d7,0x60aa,0x3935,
+ 0x60a1,0x60a4,0x3930,0x60ee,0x3943,0x60e7,0x394d,0x60e8,0x60de,0x39b7,
+ 0x39f3,0x637e,0x638b,0x3a02
+ },
+ { /* ku 16 */
+ 0x3a0b,0x6379,0x6386,0x6393,0x3a04,0x6373,0x636a,UBOGON,0x636c,0x3a08,
+ 0x637f,0x39fc,0x63b2,0x63ba,0x39ff,0x3a00,0x6366,0x6374,0x3a8b,0x655a,
+ 0x3a8d,0x654e,0x654d,0x658d,0x658e,0x65ad,0x3aca,0x65c7,0x65ca,0x3acb,
+ 0x65c9,UBOGON,0x65e3,0x6657,0x3af3,0x6663,0x6667,0x671a,0x6719,0x6716,
+ 0x3b36,0x3b6a,0x689e,0x68b6,0x6898,0x6873,0x3b6b,0x689a,0x688e,0x68b7,
+ 0x68db,0x68a5,0x686c,0x68c1,0x6884,0x3b71,0x3b68,0x6895,0x687a,0x6899,
+ 0x3b72,0x68b8,0x68b9,0x6870,0x3c2e,0x6b35,0x3c62,0x6b90,0x6bbb,0x6bed,
+ 0x3c98,0x3cb5,0x3ceb,0x6dc1,0x6dc3,0x6dce,0x3cfb,0x3cf8,0x6dad,0x6e04,
+ 0x3cf5,0x6db9,0x3d08,0x6de7,UBOGON,0x6e08,0x6e06,0x3d0a,0x6e0a,0x6db0,
+ 0x3d06,0x6df8,0x6e0c,0x3cfd
+ },
+ { /* ku 17 */
+ 0x6db1,0x3cfa,0x6e02,0x6e07,0x6e09,0x6e01,0x6e17,0x6dff,0x6e12,0x3dba,
+ 0x3db9,0x7103,0x7107,0x7101,0x70f5,0x70f1,0x7108,0x70f2,0x710f,0x3dbb,
+ 0x70fe,0x3e18,0x3e40,0x3e3d,0x731a,0x7310,0x730e,0x7402,0x73f3,0x3ecd,
+ 0x3ec9,0x73fb,0x3ecb,0x3eca,0x3ece,0x751b,0x7523,0x7561,0x7568,0x3f5e,
+ 0x7567,0x75d3,0x3f91,0x3f8c,0x7690,0x3fe1,0x4002,0x76d5,0x76d7,0x76d6,
+ 0x7730,0x402b,0x7726,0x402a,0x7740,0x3e14,0x771e,0x40ad,0x40a3,0x40ab,
+ 0x7847,0x40af,0x784b,0x7851,0x784f,0x7842,0x7846,0x4104,0x796e,0x796c,
+ 0x79f2,0x4144,0x79f1,0x79f5,0x79f3,0x79f9,0x413d,0x4147,0x419c,0x7a9a,
+ 0x7a93,0x7a91,0x7ae1,0x41e0,0x41e4,0x7b21,0x7b1c,0x7b16,0x7b17,0x7b36,
+ 0x7b1f,0x4280,0x7c93,0x7c99
+ },
+ { /* ku 18 */
+ 0x7c9a,0x7c9c,0x42ca,0x7d49,0x42d4,0x7d34,0x7d37,0x42d2,0x7d2d,0x42cb,
+ 0x7d4c,0x42ce,0x42d3,0x7d48,0x4344,0x4348,0x7f3b,0x4345,0x4381,0x4386,
+ 0x4385,0x8008,0x801a,0x43a3,0x801d,0x43b5,0x8049,0x8045,0x8044,0x7c9b,
+ 0x43fa,0x43f9,0x812a,0x812e,0x43fb,0x43f2,0x8131,0x43ef,0x811a,0x8134,
+ 0x8117,0x445b,0x4466,0x44ce,0x831d,0x8371,0x8384,0x8380,0x8372,0x83a1,
+ 0x35b4,0x8379,0x8391,0x44c8,0x839f,0x83ad,0x44d1,0x44c5,0x8323,0x44d2,
+ 0x8385,0x839c,0x83b7,0x8658,0x865a,0x458f,0x8657,0x86b2,0x45a7,0x86ae,
+ 0x45a5,0x45a4,0x4611,0x8845,0x889c,0x8894,0x88a3,0x888f,0x88a5,0x88a9,
+ 0x88a6,0x888a,0x88a0,0x8890,0x8992,0x8991,0x8994,0x46b5,0x8a26,0x8a32,
+ 0x8a28,0x46b4,0x46bd,0x8a1c
+ },
+ { /* ku 19 */
+ 0x46bb,0x8a2b,0x8a20,0x46b9,0x8a29,0x46c2,0x46be,0x46ba,0x8a21,0x8c3a,
+ 0x3ab7,0x8c5b,0x8c58,0x8c7c,0x4758,0x8ca6,0x8cae,0x8cad,0x8d65,0x479b,
+ 0x8d7e,0x479c,0x8d7c,0x8d7f,0x8d7a,0x8dbd,0x47da,0x47de,0x8dc0,0x8dbb,
+ 0x8ead,0x8eaf,0x8ed6,0x484d,0x4846,0x4847,0x484b,0x484c,0x8ed9,0x4848,
+ 0x4899,0x9012,0x900e,0x9025,0x489b,0x9013,0x90ee,0x48ce,0x90ab,0x90f7,
+ 0x48eb,0x9159,0x9154,0x91f2,0x91f0,0x91e5,0x91f6,0x491c,0x498c,0x9587,
+ 0x49d1,0x965a,0x49d6,0x49d3,0x966e,0x49d4,0x49d0,0x49d5,0x9679,0x4a0b,
+ 0x98e1,0x98e6,0x4bc6,0x9ec4,0x9ed2,0x4e80,0x3424,0x4e81,0x508f,0x5097,
+ 0x5088,0x5089,0x3474,0x347a,0x5081,0x5160,UBOGON,0x34c3,0x5e42,0x51d3,
+ 0x34d4,0x34d5,0x51d2,0x51d6
+ },
+ { /* ku 1a */
+ 0x5273,0x34fb,0x5270,0x34f7,0x3532,UBOGON,0x53a8,0x53a6,0x53c5,0x5597,
+ 0x55de,0x35ba,0x35bf,0x5596,0x55b4,0x35c7,0x5585,0x35b7,0x559b,0x55a0,
+ 0x35b9,0x5559,0x35c3,0x5586,0x35bd,0x35d0,0x55af,0x557a,0x35c1,0x35be,
+ 0x35cd,0x559e,0x35cb,0x55a9,0x570f,0x570e,0x581a,0x364f,0x581f,0x3653,
+ 0x583c,0x5818,0x583e,0x5826,0x3655,0x583a,UBOGON,0x5822,0x3651,0x58fb,
+ 0x5963,0x5964,0x369f,0x5aa8,0x5aa3,0x5a82,0x5a88,0x5aa1,0x5a85,0x5a98,
+ 0x36fe,0x5a99,0x36fb,0x5a89,0x5a81,0x5a96,0x5a80,0x36f1,0x36f5,0x5a91,
+ 0x36ef,0x3704,0x3703,0x36f4,0x5acf,0x36f3,0x3702,0x36f7,0x36fa,0x36fd,
+ 0x36ee,0x5a87,0x5aa0,0x36f0,0x5a79,0x36f2,0x5a86,0x5aab,0x5aaa,0x5aa4,
+ 0x5a8d,0x5a7e,0x3744,0x5bd5
+ },
+ { /* ku 1b */
+ 0x3762,0x3777,0x3dc9,0x5c1e,0x5c5f,0x5c5e,0x5d44,0x5d3e,0x37e8,0x5d48,
+ 0x5d1c,0x37ef,0x5d5b,0x5d4d,0x37e6,0x37ed,0x5d57,0x37e7,0x5d53,0x5d4f,
+ 0x37eb,0x5d3b,0x5d46,0x382d,0x3855,0x5e46,0x5e47,0x3853,0x5e48,0x5ec0,
+ 0x5ebd,0x5ebf,0x3890,0x5f11,0x38be,0x5f3e,0x5f3b,0x38bd,0x5f3a,0x38cf,
+ 0x38d0,0x38ec,0x5fa7,0x394b,0x60ea,0x3948,0x6107,0x6122,0x610c,0x3955,
+ 0x3951,0x60b3,0x60d6,0x60d2,0x394e,0x60e3,0x60e5,0x60e9,0x396b,0x395e,
+ 0x6111,0x60fd,0x3960,0x3967,0x611e,0x6120,0x6121,0x621e,0x39b8,0x63e2,
+ 0x63de,0x63e6,0x3a14,0x3a0f,0x3a07,0x3a13,0x63f8,0x3a17,0x63fe,0x63c1,
+ 0x63bf,0x63f7,0x63d1,0x655f,0x6560,0x6561,0x3a9a,0x3ab8,0x65d1,0x3af7,
+ 0x3af8,0x667d,0x666b,0x667f
+ },
+ { /* ku 1c */
+ 0x3afd,0x3af5,0x6673,0x6681,0x666d,0x6669,0x3afa,0x3b38,0x671e,0x68ed,
+ 0x3b87,0x3b80,0x3b88,0x3b79,0x6903,0x3b7c,0x68fe,0x68e5,0x691e,0x6902,
+ 0x3b83,0x3b85,0x6909,0x68ca,0x6900,UBOGON,0x6901,0x6918,0x68e2,0x68cf,
+ 0x3b7b,0x692e,0x68c5,0x68ff,0x3b86,0x691c,0x68c3,0x3c34,0x6b6f,0x3c55,
+ 0x6b6e,0x3c68,0x6bbe,0x3c9c,0x6bf4,0x6c2d,0x3cfc,0x6db6,0x6e75,0x6e1e,
+ 0x3d1a,0x6e18,0x3d17,0x6e48,0x3d1b,0x6e4f,0x3d13,0x6e42,0x6e6a,0x6e70,
+ 0x6dfe,0x3d05,0x3d07,0x6e6d,0x3d1c,0x6e7b,0x6e7e,0x6e59,0x3d11,0x6e57,
+ 0x3d16,0x6e80,0x6e50,0x3d15,0x6e29,0x6e76,0x6e2a,0x6e4c,0x712a,0x3dcb,
+ 0x7135,0x712c,0x7137,0x711d,0x3dc5,0x3dc2,0x7138,0x3dcd,0x7134,0x712b,
+ 0x7133,0x7127,0x7124,0x3dca
+ },
+ { /* ku 1d */
+ 0x712d,0x7232,0x7283,0x7282,0x7287,0x7306,0x7324,0x7338,0x732a,0x732c,
+ 0x732b,0x3e83,0x732f,0x7328,0x7417,0x3ed6,0x3ed5,0x7419,0x7438,0x3ed1,
+ 0x741f,0x7414,0x743c,0x73f7,0x741c,0x7415,0x7418,0x7439,0x74f9,0x7524,
+ UBOGON,0x3f52,0x3f5f,0x756e,0x756d,0x7571,0x758e,0x3f95,0x75e5,0x3f9d,
+ 0x3f98,0x3f9e,0x3f96,0x7694,0x76b3,0x4003,0x76d9,0x402f,0x7748,0x7749,
+ 0x7743,0x4031,0x4033,0x7742,0x77df,0x40b4,0x7863,0x7876,0x40b0,0x785f,
+ 0x7866,0x7966,0x7971,0x4108,0x4107,0x7976,0x7984,0x7975,0x79ff,0x7a07,
+ 0x414e,0x7a0e,0x7a09,0x4150,0x4152,0x41a1,0x41a3,0x41a5,0x41cc,0x7ae7,
+ 0x7ae2,0x7b55,0x41ef,0x41ea,0x7b43,0x7b57,0x7b6c,0x7b42,0x7b53,0x41ed,
+ 0x7b41,0x4285,0x4284,0x7ca7
+ },
+ { /* ku 1e */
+ 0x7ca0,0x7ca6,0x7ca4,0x7d74,0x42db,0x7d59,0x42d9,0x7d60,0x7d57,0x7d6c,
+ 0x7d7e,0x7d64,0x42d7,0x7d5a,0x7d5d,0x42da,0x42de,0x42d8,0x7d76,0x7d4d,
+ 0x7d75,0x42d5,0x7fd3,0x7fd6,0x439c,0x439d,0x8060,0x804e,0x8145,0x813b,
+ 0x43fe,0x8148,0x8142,0x8149,0x8140,0x8114,0x8141,0x4407,0x81ef,0x81f6,
+ 0x8203,0x446a,0x83ed,0x44e7,0x83da,0x8418,0x83d2,0x8408,0x44e2,0x8400,
+ 0x44df,0x44e1,0x44e5,0x8417,0x8346,0x8414,0x83d3,0x8405,0x841f,0x8402,
+ 0x8416,0x83cd,0x83e6,0x4591,0x865d,0x86d5,0x86e1,0x45b4,0x45b0,0x45b5,
+ 0x45ae,0x86ee,0x8847,0x8846,0x462d,0x462c,0x88bb,0x462b,0x88bf,0x88b4,
+ 0x4629,0x88b5,0x467f,0x899a,0x8a43,0x46c9,0x46cb,0x8a5a,0x46c5,0x46c6,
+ 0x46ca,0x8a35,0x8a38,0x8a42
+ },
+ { /* ku 1f */
+ 0x8a49,0x8a5d,0x8a4b,0x8a3d,0x46d2,0x46d0,0x472d,0x4735,0x8c60,0x8c5e,
+ 0x8c7f,0x8c7e,0x8c83,0x476c,0x8cb1,0x8d87,0x479d,0x47a0,0x8d88,0x8d83,
+ 0x47a2,0x479f,0x8d86,0x8d8b,0x8d82,0x8dca,0x8dd2,0x47eb,0x47e2,0x8dd4,
+ 0x8dc9,0x8eb0,0x4836,0x4832,0x4850,0x8ef2,0x8ee4,0x8ef3,0x8eea,0x484f,
+ 0x8efd,0x4852,0x8f9d,0x902b,0x902a,0x489e,0x9028,0x9029,0x902c,0x48a0,
+ 0x489c,0x903a,0x9030,0x9037,0x903b,0x48d1,0x910a,0x48ef,0x48f0,0x48f1,
+ 0x91fe,0x9220,0x491d,0x920b,0x491f,0x9218,0x9222,0x491e,0x921b,0x9208,
+ 0x4920,0x920e,0x9213,0x498e,0x4991,0x9595,UBOGON,0x4990,0x49d7,0x968c,
+ 0x967b,0x967f,0x9681,0x49d9,0x9682,0x49f4,0x49f6,0x3560,0x49f5,0x49f3,
+ 0x96ee,0x96ed,0x4a0c,0x96ec
+ },
+ { /* ku 20 */
+ 0x975f,0x976f,0x4a51,0x976d,0x4aa6,0x4aa7,0x4aa8,0x4b27,0x4b24,0x4b25,
+ 0x98f0,0x4b2a,0x4b74,0x4bc7,0x9aa9,0x4be7,0x4bed,0x9ae0,0x4eb7,0x342e,
+ 0x347b,0x50cc,0x50bc,0x347c,0x50aa,0x50b9,0x347d,0x50ab,0x50c3,0x50cd,
+ 0x517e,0x527e,0x5279,0x34fd,UBOGON,0x52e1,0x52e0,0x52e7,0x5380,0x53ab,
+ 0x53aa,0x53a9,0x53e0,0x55ea,0x35da,0x55d7,0x35d6,0x35db,0x55c1,0x5715,
+ 0x365b,0x586c,0x365c,0x585c,0x5850,0x5861,0x586a,0x5869,0x5856,0x5860,
+ 0x5866,0x585f,0x5923,0x5966,0x5968,0x3706,0x370b,0x5ace,0x370d,0x5ac5,
+ 0x5ac3,0x370a,0x3713,0x5ad0,0x3710,0x3712,0x3709,0x3708,0x3711,0x370f,
+ 0x5b74,0x5b76,0x5bdc,0x5bd7,0x5bda,0x5bdb,0x3767,0x5c20,0x5d6d,0x5d66,
+ 0x37f6,0x5d64,0x5d6e,UBOGON
+ },
+ { /* ku 21 */
+ 0x5d60,0x5f42,0x5f5a,0x5f6e,0x3964,0x396c,0x6130,0x613a,0x612a,0x6143,
+ 0x6119,0x6131,0x396d,0x613d,0x397a,0x3975,0x3a0d,0x6408,0x6432,0x6438,
+ 0x3a1e,0x6431,0x3a1b,0x6419,0x3a2a,0x6411,0x3a1f,0x3a22,0x6429,0x641d,
+ 0x3a25,0x3a27,0x3a29,0x643c,0x3a24,0x6446,0x6447,0x3a28,0x3a26,0x643a,
+ 0x6407,0x3a23,0x656b,0x3a9f,0x6570,0x656d,0x3ab1,0x65e4,0x6693,0x3b03,
+ 0x3b07,0x3b0c,0x3b06,0x668f,0x3b04,0x3b09,0x6692,0x3b05,0x668e,0x3b08,
+ 0x6946,0x3b96,0x3b9c,0x3b9f,0x3b9b,0x3b98,0x3b99,0x3b94,0x6931,0x3b8d,
+ 0x3ba3,0x693e,0x3b93,0x697c,0x6943,0x3b92,0x6973,UBOGON,0x6955,0x3b8e,
+ 0x3b8c,0x6985,0x694d,0x6950,0x6947,0x6967,0x6936,0x6964,0x6961,0x3b9a,
+ 0x697d,0x6b44,0x6b40,0x6b71
+ },
+ { /* ku 22 */
+ 0x6b73,0x6b9c,0x3c6a,0x3c6d,0x3c84,0x6bc1,0x3ca0,0x6bfa,0x6c31,0x6c32,
+ 0x3d1d,0x3d26,0x6eb8,0x6ea8,0x3d33,0x6e91,0x6ebb,0x3d38,0x6e9a,0x3d30,
+ 0x3d28,0x6ea9,0x3d27,0x3d2a,0x6eb5,0x6e6c,0x6ee8,0x3d31,0x6edd,0x6eda,
+ 0x6ee6,0x6eac,0x3d34,0x3d2e,0x3d3b,0x6ed9,0x6ee3,0x6ee9,0x6edb,0x3d29,
+ 0x716f,0x3dd2,0x3dd8,0x7148,0x3dcf,0x714a,0x716b,0x3dd9,0x714f,0x7157,
+ 0x7174,0x3dce,0x3dd3,0x3dd0,0x7145,0x7151,0x716d,0x3ba1,0x7251,0x7250,
+ 0x724e,0x3e47,0x7341,0x3e8b,0x732e,0x7346,0x3ed4,0x7427,0x3ede,0x7448,
+ 0x7453,0x743d,0x3edf,0x745d,0x7456,0x3ed7,0x741e,0x7447,0x7443,0x7458,
+ 0x7449,0x3ee1,0x744c,0x7445,0x743e,0x3f2f,0x7501,0x751e,0x3f62,0x3f63,
+ 0x757a,0x75ee,0x7602,0x7697
+ },
+ { /* ku 23 */
+ 0x7698,0x3fe2,0x4004,0x4043,0x775d,0x7764,0x7753,0x7758,0x7882,0x7890,
+ 0x788a,0x40be,0x787a,0x787d,0x40ba,0x788b,0x7878,0x40bc,UBOGON,0x788d,
+ 0x7888,0x7892,0x7881,0x797e,0x7983,0x410d,0x410e,0x4111,0x7980,0x410f,
+ 0x4112,0x4155,0x7a0f,0x4159,0x415b,0x7a1d,0x4157,0x7aa1,0x7aa4,0x41ce,
+ 0x7ae9,0x7aea,0x41fe,0x7b62,0x7b6b,0x41fc,0x7b5e,0x41f5,0x7b79,0x41f9,
+ 0x41fa,0x7b6f,0x7b68,0x4288,0x4289,0x7cae,0x428a,0x4287,0x428b,0x7cb0,
+ 0x42e6,0x7d90,0x42ed,0x7d8a,0x42e5,0x7d8b,0x7d99,0x7d95,0x42e0,0x7d87,
+ 0x7d78,0x7d97,0x7d89,0x7d98,0x42e1,0x435b,0x435c,0x7fa3,0x438f,0x438b,
+ 0x438d,0x7fdd,0x8057,0x43b9,0x8163,0x816a,0x816c,0x440f,0x4419,0x4413,
+ 0x815d,0x8175,0x4418,0x815f
+ },
+ { /* ku 24 */
+ 0x4416,0x817d,0x816d,0x4453,UBOGON,0x8241,0x844f,0x8484,0x44f6,0x847f,
+ 0x44f5,0x8448,0x842a,0x847b,0x8472,0x8464,0x842e,0x845c,0x8453,0x44f7,
+ 0x8441,0x84c8,0x44f0,0x8462,0x8480,0x843e,0x8483,0x8471,0x44f9,0x844a,
+ 0x8455,0x8458,0x4592,0x4595,0x4596,0x86fc,0x86fd,0x8715,0x45b9,0x8716,
+ 0x86ff,0x45bd,0x45b8,0x4612,0x8858,0x88cf,0x88e0,0x4680,0x4681,0x469a,
+ 0x4698,0x89e7,0x8a6a,0x8a80,0x46d4,0x8a6f,0x8a65,0x46da,0x8a78,0x8a7d,
+ 0x8a88,0x46d6,0x46db,0x8a64,0x8a7e,0x46dc,0x8a67,0x8c63,0x8c88,0x4771,
+ 0x8ccd,0x4772,0x8cc9,0x47a8,0x8ded,0x47f0,UBOGON,0x47f1,0x47fd,0x4838,
+ 0x4837,0x4839,0x8eb1,0x4855,0x4853,0x8f04,0x8f9e,0x8fa0,0x9043,0x9046,
+ 0x9048,0x9045,0x9040,0x904c
+ },
+ { /* ku 25 */
+ 0x48d5,0x48bd,0x910c,0x9113,0x9115,0x48f5,0x916b,0x9167,0x925d,0x9255,
+ 0x9235,0x4921,0x9259,0x922f,0x923c,0x928f,0x925c,0x926a,0x9262,0x925f,
+ 0x926b,0x926e,0x923b,0x9244,0x9241,0x959a,0x4992,0x9599,0x49de,0x49db,
+ 0x49da,0x968f,0x49df,0x9696,0x49f9,0x49f8,0x49fa,0x96f4,0x96fc,0x4a0e,
+ 0x9755,0x4a43,0x9779,0x4a56,0x4a53,0x4a9e,0x97ee,0x97f5,0x4aa9,0x980b,
+ 0x4afa,0x98f3,0x4b31,0x4b30,0x98f7,0x98ff,0x98f5,0x4b32,0x98ec,0x98f1,
+ 0x4b29,0x4b2e,0x999a,0x4b76,0x9ae2,0x9b3d,0x9b5d,0x9ce8,0x4ca5,0x9ceb,
+ 0x9cef,0x9cee,0x9e81,0x9f14,0x50d0,0x50d9,0x50dc,0x50d8,0x348c,0x50e1,
+ 0x50eb,0x348b,0x3489,0x50f4,0x50e2,0x50de,0x348d,0x3486,0x34d7,0x51f4,
+ 0x3504,0x3507,0x3503,0x52ed
+ },
+ { /* ku 26 */
+ 0x52ea,0x3522,0x5332,0x3551,0x53ae,0x53b0,0x3561,0x55fb,0x5603,0x560b,
+ 0x35e9,0x5607,0x35e5,0x55f8,0x35e4,0x5628,0x561e,0x35e3,0x5618,0x5611,
+ 0x5651,0x5605,0x5717,0x5892,0x3665,0x588c,0x3663,0x5878,0x5884,0x5873,
+ 0x58ad,0x5897,0x5895,0x5877,0x5872,0x5896,0x588d,0x5910,0x368c,0x596c,
+ 0x371a,0x5ae7,0x3715,0x5ae4,0x3720,0x3721,0x5aef,0x5626,0x371c,0x371b,
+ 0x5af0,0x5d7b,0x37fe,0x5d83,0x3804,0x3801,0x5d8b,0x5d8c,0x3800,0x5d78,
+ 0x5e52,0x386d,0x3893,0x5ed0,0x5ecf,0x38a1,0x5fb3,0x5fb4,0x3976,0x3979,
+ 0x3972,0x617b,0x3983,0x616f,0x6181,0x613c,0x6142,0x6138,0x6133,UBOGON,
+ 0x6160,0x6169,0x617d,0x6186,0x622c,0x6228,0x3a38,0x644c,0x3a30,0x6457,
+ 0x647c,0x3a34,0x3a3a,0x6455
+ },
+ { /* ku 27 */
+ 0x6462,0x6471,0x646a,0x6456,0x643b,0x6481,0x3a35,0x644f,0x647e,0x6464,
+ 0x3a3f,0x3a40,0x3a32,0x3a31,0x3a36,0x6571,UBOGON,0x3b0f,0x66a5,0x669a,
+ 0x669c,0x3b10,0x66a6,0x3b0d,0x66a4,0x698f,0x69c5,0x69c8,0x6992,0x69b2,
+ 0x3ba9,0x3bb4,0x3bac,0x69e3,0x69c0,0x69d6,0x69d1,0x699f,0x69a2,0x69d2,
+ 0x3bb8,0x3bae,UBOGON,0x69e1,0x69d5,0x699d,0x3bb3,0x3bba,0x6998,0x3c3f,
+ 0x6b74,0x6ba1,0x3d3c,0x6ef0,0x6ef3,0x3d42,0x3d40,0x6f1b,0x6f0c,0x6f1d,
+ 0x6f34,0x6f28,0x6f17,0x3d3e,0x6f44,0x6f42,0x6f04,0x6f11,0x6efa,0x6f4a,
+ 0x7191,0x718e,0x3de1,0x718b,0x718d,0x717f,0x718c,0x717e,0x717c,0x7183,
+ 0x3de6,0x7188,0x3de0,0x3e15,0x7294,0x3e93,0x7355,0x7353,0x734f,0x7354,
+ 0x746c,0x7465,0x7466,0x7461
+ },
+ { /* ku 28 */
+ 0x746b,0x7468,0x7476,0x3ee7,0x7460,UBOGON,0x7474,0x7506,0x760e,0x3fad,
+ 0x7607,0x3fae,0x3fe3,0x76b9,0x3ff5,0x76b7,0x76e2,0x4006,0x7774,0x7777,
+ 0x7776,0x7775,0x404f,0x7778,0x7771,0x4054,0x777a,0x715b,0x777b,0x78a6,
+ 0x78ae,0x78b8,0x40cb,0x40e3,0x40c9,0x78b1,0x78af,0x4113,0x7989,0x7987,
+ 0x4115,0x4161,0x7a29,0x4166,0x7a2a,0x4164,0x7a2d,0x7a2c,0x4160,0x7a32,
+ 0x4163,0x7aec,0x7af0,0x7b81,0x7b9e,0x7b83,0x420a,0x7b92,0x4204,0x7ba3,
+ 0x7b9f,0x7b93,0x4207,0x7b86,0x7cb8,0x7cb7,0x428d,0x428f,0x4290,0x4292,
+ 0x42ec,0x7dc8,0x7db6,UBOGON,0x7dd1,0x42e7,0x7da8,0x7dab,0x42f2,0x7db3,
+ 0x7dcd,0x42ee,0x7dcf,0x7da4,0x42ef,0x434c,0x7f41,0x7f6f,0x7f71,0x435e,
+ 0x435f,0x4376,0x4374,0x4372
+ },
+ { /* ku 29 */
+ 0x4390,0x8023,0x805b,0x43be,0x8061,0x805f,0x8181,0x4426,0x4425,0x8184,
+ 0x8213,0x4474,0x824a,0x824c,0x44fd,0x4505,0x4501,0x84bd,0x8495,0x4509,
+ 0x8492,0x84c3,0x450c,0x8496,0x84a5,0x84b5,0x84b3,0x84a3,0x84e4,0x84d8,
+ 0x84d5,0x450d,0x84b7,0x84ad,0x84da,0x8493,0x8736,0x45c0,0x45c5,0x45c9,
+ 0x873d,0x872b,0x8747,0x8739,0x45d5,0x8745,0x871d,0x4641,0x88ff,0x88ea,
+ 0x4633,0x88f5,0x463a,0x8900,0x88ed,0x8903,0x88e9,0x4640,0x4642,0x89ea,
+ 0x46e8,0x8a9b,0x8a8e,0x8aa2,0x46e4,0x8a9c,0x8a94,0x8a90,0x8aa9,0x8aac,
+ 0x46e7,0x8a9f,0x46e6,0x46e1,0x8a9d,0x4739,0x8c67,0x475c,0x4775,0x8cd0,
+ 0x8cd6,0x8cd4,0x8d98,0x8d9a,0x8d97,0x47ae,0x47b0,0x47fa,0x8e0b,0x8e08,
+ 0x8e01,0x8eb4,0x8eb3,0x485b
+ },
+ { /* ku 2a */
+ 0x8fa1,0x8fa2,0x48a5,0x905a,0x48a2,0x9061,0x905f,0x48db,0x48da,0x9125,
+ 0x917b,0x9176,0x917c,0x4924,0x9289,0x92f6,0x92b1,0x92ad,0x9292,0x9281,
+ 0x9284,0x4926,0x92ae,0x9290,0x929e,0x4998,0x4996,0x499a,0x95a2,0x95a7,
+ 0x4997,0x49e1,0x49e0,0x49e3,0x49e2,0x96a0,0x969d,0x969f,0x96d0,0x49fb,
+ 0x96d1,0x4a12,0x4a14,0x9759,0x4a45,0x9764,0x4a5c,0x4a5d,0x4ab8,0x9819,
+ 0x4aba,0x9814,0x9815,0x981a,0x4b03,0x4b35,0x4b36,0x4b39,0x9906,0x4b2d,
+ 0x98f8,0x9901,0x4b7a,0x99be,0x99bc,0x99b7,0x99b6,0x99c0,0x4b78,0x99b8,
+ 0x4b7b,0x4b7c,0x4b7e,0x99c4,0x4b7d,0x99bf,0x4bc9,0x9ada,0x9ae4,0x9ae9,
+ 0x9ae8,0x9aea,0x9ae5,0x4bf3,0x9b26,0x4c1a,0x4c19,0x9b40,0x4c1f,0x4ca6,
+ 0x4ca7,0x4ca8,0x4cab,0x4ca9
+ },
+ { /* ku 2b */
+ 0x4d2e,0x9ebd,0x4d5e,0x3495,0x3493,0x3492,0x510e,0x3496,0x50f7,0x3497,
+ 0x50fc,0x510d,0x5101,0x51da,0x51d9,0x51db,0x5286,0x528e,0x52ee,0x5333,
+ 0x53b1,0x35f5,0x5647,0x562d,0x5654,0x35ea,0x564b,0x5652,0x5631,0x5644,
+ 0x5656,0x5650,0x562b,0x35f3,0x564d,0x5637,0x564f,0x58a2,0x58b7,0x3669,
+ 0x58b2,0x366b,0x58aa,0x58b5,0x58b0,0x366c,0x58b4,0x58a4,0x58a7,0x3668,
+ 0x5926,0x5afe,0x3728,0x5b04,0x3726,0x5afc,0x3725,0x5b06,0x5b0a,0x5afa,
+ 0x5b0d,0x5b00,0x5b0e,0x376b,0x380f,0x3808,0x5d91,0x380c,0x5d8f,0x5d90,
+ 0x5d98,0x5da4,0x5d9b,0x5da3,0x5d96,0x5de4,0x5e5a,0x3860,0x3862,0x5e5e,
+ 0x3898,0x5fb8,0x6157,0x615c,0x61a6,0x6195,0x6188,0x398a,0x61a3,0x618f,
+ 0x3984,0x6164,0x397f,0x6159
+ },
+ { /* ku 2c */
+ 0x6178,0x3982,0x6185,0x6187,0x619e,0x3996,0x3989,0x6198,0x619c,0x398d,
+ 0x39bc,0x622f,0x6480,0x649b,0x648e,0x648d,0x6494,0x64c6,0x3a44,0x64a8,
+ 0x6483,0x3a3c,0x64b9,0x6486,0x64b4,0x64af,0x6491,0x3a4e,0x64aa,0x64a1,
+ 0x64a7,0x66b6,0x66b3,0x3b14,0x66bc,0x66ac,0x3b15,0x66ad,0x6a0e,0x3bce,
+ 0x6a1c,0x6a1a,0x3be0,0x3bc2,0x6a0b,0x3bbf,0x69ef,0x6a0c,0x69f0,0x6a22,
+ 0x3bc4,0x69d8,0x3bcf,0x6a12,0x69fa,0x3bc8,0x6a2a,0x3bcc,0x6a10,0x3bcd,
+ 0x3bc7,0x6a29,0x69f9,0x69ea,0x6a2c,0x6a24,0x4cb7,0x69e9,0x6b52,0x6b4f,
+ 0x6b53,0x3c43,0x3cb6,0x6f10,0x6f65,0x6f75,0x3d51,0x3d4a,0x3d4d,0x3d56,
+ 0x6fd0,0x3d53,0x6f5c,0x6f3d,0x6f71,0x3d59,0x6f91,0x6f0b,0x6f79,0x6f81,
+ 0x6f8f,0x3d4e,0x6f59,0x6f74
+ },
+ { /* ku 2d */
+ 0x3dee,0x71ae,0x3dec,0x71a3,0x71ad,0x3deb,0x3def,0x71ab,0x71a6,0x71a2,
+ 0x3ded,0x52f2,0x7257,0x7255,0x7299,0x734b,0x747a,0x3ef2,0x3eef,0x3ef1,
+ 0x748c,0x7484,0x3eed,0x3ef0,0x7482,0x7493,0x747b,0x3eee,0x7509,0x4c1b,
+ 0x3f50,0x3f66,0x3684,0x3fb8,0x3ff6,0x778a,0x4057,0x7790,0x405e,0x78c6,
+ 0x78d3,0x78c0,0x78d2,0x78c7,0x78c2,0x4119,0x799f,0x799d,0x799e,0x4170,
+ 0x7a41,0x416e,0x7a38,0x7a3a,0x7a42,0x4172,0x4176,0x7a3e,0x7ab0,0x7bae,
+ 0x7bb3,0x4212,0x421f,0x7bbf,0x4211,0x4216,0x7bcd,0x4219,0x7bb2,0x4224,
+ 0x4214,0x4225,0x4295,0x4296,0x4293,0x4294,0x7cc4,0x7ccd,0x7cc2,0x7cc6,
+ 0x7cc3,0x7cc9,0x7cc7,0x42a0,0x7df8,0x42fb,0x7ded,0x7de2,0x42fc,0x4300,
+ 0x42f8,0x7ddc,0x7e02,0x7e01
+ },
+ { /* ku 2e */
+ 0x42f9,0x7dd6,0x4304,0x7de4,0x7dfe,0x4303,0x7e00,0x7dfc,0x7dfd,0x42f3,
+ 0x7df5,0x7dff,0x42fa,0x7deb,0x7de5,0x7f78,0x7fae,0x7fe7,0x43bf,0x8065,
+ 0x806a,0x8066,0x8068,0x806b,0x8194,0x81a1,0x8192,0x8196,0x8193,0x4479,
+ 0x4510,0x8501,0x4514,0x84f8,0x450e,0x84f5,0x451a,0x8504,0x4519,0x4521,
+ 0x4523,0x451f,0x851b,0x8503,0x8533,0x8534,0x84ed,0x4525,0x452b,0x8535,
+ 0x4516,0x8505,0x4522,0x451b,0x45ce,0x45cf,0x877d,0x45cb,0x45d1,0x45cc,
+ 0x8771,0x4617,0x885c,0x88e6,0x890f,0x891b,0x4651,0x89a9,0x89a5,0x89ee,
+ 0x8ab1,0x46ed,0x8acc,0x8ace,0x46f4,0x8ab7,0x46f1,0x8ab5,0x8ae9,0x8ab4,
+ 0x46f8,0x8ab3,0x8ac1,0x8aaf,0x8aca,0x8ad0,0x472f,0x475e,0x475d,0x8c8e,
+ 0x4776,0x4777,0x8ce9,0x8cdb
+ },
+ { /* ku 2f */
+ 0x477e,0x8ceb,0x8da4,0x47b6,0x8da2,0x8d9d,0x47b3,0x47fc,0x4803,0x4800,
+ 0x8e2a,0x8e28,0x480a,0x4802,0x8eb8,0x8eb6,0x8eb9,0x8eb7,0x8f22,0x8f2b,
+ 0x8f27,0x8f19,0x8fa4,0x4887,0x8fb3,0x48a6,0x9071,0x906a,0x48a9,0x48de,
+ 0x9188,0x918c,0x92bf,0x92b8,0x92be,0x92dc,0x92e5,0x492e,0x492d,0x92d4,
+ 0x92d6,0x4930,0x92da,0x92ed,0x92f3,0x92db,0x492b,0x92b9,0x92e2,0x92eb,
+ 0x95af,0x499e,0x95b2,0x95b3,0x499f,0x49e5,0x49e4,0x96a3,0x96a5,0x49fd,
+ 0x49fc,0x4a17,0x4a19,0x970a,0x4a18,0x9787,0x9789,0x978c,0x97ef,0x982a,
+ 0x9822,0x4abf,0x981f,0x4b3c,0x9919,0x4b6b,0x99ca,0x99da,0x4b83,0x4b81,
+ 0x4b80,0x99de,0x99c8,0x99e0,0x4bca,0x9ab6,0x9ab5,0x4bce,0x9af4,0x4bf6,
+ 0x9b6b,0x9b69,0x9b72,0x9b63
+ },
+ { /* ku 30 */
+ 0x4c39,0x9d0d,0x4cae,0x9d01,0x9d0c,0x4cb5,0x9cf8,0x4cb3,0x4cb4,0x9cfe,
+ 0x9d02,0x9e84,0x4d22,0x9eab,0x9eaa,0x511d,0x5116,0x3499,0x512b,0x511e,
+ 0x511b,0x5290,0x5294,0x5314,UBOGON,0x3602,0x5667,0x3601,0x567b,0x36a1,
+ 0x565f,0x5661,0x35fd,0x3673,0x3674,0x3670,0x3676,0x3675,0x3672,0x58c3,
+ 0x58ca,0x58bb,0x58c0,0x58c4,0x5901,0x5b1f,0x5b18,0x5b11,0x5b15,0x3729,
+ 0x5b12,0x5b1c,0x372a,0x5b22,0x5b79,0x5da6,0x3816,0x5db3,0x5dab,0x5eea,
+ 0x3899,0x5f5b,0x38d3,0x38f5,0x61b7,0x61ce,0x61b9,0x61bd,0x61cf,0x61c0,
+ 0x6199,0x6197,0x3994,0x61bb,0x61d0,0x61c4,0x6231,0x3a56,0x64d3,0x64c0,
+ 0x3a59,0x3a58,0x3a55,0x3a52,0x64dc,0x64d1,0x64c8,0x3a57,0x64d5,0x66c3,
+ 0x3b1b,0x3b1c,0x66bf,0x66c5
+ },
+ { /* ku 31 */
+ 0x3b19,0x66cd,0x66c1,0x6706,0x3b3f,0x6724,0x6a63,0x6a42,0x6a52,0x3bdb,
+ 0x6a43,0x6a33,0x3be2,0x6a6c,0x6a57,0x3bd7,0x6a4c,0x6a6e,0x3bde,0x3be5,
+ 0x3be4,0x3be6,0x3bd6,0x6a37,0x3bdf,0x6a71,0x6a4a,0x6a36,0x3bdc,0x6a53,
+ 0x3bda,0x6a45,0x6a70,0x3bd3,0x3bd0,0x6a5c,0x6b58,0x6b57,0x3c86,0x3c87,
+ 0x3cad,0x3cb7,0x3d58,0x3d6a,0x6fbb,0x3d62,0x3d61,0x6fbe,0x3d69,0x3d6c,
+ 0x3d65,0x6fb5,0x6fd3,0x6f9f,0x3d66,0x6fb7,0x6ff5,0x71b7,0x3df5,0x71bb,
+ 0x3df4,0x71d1,0x3df7,0x71ba,0x3df8,0x71b6,0x71cc,0x3dfb,0x3dfc,0x71d3,
+ 0x749b,0x3ef5,0x3ef8,0x7496,0x74a2,0x749d,0x750a,0x750e,0x3f3c,0x7581,
+ 0x762c,0x7637,0x7636,0x763b,0x3fc5,0x76a1,0x4062,0x4063,0x7798,0x4067,
+ 0x7796,0x4066,0x40d9,0x40db
+ },
+ { /* ku 32 */
+ 0x78d6,0x78eb,0x40d8,0x78dc,0x411b,0x79a5,0x79a9,0x9834,0x7a53,0x7a45,
+ 0x4179,0x7a4f,0x417d,0x7abd,0x7abb,0x7af1,0x422c,0x4237,0x7bec,0x7bed,
+ 0x4230,0x429a,0x7cd3,0x4a00,0x7ce1,0x4305,0x7e19,0x4307,0x4309,0x430a,
+ 0x7e27,0x7e26,0x4379,0x43c2,0x806e,0x81af,0x4438,0x4437,0x81ad,0x4421,
+ 0x81aa,0x8218,0x445e,0x453d,0x4537,0x4540,0x856f,0x854c,0x451d,0x8542,
+ 0x4533,0x855c,0x8570,0x855f,0x4535,0x855a,0x854b,0x853f,0x878a,0x45d8,
+ 0x878b,0x87a1,0x878e,0x45dc,0x45de,0x8799,0x885e,0x885f,0x8924,0x89a7,
+ 0x8aea,0x8afd,0x8af9,0x8ae3,0x8ae5,0x46fa,0x46fb,0x8aec,0x473d,0x473b,
+ 0x473f,0x475f,0x8cf2,0x477f,0x8cef,0x4784,0x8da6,0x47bc,0x4814,0x480f,
+ 0x8e3b,0x8e43,0x480e,0x8e32
+ },
+ { /* ku 33 */
+ 0x8f31,0x8f30,0x4860,0x8f2d,0x8f3c,0x8fa7,0x8fa5,0x48ab,0x48ac,0x48aa,
+ 0x9137,0x9195,0x918e,0x4904,0x9196,0x4908,0x9345,0x930a,0x4933,0x4934,
+ 0x92fd,0x9317,0x931c,0x9307,0x9331,0x9332,0x932c,0x9330,0x9303,0x9305,
+ 0x49a2,0x95c2,0x49a4,0x95b8,0x49a5,0x95c1,0x49a7,0x49a6,0x49e7,0x96ab,
+ 0x96b7,0x49ff,0x49fe,0x9715,0x9714,0x4a1d,0x4a1c,0x970c,0x9717,0x4a67,
+ 0x9793,0x4a94,0x97d2,0x4ac5,0x4ac8,0x9836,0x9831,0x9833,0x983c,0x982e,
+ 0x983a,0x4ac9,0x983d,0x4ac7,0x98b5,0x9922,0x9923,0x9920,0x991c,0x991d,
+ 0x4b6c,0x99a0,0x4b8a,0x99ef,0x99e8,0x99eb,0x4b88,0x4b87,0x4b86,0x99e1,
+ 0x99e6,0x4bcf,0x4bd0,0x9af8,0x9af5,0x4c1c,0x4c23,0x9b83,0x9b94,0x9b84,
+ 0x4c49,0x9b8b,0x9b8f,0x4c43
+ },
+ { /* ku 34 */
+ 0x9b8c,0x4c48,0x9b89,0x4c47,0x9b8e,0x4c46,0x4c3f,0x4c44,0x9d24,0x9d0f,
+ 0x4cbe,0x9d13,0x9d0a,0x4cc2,0x4cba,0x4cbc,0x4cc6,0x9d2a,0x9d1a,0x4cc8,
+ 0x9d27,0x9d16,0x9d21,0x4d23,0x9e85,0x9eac,0x9ec6,0x9ec5,0x9ed7,0x9f53,
+ 0x349d,0x5128,0x5127,0x51df,0x3524,0x5335,0x53b3,0x3607,0x568a,0x567d,
+ 0x5689,0x3679,0x58cd,0x58d0,0x3678,0x5b2b,0x5b33,0x5b29,0x5b35,0x5b31,
+ 0x5b37,0x5c36,0x5dbe,0x3819,0x5db9,0x381c,0x5dbb,0x3818,0x61e2,0x61db,
+ 0x61dd,0x61dc,0x61da,UBOGON,0x61d9,0x39bd,0x3a5d,0x64df,0x3a5a,0x3a5e,
+ 0x64e1,0x3a5c,0x64ee,0x3a5b,0x65b5,0x66d4,0x66d5,0x3b21,0x66d0,0x66d1,
+ 0x66ce,0x66d7,0x3b20,0x3b32,0x6a7d,0x6a8a,0x3bf2,0x6aa7,0x3bf5,0x6a99,
+ 0x6a82,0x6a88,0x3bee,0x3bec
+ },
+ { /* ku 35 */
+ 0x6a86,0x3bea,0x6a98,0x6a9d,0x3bed,0x3bf3,0x6a8f,0x3bf6,0x6aaa,0x3c48,
+ 0x6b5d,0x3c49,0x6c0a,0x3d75,0x6fd7,0x6fd6,0x6fe5,0x3d6f,0x3d7b,0x3d73,
+ 0x6fd9,0x6fda,0x6fea,0x3d70,0x6ff6,UBOGON,0x3d78,0x71e3,0x3dfe,0x71e9,
+ 0x3e00,0x71eb,0x71ef,0x71f3,0x71ea,0x3e01,UBOGON,0x3e55,0x3e56,0x3e9d,
+ 0x7371,0x3ef9,0x74ae,0x3eff,0x74b3,0x3efd,0x74ac,0x3f43,0x3f41,0x7583,
+ 0x7645,0x764e,0x7644,0x76a3,0x76a5,0x77a6,0x77a4,0x406f,0x77a9,0x77af,
+ 0x408a,0x40e5,0x40e6,0x78f0,0x78f8,0x78f1,0x417f,0x7a49,0x41b5,0x41b6,
+ 0x41bb,0x7ac2,0x7af2,0x7af3,0x7bfa,0x4240,0x7bf6,0x7bfc,0x7c18,0x7c08,
+ 0x7c12,0x429d,0x429c,0x7cdb,0x7cda,0x430f,0x4311,0x430d,0x7e2c,0x7e4d,
+ 0x4314,0x4313,0x7f46,0x7ff6
+ },
+ { /* ku 36 */
+ 0x802b,0x8074,0x81b8,0x81c8,0x4482,0x4483,0x454d,0x8592,0x8593,0x454f,
+ 0x857f,0x85ab,0x8597,0x454c,0x4551,0x85ac,0x45ee,0x45e8,0x4ccb,0x87ce,
+ 0x45eb,0x87cd,0x45e2,0x45e6,0x87c1,0x87b1,0x87c7,0x45ec,0x8940,0x4659,
+ 0x893f,0x8939,0x465d,0x8943,0x4657,0x465b,0x4656,0x89ab,0x46fe,0x8b1f,
+ 0x8b09,0x8b0c,0x4700,0x4701,0x8c40,0x4742,0x8c96,0x4760,0x8cf6,0x8cf7,
+ 0x481d,0x8e46,0x8e4f,0x483e,0x4869,0x4865,0x8f3d,0x8f41,0x9366,0x9378,
+ 0x935d,0x9369,0x9374,0x937d,0x936e,0x9372,0x9373,0x9362,0x9348,0x9353,
+ 0x935f,0x9368,0x4938,0x937f,0x936b,0x49ae,0x95c4,0x49ad,0x96af,0x96ad,
+ 0x96b2,0x4a02,0x4a1f,0x971a,0x971b,0x4a22,0x4a20,UBOGON,0x4a6c,0x979b,
+ 0x979f,0x4a68,0x4a6d,0x4a6e
+ },
+ { /* ku 37 */
+ 0x4aa0,0x4ace,0x4ad0,0x4ad1,0x4acb,0x9840,0x4ad2,0x9847,0x4ad3,0x98b7,
+ 0x4b20,0x4b4e,0x4b4b,0x4b72,0x4b70,0x99a2,0x4b92,0x4b8f,0x9a00,0x99f3,
+ 0x4b90,UBOGON,0x99f5,0x4bd9,0x4bd5,0x9abd,0x9b00,0x9b02,0x4bfa,0x9b34,
+ 0x9b49,0x9b9f,0x4c4b,0x9ba3,0x9bcd,0x9b99,0x9b9d,0x4cd0,0x4cce,0x9d39,
+ 0x4ccf,0x9d44,0x4cc4,0x4ccc,0x9d35,0x4cd2,0x4d35,0x9eaf,0x3e03,0x512f,
+ 0x349e,0x34af,0x9f8e,0x360c,0x569f,0x569b,0x569e,0x5696,0x5694,0x56a0,
+ 0x367c,0x5b3b,0x3730,0x3731,0x5b3a,0x5dc1,0x5f4d,0x5f5d,0x61f3,0x39a1,
+ 0x399e,0x3a68,0x3a61,0x64f6,0x64e5,0x64ea,0x64e7,0x6505,0x3a65,0x64f9,
+ 0x3a66,0x3a6a,0x3aab,0x6aab,0x6aed,0x6ab2,0x6ab0,0x6ab5,0x6abe,0x6ac1,
+ 0x6ac8,0x3bf9,0x6ac0,0x6abc
+ },
+ { /* ku 38 */
+ 0x6ab1,0x6ac4,0x6abf,0x3c58,0x3c8a,0x7008,0x7003,0x6ffd,0x7010,0x7002,
+ 0x7013,0x3e04,0x71fa,0x7200,0x74b9,0x74bc,0x3f02,0x765b,0x7651,0x764f,
+ 0x76eb,0x77b8,0x4079,0x77b9,0x77c1,0x77c0,0x77be,0x790b,0x40eb,0x7907,
+ 0x790a,0x7908,0x40e9,0x790d,0x7906,0x7915,0x79af,0x4120,0x4121,0x4181,
+ 0x7af5,0x424d,0x4259,0x7c2e,0x4258,0x7c1b,UBOGON,0x7c1a,0x7c24,0x42a5,
+ 0x42a9,0x7ce6,0x7ce3,0x431a,0x4319,0x7e5d,0x7e4f,0x7e66,0x7e5b,0x7f47,
+ 0x7fb4,0x4396,0x4398,0x4397,0x7ffa,0x802e,UBOGON,0x43c8,0x81ce,0x4443,
+ 0x4445,0x8219,0x4552,0x4557,0x85cc,0x85b2,0x4555,0x85bb,0x85c1,0x4556,
+ 0x4558,0x45f2,0x87e9,0x87ee,0x87f0,0x87d6,0x880e,0x87da,0x8948,0x894a,
+ 0x894e,0x894d,0x89b1,0x89b0
+ },
+ { /* ku 39 */
+ 0x89b3,0x4707,0x8b38,0x8b32,0x4708,0x8b2d,0x470a,0x8b34,0x431b,0x8b29,
+ 0x8c74,0x4761,0x4762,0x8d03,0x47c2,0x47c6,0x8da9,0x8e58,0x481e,0x4825,
+ 0x8ebf,0x8ec1,0x8f4a,0x8fac,0x48b0,0x9089,0x913d,0x913c,0x91a9,0x93a0,
+ 0x493d,0x9390,0x493e,0x9393,0x938b,0x93ad,0x93bb,0x93b8,0x4946,0x4945,
+ 0x939c,0x95d8,0x95d7,0x4a03,0x4a26,0x4a27,0x975d,0x97a9,0x97da,0x4a98,
+ 0x4aad,0x4ad5,0x4ada,0x9854,0x4ad9,0x9855,0x984b,0x4add,0x983f,0x98b9,
+ 0x4b15,0x4b16,0x4b17,0x4b21,0x9938,0x9936,0x9940,0x4b4c,0x993b,0x9939,
+ 0x99a4,0x4b96,0x4b98,0x9a08,0x9a0c,0x4b9b,0x9a10,0x4bff,0x9b07,0x4c25,
+ 0x9bd2,0x4c4f,0x9bc2,0x9bbb,0x9bcc,0x9bcb,0x4c56,0x4c54,0x9d4d,0x9d63,
+ 0x9d4e,0x4cd8,0x9d50,0x9d55
+ },
+ { /* ku 3a */
+ 0x4cd7,0x9d5e,0x4d26,0x9e90,0x9eb2,0x9eb1,0x4d38,0x9eca,0x9f02,0x9f27,
+ 0x9f26,0x4d8a,0x56af,0x58e0,0x58dc,0x3734,0x5b39,0x3735,UBOGON,0x5b7c,
+ 0x5bf3,UBOGON,0x37a1,0x5c6b,0x5dc4,0x650b,0x6508,0x650a,0x3a6c,0x3a6d,
+ 0x65dc,0x3b29,0x3b2a,0x66e1,0x66df,0x6ace,0x6ad4,0x6ae3,0x6ad7,0x6ae2,
+ 0x3c00,0x3c08,0x3c06,0x3c05,0x6ad8,0x6ad5,0x6ad2,0x3cb1,0x3d88,0x701e,
+ 0x702c,0x7025,0x6ff3,0x7204,0x7208,0x7215,0x3e09,0x74c4,0x74c9,0x74c7,
+ 0x74c8,0x76a9,0x77c6,0x77c5,0x7918,0x791a,0x7920,0x4122,0x7a66,0x7a64,
+ 0x7a6a,0x41d5,0x4261,0x425d,0x4262,0x424f,0x4260,0x7c35,0x7c34,0x42aa,
+ 0x4322,0x7e6c,0x4321,0x7e6e,0x7e71,0x4446,0x81d4,0x81d6,0x821a,0x8262,
+ 0x8265,0x8276,0x85db,0x85d6
+ },
+ { /* ku 3b */
+ 0x4562,0x85e7,0x4560,0x4564,0x85f4,UBOGON,0x87fd,0x87d5,0x8807,0x45f6,
+ 0x880f,0x87f8,UBOGON,0x4619,0x8987,0x4691,0x89b5,0x89f5,0x470d,0x8b3f,
+ 0x8b43,0x8b4c,0x4765,0x8d0b,0x8e6b,0x8e68,0x8e70,0x8e75,0x8e77,0x483f,
+ 0x8ec3,0x494b,0x93e9,0x93ea,0x93cb,0x93c5,0x93c6,0x4948,0x93ed,0x93d3,
+ 0x4952,0x93e5,0x494a,0x4951,0x93db,0x93eb,0x93e0,0x93c1,0x4950,0x494c,
+ 0x95dd,0x49ee,0x4a04,0x4a06,0x4a2d,0x4a2e,0x4a2f,0x4a7b,0x4a78,0x4a77,
+ 0x97b2,0x97b4,0x97b1,0x97b5,0x97f2,0x4aa2,0x4aa1,0x4ae3,0x9856,0x4b1a,
+ 0x4b19,0x4b57,0x9944,0x4b9e,0x9a26,0x9a1f,0x9a18,0x9a21,0x9a17,0x4bdd,
+ 0x9b09,0x4c05,0x4c28,0x9bc5,0x9bdf,0x4c60,0x9be3,0x4c66,0x9be9,0x9bee,
+ 0x4c67,0x4c68,0x9d66,0x9d7a
+ },
+ { /* ku 3c */
+ 0x4cde,0x9d6e,0x9d91,0x9d83,0x9d76,0x9d7e,0x9d6d,0x4ce1,0x9e95,0x9ee3,
+ 0x4d69,0x4d77,0x9f03,0x9f04,UBOGON,0x9f17,0x34a6,0x5136,0x34a5,0x5336,
+ 0x3614,0x5b42,0x3736,0x3738,0x5b44,0x5b46,0x5b7e,0x5dca,0x5dc8,0x5dcc,
+ 0x5ef0,0x3a70,0x6585,0x66e5,0x66e7,0x3b2b,0x3c11,0x3c0a,0x6af4,0x3c0d,
+ 0x6ae9,0x3c16,0x3c10,0x3c09,0x3c0e,0x3c7a,0x703d,0x3d8c,0x7036,0x3d91,
+ 0x7216,0x3e0a,0x7212,0x720f,0x7217,0x7211,0x720b,0x3e08,0x3e0b,0x74cd,
+ 0x74d0,0x74cc,0x74ce,0x74d1,0x3f07,0x7589,0x40f2,0x7a6f,0x7c4b,0x7c44,
+ 0x7c55,0x42ae,0x4324,0x4326,0x4327,0x7e7f,0x8b71,0x4399,0x802f,0x807a,
+ 0x807b,0x807c,0x455f,0x456a,0x4571,0x85fc,0x8610,0x8602,0x456c,0x456f,
+ 0x85ee,0x8603,0x4568,0x860d
+ },
+ { /* ku 3d */
+ 0x8613,0x8608,0x860f,0x8818,0x8812,0x4601,0x4668,0x8967,0x8965,0x89bb,
+ 0x8b69,0x8b62,0x4713,0x8b6e,0x4716,0x8b61,0x4718,0x8b64,0x8b4d,0x8c51,
+ 0x4789,0x47c8,0x8e83,0x8ec6,0x4884,0x941f,0x4954,0x9404,0x9417,0x9408,
+ 0x9405,0x4956,0x93f3,0x941e,0x9402,0x941a,0x941b,0x9427,0x941c,0x495a,
+ 0x96b5,0x4a05,0x4a07,0x9733,0x4a31,0x9734,0x9731,0x97b8,0x97ba,0x4aa3,
+ 0x97fc,0x4aeb,0x4b1c,0x98c3,0x4b5a,0x994d,0x4b5b,0x9a2f,0x4ba6,0x4baa,
+ 0x4ba5,0x9ac9,0x4be1,0x9ac8,0x9ac4,0x9b2a,0x9b38,0x9b50,0x4c2a,0x9c0a,
+ 0x9bfb,0x9c04,0x9bfc,0x9bfe,0x4c72,0x4c6f,0x4c73,0x9c02,0x9bf6,0x9c1b,
+ 0x9bf9,0x9c15,0x9c10,0x9bff,0x9c00,0x9c0c,0x4c6b,0x4ce6,0x9d95,0x9da5,
+ 0x4ce9,0x4cec,0x4ce8,0x4cf0
+ },
+ { /* ku 3e */
+ 0x9e98,0x9ec1,0x4d8c,0x9f5a,0x5164,0x56bb,0x3615,0x58e6,0x5b49,0x5bf7,
+ 0x3771,0x3826,0x5dd0,0x38c6,0x5fc2,0x39a8,0x6511,0x3a73,0x6aff,0x6afe,
+ 0x6afd,0x3c15,0x6b01,0x3d98,0x3d97,0x704b,0x704d,0x7047,0x74d3,0x7668,
+ 0x7667,0x3fd7,0x4080,0x77d1,0x7930,0x7932,0x792e,0x4188,0x9f9d,0x7ac9,
+ 0x7ac8,0x4269,0x7c56,0x7c51,0x426b,0x4329,0x4328,0x7e85,0x7e89,0x7e8e,
+ 0x7e84,0x445f,0x826a,0x862b,0x862f,0x8628,0x4574,0x8616,0x8615,0x861d,
+ 0x881a,0x4602,0x466a,0x4694,0x89bc,0x8b75,0x8b7c,0x478a,0x8d11,0x8d12,
+ 0x8f5c,0x91bb,0x4964,0x93f4,0x495e,0x4961,0x942d,0x4965,0x4966,0x96e4,
+ 0x9737,0x9736,0x9767,0x97be,0x97bd,0x97e2,0x9868,0x9866,0x98c8,0x98ca,
+ 0x98c7,0x98dc,0x4b5f,0x994f
+ },
+ { /* ku 3f */
+ 0x99a9,0x9a3c,0x4baf,0x9a3b,0x9ace,0x4c0d,0x9b14,0x9b53,0x4c7c,0x9c2e,
+ 0x4c7a,0x9c1f,0x4c76,0x4c79,0x4c7d,0x4c77,0x9db0,0x9dbd,0x4cf6,0x4cf1,
+ 0x9dae,0x9dc4,0x9e7b,0x400b,0x4d29,0x9e9e,0x4d6f,0x9f05,0x4d9a,0x9f69,
+ 0x9fa1,0x56c7,0x571d,0x5b4a,0x5dd3,0x3869,0x5f72,0x6202,0x39ab,0x6235,
+ 0x6527,0x651e,0x651f,0x3b2c,0x3b2d,0x6b07,0x6b06,0x3c17,0x3d9a,0x7054,
+ 0x721c,0x7220,0x7af8,0x426e,0x7c5d,0x7c58,0x432c,0x7e92,0x7f4e,0x43ca,
+ 0x4578,0x4606,0x8827,0x4607,0x8b81,0x8b83,0x4720,0x8c44,0x4753,0x47ce,
+ 0x487a,0x4879,0x9442,0x944d,0x9454,0x944e,0x496b,0x9443,0x4967,0x496d,
+ 0x973c,0x9740,0x97c0,0x4a85,0x4ab0,0x4af3,0x4b63,0x995a,0x9a51,0x4bb6,
+ 0x9add,0x4c82,0x4c7f,0x9c38
+ },
+ { /* ku 40 */
+ 0x4c86,0x9c45,0x9c3a,0x4c84,0x9c35,0x4cfc,0x4cfd,0x4cfa,0x9ef1,0x4d87,
+ 0x9f93,0x529a,0x361a,0x3619,0x8641,0x5dd7,0x3a75,0x6528,0x3c1a,0x3c1b,
+ 0x3c19,0x7053,0x7059,0x3d9c,0x7221,0x3e10,0x766f,0x7937,0x79b5,0x7c62,
+ 0x7c5e,0x7cf5,0x457b,0x457c,0x863d,0x4608,0x882d,0x8989,0x8b8d,0x8b87,
+ 0x8b90,0x8d1a,0x8e99,0x4841,0x48e3,0x4972,0x945f,0x4973,0x4968,0x9456,
+ 0x9461,0x945b,0x945a,0x945c,0x9465,0x4a35,0x9741,0x4a88,0x4a9d,0x986e,
+ 0x986c,0x986d,0x4275,0x99aa,0x9a5c,0x9a58,0x9ade,0x4c8f,0x9c4f,0x9c51,
+ 0x4c8e,0x9c53,0x4d05,0x4d04,0x4cff,0x9dfc,0x9f39,0x4d9e,0x513e,0x3554,
+ 0x56d2,0x3681,0x5b4f,0x6b14,0x40fa,0x7a72,0x7a73,0x4332,0x4670,0x466e,
+ 0x8b91,UBOGON,0x487c,0x91bf
+ },
+ { /* ku 41 */
+ 0x4975,0x946c,0x4974,0x4977,0x96e6,0x9745,0x4a37,0x97c8,0x97e4,0x995d,
+ 0x4bba,0x9b21,0x4c11,0x9b2c,0x9b57,0x4c92,0x4c99,0x9c5d,0x9c61,0x9c65,
+ 0x9e08,0x4d0a,0x4d2a,0x4d2b,0x4d44,0x4d79,0x9f45,0x34aa,0x3748,0x6205,
+ 0x66ef,0x6b1b,0x6b1d,0x7225,0x7224,0x7c6d,0x42b4,0x8642,0x8649,0x460d,
+ 0x8978,0x898a,0x8b97,0x4754,0x8c9b,0x8d1c,0x4830,0x8ea2,0x4a09,0x4a38,
+ 0x4a36,0x4a8b,0x4af7,0x4b66,0x4bbd,0x4c1e,0x9c6c,0x4c96,0x9c6f,0x4d0d,
+ 0x9e0e,0x4d73,0x9f08,0x9f1d,0x9fa3,0x373b,0x373c,0x5f60,0x6b1c,0x3da0,
+ 0x40fb,UBOGON,0x7cf3,0x4581,0x8b9b,0x8ea7,0x91c4,0x4978,0x947a,0x4a8d,
+ 0x4b73,0x9a61,0x9a63,0x9ad7,0x9c76,0x4da6,0x9fa5,0x39ad,0x7067,0x3e11,
+ 0x72ab,0x864a,0x897d,0x8b9d
+ },
+ { /* ku 42 */
+ 0x8c53,0x8f65,0x947b,0x4a39,0x98cd,0x98dd,0x4bbf,0x9b30,0x9e16,0x4d0f,
+ 0x4da7,0x4db5,0x3fdc,0x4831,0x96e7,0x9e18,0x9ea2,0x4da8,0x9f7c,0x4125,
+ 0x7e9e,0x9484,0x4bc1,0x9e1c,0x4190,0x7c71,0x97ca,0x4696,0x487f,0x4d10,
+ 0x9ea3,0x4a0a,0x9c7b,0x9f97,0x4d12,0x4a3a,0x9750,0x4a3b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f66,
+ 0x4f68,0x4fe7,0x503f,UBOGON,0x50a6,0x510f,0x523e,0x5324,0x5365,0x539b,
+ 0x517f,0x54cb,0x5573,0x5571,0x556b,0x55f4,0x5622,0x5620,0x5692,0x56ba,
+ 0x5691,0x56b0,0x5759,0x578a,0x580f,0x5812,0x5813,0x5847,0x589b,0x5900,
+ 0x594d,0x5ad1,0x5ad3,0x5b67,0x5c57,0x5c77,0x5cd5,0x5d75,0x5d8e,0x5da5,
+ 0x5db6,0x5dbf,0x5e65,0x5ecd,0x5eed,0x5f94,0x5f9a,0x5fba,0x6125,0x6150,
+ 0x62a3,0x6360,0x6364,0x63b6
+ },
+ { /* ku 45 */
+ 0x6403,0x64b6,0x651a,0x7a25,0x5c21,0x66e2,0x6702,0x67a4,0x67ac,0x6810,
+ 0x6806,0x685e,0x685a,0x692c,0x6929,0x6a2d,0x6a77,0x6a7a,0x6aca,0x6ae6,
+ 0x6af5,0x6b0d,0x6b0e,0x6bdc,0x6bdd,0x6bf6,0x6c1e,0x6c63,0x6da5,0x6e0f,
+ 0x6e8a,0x6e84,0x6e8b,0x6e7c,0x6f4c,0x6f48,0x6f49,0x6f9d,0x6f99,0x6ff8,
+ 0x702e,0x702d,0x705c,0x79cc,0x70bf,0x70ea,0x70e5,0x7111,0x7112,0x713f,
+ 0x7139,0x713b,0x713d,0x7177,0x7175,0x7176,0x7171,0x7196,0x7193,0x71b4,
+ 0x71dd,0x71de,0x720e,0x5911,0x7218,0x7347,0x7348,0x73ef,0x7412,0x743b,
+ 0x74a4,0x748d,0x74b4,0x7673,0x7677,0x76bc,0x7819,0x781b,0x783d,0x7853,
+ 0x7854,0x7858,0x78b7,0x78d8,0x78ee,0x7922,0x794d,0x7986,0x7999,0x79a3,
+ 0x79bc,0x7aa7,0x7b37,0x7b59
+ },
+ { /* ku 46 */
+ 0x7bd0,0x7c2f,0x7c32,0x7c42,0x7c4e,0x7c68,0x7ca9,0x7ced,0x7dd0,0x7e07,
+ 0x7dd3,0x7e64,0x7f40,UBOGON,0x8041,0x8063,0x80bb,0x6711,0x6725,0x8248,
+ 0x8310,0x8362,0x8312,0x8421,0x841e,0x84e2,0x84de,0x84e1,0x8573,0x85d4,
+ 0x85f5,0x8637,0x8645,0x8672,0x874a,0x87a9,0x87a5,0x87f5,0x8834,0x8850,
+ 0x8887,0x8954,0x8984,0x8b03,0x8c52,0x8cd8,0x8d0c,0x8d18,0x8db0,0x8ebc,
+ 0x8ed5,0x8faa,0x909c,UBOGON,0x915c,0x922b,0x9221,0x9273,0x92f4,0x92f5,
+ 0x933f,0x9342,0x9386,0x93be,0x93bc,0x93bd,0x93f1,0x93f2,0x93ef,0x9422,
+ 0x9423,0x9424,0x9467,0x9466,0x9597,0x95ce,0x95e7,0x973b,0x974d,0x98e4,
+ 0x9942,0x9b1d,0x9b98,UBOGON,0x9d49,0x6449,0x5e71,0x5e85,0x61d3,0x990e,
+ 0x8002,0x781e,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ 0x5528,0x5572,0x55ba,0x55f0,0x55ee,0x56b8,0x56b9,0x56c4,0x8053,0x92b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 4 conversion table */
+
+static const unsigned short
+ cns11643_4tab[MAX_CNS11643_KU_4][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,0x4e40,0x4e41,0x4e5a,UBOGON,0x4e02,0x4e29,UBOGON,UBOGON,0x5202,
+ 0x353e,0x5ddc,UBOGON,UBOGON,UBOGON,0x5342,0x536a,0x5b52,UBOGON,UBOGON,
+ UBOGON,0x5fc4,0x624c,0x72ad,0x4e12,0x4e2f,0x4e96,0x4ed0,0x5142,0x5183,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5383,0x53b8,UBOGON,UBOGON,UBOGON,
+ 0x5928,UBOGON,0x5c23,0x5e01,0x5f00,UBOGON,0x3cb8,0x706c,0x722b,0x5188,
+ 0x8279,0x8fb6,0x4e17,UBOGON,0x340c,UBOGON,0x3430,0x4ee2,0x4edb,UBOGON,
+ UBOGON,0x51ad,UBOGON,0x51f7,0x34da,UBOGON,UBOGON,0x3513,0x531b,0x5388,
+ 0x5387,UBOGON,0x53cf,0x53fd,0x3563,0x53e7,0x56dc,UBOGON,0x56d9,0x5725,
+ 0x5727,0x5933,0x5c13,UBOGON,UBOGON,0x5c75,UBOGON,UBOGON,UBOGON,0x39c4,
+ 0x39c3,0x66f1,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ 0x7f52,UBOGON,UBOGON,0x3401,UBOGON,UBOGON,0x4e51,0x4e6a,UBOGON,0x4f0c,
+ UBOGON,UBOGON,0x4efe,0x4f1b,UBOGON,UBOGON,0x343a,UBOGON,0x34ab,0x5173,
+ UBOGON,0x518e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34dd,UBOGON,UBOGON,
+ 0x52a5,0x3515,0x52a7,0x52a4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53bd,
+ UBOGON,UBOGON,UBOGON,0x5402,UBOGON,UBOGON,UBOGON,UBOGON,0x572b,0x591b,
+ 0x5935,UBOGON,0x36a7,0x36a5,UBOGON,UBOGON,0x36a6,UBOGON,UBOGON,0x5c17,
+ 0x377c,UBOGON,0x5c70,0x5c7d,0x37a9,UBOGON,0x5de9,UBOGON,0x3834,0x3835,
+ UBOGON,UBOGON,UBOGON,0x38a8,0x5f19,0x5f1c,0x5f75,UBOGON,UBOGON,0x38ff,
+ 0x5fc8,UBOGON,0x39c7,0x39c6,0x39c8,UBOGON,UBOGON,0x3ad0,0x3ad1,UBOGON,
+ UBOGON,0x3c59,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ 0x6c12,0x3cbd,UBOGON,UBOGON,UBOGON,0x3e28,0x72b3,UBOGON,0x3ea9,0x7390,
+ 0x7536,UBOGON,0x43cc,UBOGON,0x8281,0x8fb8,UBOGON,0x48b4,UBOGON,UBOGON,
+ 0x4e23,0x3416,0x342c,UBOGON,0x4f2e,UBOGON,0x514f,UBOGON,0x51ba,0x34df,
+ 0x34e0,0x5222,UBOGON,UBOGON,UBOGON,0x3517,UBOGON,0x52af,0x52b0,0x52b1,
+ UBOGON,UBOGON,0x352f,UBOGON,0x5364,UBOGON,0x53d3,UBOGON,0x3574,UBOGON,
+ 0x3570,0x356d,UBOGON,UBOGON,UBOGON,UBOGON,0x356e,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x362b,0x3628,UBOGON,UBOGON,0x593f,UBOGON,UBOGON,0x3692,UBOGON,
+ 0x598b,UBOGON,0x5991,0x5995,UBOGON,UBOGON,0x373f,UBOGON,0x5b8a,0x374f,
+ 0x3774,UBOGON,UBOGON,0x377d,UBOGON,0x37b7,0x37a3,0x37b0,0x37b1,0x5c87,
+ 0x37ab,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0x383a,0x3837,0x5e0d,0x3838,0x3840,UBOGON,UBOGON,0x5e8e,0x389f,UBOGON,
+ UBOGON,0x5f7a,UBOGON,0x3904,0x3909,0x3906,0x38fd,0x390a,0x3907,UBOGON,
+ UBOGON,0x39ca,UBOGON,UBOGON,0x6290,0x39c9,UBOGON,0x629a,UBOGON,0x653c,
+ 0x653a,0x3a7f,0x6598,UBOGON,0x3ad2,UBOGON,UBOGON,0x6765,UBOGON,0x3b43,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cc1,UBOGON,0x3cc5,0x3da3,
+ UBOGON,UBOGON,0x3e2a,0x3e5f,UBOGON,UBOGON,0x3e5d,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3f17,UBOGON,UBOGON,0x3f71,0x3f72,UBOGON,UBOGON,0x400f,UBOGON,
+ UBOGON,0x79c2,0x4191,UBOGON,UBOGON,UBOGON,0x43b2,0x43cf,0x43ce,0x809e,
+ UBOGON,UBOGON,0x81eb,UBOGON,0x8289,0x4496,UBOGON,UBOGON,0x8296,UBOGON,
+ 0x8287,UBOGON,0x4497,UBOGON
+ },
+ { /* ku 05 */
+ 0x8fc0,0x488b,0x8fc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9578,UBOGON,UBOGON,0x9625,UBOGON,0x4e75,0x4e74,UBOGON,UBOGON,0x342d,
+ UBOGON,0x4f99,UBOGON,0x3450,0x344b,UBOGON,0x344f,0x344c,UBOGON,0x4f71,
+ 0x5153,0x51bf,UBOGON,UBOGON,0x51c0,UBOGON,0x51ee,UBOGON,0x34e4,0x34e3,
+ UBOGON,0x34e1,UBOGON,0x34e2,UBOGON,0x523d,0x3519,0x52bd,0x530c,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3541,0x7f37,UBOGON,0x53c0,0x355e,UBOGON,UBOGON,
+ 0x3579,UBOGON,0x546e,0x5483,UBOGON,UBOGON,0x545e,0x545d,0x577e,0x5779,
+ UBOGON,0x577a,0x576c,UBOGON,UBOGON,UBOGON,0x3632,0x5787,UBOGON,0x591d,
+ 0x3694,0x5946,0x3697,UBOGON,0x5943,UBOGON,0x3696,0x3698,UBOGON,UBOGON,
+ 0x36b2,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x36b9,0x5b61,0x5b66,UBOGON,0x5b90,0x3775,0x377f,0x377e,0x5c29,0x378f,
+ UBOGON,UBOGON,UBOGON,0x37bd,0x5cb2,UBOGON,0x37bb,0x37bc,UBOGON,0x5cc0,
+ UBOGON,0x383d,0x383e,0x3874,UBOGON,0x387a,0x3876,0x3878,0x3875,UBOGON,
+ UBOGON,0x38af,0x38b0,0x38c7,0x38cc,UBOGON,UBOGON,0x3916,UBOGON,0x3912,
+ 0x391d,UBOGON,UBOGON,0x3915,0x390f,0x3914,0x601f,0x5fe2,UBOGON,UBOGON,
+ UBOGON,0x39b0,0x39bf,0x39c0,UBOGON,0x39d2,0x39d9,UBOGON,0x3a7a,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6616,0x65f9,0x3ada,UBOGON,0x6788,UBOGON,
+ 0x679b,UBOGON,0x676e,0x679e,UBOGON,0x3c22,0x3c1f,UBOGON,UBOGON,0x3c21,
+ 0x6b24,UBOGON,UBOGON,UBOGON,0x3c5c,0x6b7d,UBOGON,0x3c7d,0x3c8d,0x3c8f,
+ 0x6ce6,UBOGON,0x6ccb,0x3cd0
+ },
+ { /* ku 07 */
+ UBOGON,0x3cd8,UBOGON,UBOGON,UBOGON,0x6cb5,0x3da7,UBOGON,0x7097,UBOGON,
+ 0x709b,0x3e12,UBOGON,UBOGON,0x3e2f,UBOGON,0x726b,0x3e2e,0x3e2c,0x3e5c,
+ UBOGON,0x72d5,UBOGON,UBOGON,0x3e62,0x3e67,0x3eb4,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7543,UBOGON,UBOGON,0x759c,UBOGON,UBOGON,0x3fea,UBOGON,0x3ffb,
+ UBOGON,0x4014,UBOGON,0x4013,0x4012,0x4010,0x4011,UBOGON,0x4086,0x77e4,
+ 0x4098,UBOGON,UBOGON,0x412a,UBOGON,UBOGON,UBOGON,UBOGON,0x7ace,0x42b5,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8013,0x43d6,0x43d8,0x80b7,0x43d9,0x43d4,
+ 0x43d7,UBOGON,0x80b9,UBOGON,UBOGON,0x81e4,0x81fd,0x820f,0x4460,UBOGON,
+ UBOGON,0x449e,0x44a1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82bf,
+ 0x82ca,UBOGON,UBOGON,0x82c1
+ },
+ { /* ku 08 */
+ 0x44a0,UBOGON,UBOGON,UBOGON,UBOGON,0x8fd0,UBOGON,UBOGON,0x48b9,UBOGON,
+ 0x90ae,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x49c1,0x49c2,0x9638,UBOGON,
+ 0x341c,UBOGON,0x345e,0x4fbc,0x3459,0x345c,UBOGON,0x345f,0x4fe9,0x4fbd,
+ 0x4fe2,0x5158,UBOGON,UBOGON,0x34ce,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x52c6,UBOGON,0x52c8,UBOGON,UBOGON,0x5328,UBOGON,0x5329,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x355f,UBOGON,0x3585,UBOGON,UBOGON,0x3586,
+ UBOGON,0x57b4,UBOGON,0x57a9,0x3687,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x36ca,UBOGON,0x36c3,UBOGON,UBOGON,UBOGON,0x36c2,0x5b68,UBOGON,0x3741,
+ UBOGON,UBOGON,UBOGON,0x3780,0x3781,UBOGON,UBOGON,0x3793,0x3792,UBOGON,
+ 0x37c5,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ 0x3846,0x3841,0x3845,0x3842,0x383f,UBOGON,UBOGON,0x3882,0x3881,0x387f,
+ 0x38a5,0x5f2b,0x38b3,0x38b5,UBOGON,UBOGON,0x5f8d,0x38da,UBOGON,0x38db,
+ 0x390d,0x6018,0x390e,UBOGON,0x391e,0x3925,0x3926,0x391c,0x3921,0x6057,
+ 0x6048,0x3927,0x391a,UBOGON,UBOGON,UBOGON,0x6038,UBOGON,UBOGON,0x3924,
+ UBOGON,UBOGON,0x6071,UBOGON,0x39c1,0x39e1,UBOGON,UBOGON,0x6312,0x39eb,
+ UBOGON,0x39e2,0x39d7,0x39e9,UBOGON,UBOGON,0x630a,UBOGON,0x6323,UBOGON,
+ 0x3a84,UBOGON,UBOGON,UBOGON,0x3ab5,0x3abc,0x3adc,0x3ade,0x3adf,UBOGON,
+ 0x662a,UBOGON,UBOGON,UBOGON,UBOGON,0x3b54,0x67e0,0x67be,0x3b53,0x3c24,
+ 0x3c25,0x6b29,0x3c28,0x3c27,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3c8b,UBOGON,UBOGON,0x3cdc
+ },
+ { /* ku 0a */
+ 0x6d43,UBOGON,UBOGON,UBOGON,UBOGON,0x70a6,0x3db2,0x70c0,UBOGON,0x722f,
+ UBOGON,0x3e1b,UBOGON,0x3e32,0x7271,UBOGON,UBOGON,0x3e6b,UBOGON,0x3e6c,
+ 0x3e6d,UBOGON,0x3eb9,0x3eba,0x3f09,0x3f0a,UBOGON,0x74ea,0x3f1b,UBOGON,
+ UBOGON,0x7520,0x3f58,UBOGON,0x3f5a,UBOGON,UBOGON,0x3f77,UBOGON,UBOGON,
+ 0x3f79,0x75a9,UBOGON,0x7685,UBOGON,0x3feb,UBOGON,0x3ffd,0x3ffc,0x7706,
+ 0x4015,0x4018,0x76f6,0x4016,0x4017,0x4019,0x7700,0x401b,UBOGON,UBOGON,
+ 0x7702,UBOGON,0x4087,UBOGON,UBOGON,0x409c,UBOGON,0x409a,UBOGON,0x40ff,
+ 0x40fe,UBOGON,0x4131,0x412e,0x4130,0x4132,UBOGON,UBOGON,0x412f,UBOGON,
+ 0x4195,0x4196,UBOGON,UBOGON,UBOGON,0x41c5,0x427a,UBOGON,0x4342,UBOGON,
+ 0x4354,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8009,UBOGON,0x439f,0x43a0,0x43a2,0x43e0,
+ UBOGON,0x43e1,UBOGON,UBOGON,0x43df,UBOGON,UBOGON,0x4462,0x4461,UBOGON,
+ UBOGON,0x44a7,UBOGON,UBOGON,UBOGON,0x82da,UBOGON,UBOGON,0x830a,0x4589,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x461d,UBOGON,UBOGON,
+ 0x472a,UBOGON,UBOGON,0x47d3,0x4842,0x4843,UBOGON,0x4891,UBOGON,UBOGON,
+ 0x4890,0x48bf,UBOGON,0x48bc,UBOGON,0x48c0,0x49c7,0x49c5,0x9655,UBOGON,
+ 0x9652,0x4e35,UBOGON,UBOGON,0x5034,0x5001,UBOGON,0x500a,0x3466,UBOGON,
+ UBOGON,UBOGON,0x34ad,UBOGON,UBOGON,UBOGON,UBOGON,0x5258,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x351d,UBOGON,0x3531,0x532b,UBOGON,0x354c,
+ UBOGON,UBOGON,0x3595,0x3591
+ },
+ { /* ku 0c */
+ 0x3594,UBOGON,UBOGON,UBOGON,0x358f,0x54ec,UBOGON,0x5515,0x54fe,UBOGON,
+ UBOGON,UBOGON,0x54e3,0x5516,0x3640,UBOGON,0x3641,UBOGON,0x57d3,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5959,0x5a27,0x36d8,0x36d2,0x36da,0x5a28,0x5a10,
+ 0x36d7,0x5a0e,0x3742,0x3757,UBOGON,UBOGON,UBOGON,0x5baf,UBOGON,0x5bba,
+ 0x5bb1,UBOGON,UBOGON,0x3778,0x3782,0x3797,UBOGON,UBOGON,0x37c9,UBOGON,
+ 0x37c8,0x37d6,0x37cc,UBOGON,0x37d0,UBOGON,UBOGON,0x37ce,0x37c7,0x5cfc,
+ UBOGON,0x37cf,0x37cb,0x5cf2,0x5cfe,UBOGON,UBOGON,0x5df8,UBOGON,0x3847,
+ UBOGON,0x3848,UBOGON,UBOGON,0x3883,0x3885,0x3884,UBOGON,UBOGON,0x5f2c,
+ 0x38b8,0x38bc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,0x3931,0x3934,0x3936,0x6082,UBOGON,UBOGON,0x3923,UBOGON,UBOGON,
+ 0x393a,0x6091,0x608f,UBOGON,0x39b4,0x39b5,0x39ed,0x39ec,0x39d8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39f6,0x39e7,UBOGON,UBOGON,
+ 0x3a7c,0x3a7b,UBOGON,UBOGON,0x6547,0x654c,UBOGON,UBOGON,0x658a,UBOGON,
+ 0x3abe,UBOGON,0x3ae7,0x3ae5,0x3aee,0x67e1,0x684a,UBOGON,0x3b59,0x3b5e,
+ UBOGON,0x3b5a,0x683f,0x3b61,0x3b58,0x3b5b,0x67bd,UBOGON,0x3b5f,UBOGON,
+ 0x3c2a,0x3c2d,UBOGON,0x3c23,0x3c2b,0x3c2c,UBOGON,0x3c7e,UBOGON,0x3c93,
+ 0x3c99,UBOGON,UBOGON,0x3cb3,0x3ce7,0x3cea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3db4,UBOGON,0x70c9,0x3e17,UBOGON,UBOGON,UBOGON,0x3e21,
+ UBOGON,0x3e38,0x3e37,0x3e74
+ },
+ { /* ku 0e */
+ 0x3e73,0x3e75,UBOGON,UBOGON,UBOGON,0x3e76,0x3e78,UBOGON,UBOGON,0x73ba,
+ 0x3f0c,0x3f20,0x3f1e,UBOGON,0x3f5b,UBOGON,0x3f5c,UBOGON,0x3f83,0x75c6,
+ 0x3f80,0x3f81,0x3f7e,0x3f88,0x3f85,0x3f89,0x3f7f,0x3f8e,UBOGON,UBOGON,
+ 0x3f84,0x75b7,0x768c,UBOGON,0x768d,UBOGON,0x3fee,0x3fed,0x3ffe,UBOGON,
+ 0x3fff,UBOGON,UBOGON,UBOGON,0x4023,UBOGON,0x7717,UBOGON,0x771c,0x401f,
+ UBOGON,UBOGON,0x7714,UBOGON,0x408f,0x4090,UBOGON,UBOGON,UBOGON,0x40a0,
+ UBOGON,0x40a6,0x409f,UBOGON,0x40a7,0x40a1,UBOGON,0x4102,0x4136,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41c8,UBOGON,UBOGON,0x41d8,
+ 0x7b0c,0x41dd,0x41dc,UBOGON,0x41d7,UBOGON,0x41da,0x42ba,UBOGON,UBOGON,
+ UBOGON,0x42be,0x42c2,0x42bb
+ },
+ { /* ku 0f */
+ 0x42c0,UBOGON,UBOGON,UBOGON,0x7d23,UBOGON,UBOGON,UBOGON,0x4343,0x4355,
+ UBOGON,UBOGON,0x4357,0x4368,0x7f98,0x7f90,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x43a1,UBOGON,0x803a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x43ea,UBOGON,0x43e7,UBOGON,UBOGON,UBOGON,UBOGON,0x43e8,0x43e9,UBOGON,
+ UBOGON,0x4454,UBOGON,UBOGON,UBOGON,UBOGON,0x8226,0x4465,UBOGON,UBOGON,
+ 0x448a,UBOGON,0x44b0,UBOGON,UBOGON,UBOGON,0x44bc,0x832e,UBOGON,0x8355,
+ 0x831a,0x44b8,0x833d,UBOGON,0x44b2,UBOGON,0x8330,0x44bd,UBOGON,UBOGON,
+ 0x458a,0x8651,0x45a1,UBOGON,0x45a2,UBOGON,0x8688,UBOGON,0x4615,UBOGON,
+ UBOGON,0x4620,0x4673,UBOGON,0x898e,0x898d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8a09,0x8a14,0x46b1
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,0x472b,UBOGON,0x4745,UBOGON,0x4797,0x4798,UBOGON,0x47d5,
+ UBOGON,UBOGON,0x4893,0x4896,0x9007,0x4894,UBOGON,UBOGON,0x48c7,0x48c5,
+ UBOGON,UBOGON,UBOGON,0x48c4,UBOGON,0x9579,0x9584,0x49ce,0x49ca,0x49cc,
+ 0x9657,0x49c9,0x96ba,UBOGON,UBOGON,UBOGON,0x346e,UBOGON,0x5067,UBOGON,
+ UBOGON,0x3471,UBOGON,0x34bb,0x34d3,UBOGON,UBOGON,UBOGON,UBOGON,0x34f3,
+ 0x34ed,0x34f5,UBOGON,UBOGON,0x34f1,0x34f2,0x34f6,0x3520,UBOGON,0x3528,
+ UBOGON,0x5318,0x532c,0x5359,UBOGON,UBOGON,UBOGON,0x5368,0x537e,UBOGON,
+ UBOGON,0x53a1,UBOGON,0x35a1,0x555b,0x35aa,0x35a9,UBOGON,0x35b5,0x35a5,
+ 0x35a8,0x5542,0x35a7,0x5547,UBOGON,UBOGON,0x553d,UBOGON,UBOGON,0x5560,
+ 0x57eb,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ 0x364d,UBOGON,UBOGON,UBOGON,0x369c,0x595f,UBOGON,0x36ea,0x36e5,UBOGON,
+ UBOGON,0x5b6f,UBOGON,0x375e,UBOGON,UBOGON,UBOGON,0x3786,0x3784,UBOGON,
+ 0x5c5a,UBOGON,0x37d9,UBOGON,0x37de,UBOGON,UBOGON,UBOGON,0x37db,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3831,UBOGON,UBOGON,0x384b,UBOGON,0x3849,0x384a,
+ 0x384c,UBOGON,UBOGON,0x388a,UBOGON,0x3889,0x388b,UBOGON,0x38bb,0x5fa2,
+ 0x5f9d,0x38e4,UBOGON,UBOGON,0x5fa3,UBOGON,UBOGON,0x393b,0x392e,0x393e,
+ 0x3946,0x3953,UBOGON,0x3944,UBOGON,0x393f,0x3942,0x394f,UBOGON,0x3952,
+ 0x394a,0x60c2,UBOGON,0x395a,0x60a5,0x3949,UBOGON,0x621c,UBOGON,0x621d,
+ 0x3a03,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6395,0x639a,0x3a01,0x3a06,
+ 0x39fb,0x39f9,UBOGON,0x3a05
+ },
+ { /* ku 12 */
+ 0x39fa,UBOGON,0x63a6,UBOGON,0x39fe,UBOGON,0x3a7d,UBOGON,UBOGON,UBOGON,
+ 0x6550,UBOGON,UBOGON,UBOGON,0x6552,UBOGON,UBOGON,UBOGON,UBOGON,0x65c8,
+ UBOGON,0x3af0,0x3af2,UBOGON,0x6658,0x3af1,0x3ae6,UBOGON,UBOGON,UBOGON,
+ 0x6888,UBOGON,0x3b6f,0x3b6d,0x3b69,UBOGON,UBOGON,0x3b6e,UBOGON,0x3c2f,
+ 0x3c30,0x3c63,UBOGON,UBOGON,UBOGON,0x6bb8,0x3c80,0x6bb9,0x3c9a,0x3c94,
+ 0x3c96,0x3c95,0x3c97,UBOGON,0x3cf4,0x3cfe,UBOGON,0x3d01,UBOGON,0x3d02,
+ UBOGON,0x3cf9,UBOGON,UBOGON,UBOGON,UBOGON,0x3cf6,0x3cf7,UBOGON,UBOGON,
+ UBOGON,0x3cff,UBOGON,UBOGON,UBOGON,UBOGON,0x6e0b,UBOGON,UBOGON,0x3dbf,
+ 0x3dbc,0x7105,UBOGON,UBOGON,UBOGON,0x3dbe,0x3dc0,UBOGON,0x3e3b,0x3e39,
+ UBOGON,UBOGON,UBOGON,0x3e3c
+ },
+ { /* ku 13 */
+ UBOGON,0x7314,0x7304,UBOGON,0x3e7d,UBOGON,0x3e7f,0x3e7a,0x3e7c,0x7305,
+ 0x3e7e,0x7315,0x730d,0x3e80,0x3ebf,0x3ec3,UBOGON,UBOGON,0x3ecc,0x3f0e,
+ 0x3f0d,UBOGON,0x3f26,0x3f24,0x3f25,0x3f23,0x3f21,0x3f29,UBOGON,UBOGON,
+ 0x3f8f,0x3f8d,UBOGON,0x3f8b,0x3f92,UBOGON,0x3f90,UBOGON,0x3fef,0x3ff0,
+ UBOGON,UBOGON,0x4001,UBOGON,0x402e,0x402d,0x772e,0x4028,0x4029,0x402c,
+ UBOGON,UBOGON,UBOGON,0x7741,0x4088,UBOGON,0x4092,0x4091,0x77ea,UBOGON,
+ 0x7844,0x40a9,0x40ac,0x40ae,0x40aa,0x4106,0x4105,0x414a,0x413e,0x413c,
+ 0x413b,UBOGON,0x4142,0x4141,0x4143,UBOGON,0x4145,UBOGON,0x419a,0x419b,
+ 0x419f,0x419e,UBOGON,UBOGON,UBOGON,0x41de,0x41e2,0x41e6,UBOGON,0x7b29,
+ 0x41e3,0x7b27,0x41df,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,0x7c9d,UBOGON,UBOGON,0x427e,0x42c9,0x42cc,UBOGON,0x42d1,UBOGON,
+ 0x42d0,UBOGON,0x42cf,0x42c8,UBOGON,0x42cd,UBOGON,UBOGON,0x4349,0x4347,
+ 0x4358,0x436b,0x436c,0x436a,UBOGON,0x4380,0x4382,0x4384,0x7fc8,0x4383,
+ UBOGON,UBOGON,0x43b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8126,0x43f1,
+ UBOGON,0x43f6,0x43f3,0x43f0,0x811c,UBOGON,UBOGON,0x8128,0x43f5,0x43f4,
+ 0x43f7,UBOGON,UBOGON,UBOGON,UBOGON,0x4455,UBOGON,UBOGON,UBOGON,0x448b,
+ 0x44cb,0x44c2,UBOGON,UBOGON,UBOGON,0x44ca,0x44cc,UBOGON,0x44c7,0x44c9,
+ 0x8370,UBOGON,0x44c6,UBOGON,UBOGON,0x44c3,0x8382,UBOGON,0x83ac,UBOGON,
+ 0x44c4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45a9,0x86ad,
+ 0x45a8,0x45a6,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x86ca,0x8851,UBOGON,UBOGON,0x4622,
+ UBOGON,0x4626,0x4624,0x4625,0x889d,0x462a,0x4674,UBOGON,0x4679,0x8990,
+ 0x467a,UBOGON,UBOGON,0x89d8,0x89d7,0x4697,UBOGON,UBOGON,0x8a2e,UBOGON,
+ 0x46bc,UBOGON,0x46b3,UBOGON,0x46bf,0x46b7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4734,0x4746,0x4748,0x8c59,UBOGON,UBOGON,0x4756,0x4767,UBOGON,
+ UBOGON,0x4768,0x4799,0x479a,UBOGON,UBOGON,0x47d8,UBOGON,0x47db,0x47dc,
+ 0x47dd,0x47d7,UBOGON,UBOGON,0x4849,0x484a,0x8eda,UBOGON,UBOGON,0x9033,
+ UBOGON,0x9018,0x489a,UBOGON,0x48cd,0x48ca,UBOGON,0x48cb,UBOGON,0x48cf,
+ UBOGON,UBOGON,0x48cc,0x48ea,0x48ed,UBOGON,UBOGON,0x48e9,UBOGON,0x491a,
+ 0x91ef,0x498d,0x49d2,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,0x49f2,UBOGON,UBOGON,UBOGON,0x4a3d,UBOGON,0x4a3e,
+ 0x4af8,0x4b23,0x9ad9,0x4eb4,UBOGON,0x50a0,0x5090,0x3475,0x5086,0x5084,
+ UBOGON,0x508a,0x3476,0x3473,0x509f,0x50a1,UBOGON,0x5093,0x34bd,UBOGON,
+ 0x51d5,UBOGON,UBOGON,0x34f9,UBOGON,UBOGON,0x34fa,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3521,UBOGON,0x3529,UBOGON,UBOGON,0x3538,0x354e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35c8,0x35bc,UBOGON,UBOGON,0x5590,
+ UBOGON,0x35bb,0x35c2,0x35c0,UBOGON,UBOGON,0x35ca,UBOGON,0x35c9,UBOGON,
+ 0x35b8,0x5710,0x5817,UBOGON,0x364e,UBOGON,0x5844,0x3650,0x582b,UBOGON,
+ 0x5845,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x368a,UBOGON,0x5965,UBOGON,
+ UBOGON,UBOGON,0x36fc,0x36f9
+ },
+ { /* ku 17 */
+ UBOGON,0x3763,UBOGON,0x5bcf,UBOGON,UBOGON,UBOGON,0x3787,0x3788,UBOGON,
+ 0x379a,UBOGON,UBOGON,UBOGON,0x5d56,UBOGON,UBOGON,0x37e9,UBOGON,0x37ea,
+ 0x5d54,0x3850,UBOGON,UBOGON,0x3856,0x3852,0x384f,0x3854,0x3851,UBOGON,
+ UBOGON,0x388e,0x388f,UBOGON,UBOGON,UBOGON,0x5f3d,UBOGON,UBOGON,0x38ed,
+ 0x38eb,0x5fa4,UBOGON,UBOGON,UBOGON,0x3962,UBOGON,0x395d,UBOGON,UBOGON,
+ UBOGON,0x3961,0x3965,0x395c,UBOGON,UBOGON,0x395f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x63ec,0x3a16,0x3a0a,UBOGON,0x3a0e,0x3a12,UBOGON,
+ UBOGON,0x3a11,UBOGON,UBOGON,0x3a10,UBOGON,UBOGON,0x3a09,0x63fa,0x3a15,
+ 0x63d4,UBOGON,UBOGON,UBOGON,0x3a91,0x3a95,0x3a93,0x3a92,0x3a8f,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3af6
+ },
+ { /* ku 18 */
+ UBOGON,0x3afb,UBOGON,0x6675,0x3af9,UBOGON,UBOGON,UBOGON,0x671c,0x3b7d,
+ UBOGON,0x3b7a,0x3b7f,UBOGON,0x3b78,UBOGON,UBOGON,0x68d9,UBOGON,0x3b70,
+ 0x3b82,UBOGON,0x3b84,UBOGON,0x3c33,UBOGON,0x3c32,0x3c36,UBOGON,UBOGON,
+ 0x3c56,UBOGON,UBOGON,0x3c67,UBOGON,0x3c65,0x3c64,0x3c66,UBOGON,UBOGON,
+ 0x3c81,0x3c82,0x3c83,0x3c9e,UBOGON,0x6bf1,0x3c9d,UBOGON,0x3d0f,0x3d12,
+ UBOGON,UBOGON,UBOGON,0x3d10,0x3d18,UBOGON,0x3d14,0x3d19,0x6e37,UBOGON,
+ UBOGON,0x6e7d,0x6e86,0x3dc8,0x3dc4,0x3dc6,UBOGON,0x3dc7,0x3dc3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e19,0x3e1c,UBOGON,UBOGON,UBOGON,0x3e41,
+ UBOGON,0x3e42,0x3e43,UBOGON,UBOGON,UBOGON,0x3e82,UBOGON,0x3e81,0x3e94,
+ 0x3e84,UBOGON,0x3ed2,0x3f0f
+ },
+ { /* ku 19 */
+ 0x3f22,UBOGON,0x3f27,0x3f2a,0x74fa,0x3f28,UBOGON,0x3f60,UBOGON,UBOGON,
+ UBOGON,0x7572,UBOGON,UBOGON,0x3f9b,0x3f9c,UBOGON,0x3f93,0x3f94,0x75dc,
+ 0x3fa0,0x3f99,UBOGON,0x3fa1,UBOGON,0x3ff1,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4036,UBOGON,0x4037,0x403f,0x403c,UBOGON,0x4034,0x4039,0x403b,0x4035,
+ 0x4030,0x4032,0x4038,0x403e,0x403a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x40b6,0x7867,UBOGON,UBOGON,UBOGON,0x40b3,0x4109,0x7977,UBOGON,UBOGON,
+ UBOGON,0x414c,UBOGON,UBOGON,0x4153,0x414d,0x4151,0x414f,0x7a9b,UBOGON,
+ 0x41a2,UBOGON,UBOGON,0x41cd,UBOGON,UBOGON,0x41e7,UBOGON,0x41f0,UBOGON,
+ 0x41e9,0x41ec,UBOGON,0x41e8,0x41ee,0x4202,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4282,0x4283,0x4286,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7d2a,0x7d65,0x434a,UBOGON,0x435a,
+ 0x7f64,UBOGON,0x436e,UBOGON,UBOGON,0x4370,0x436f,UBOGON,0x438a,0x4387,
+ 0x4388,UBOGON,UBOGON,0x8020,0x43b7,0x43fd,0x8120,UBOGON,0x4405,0x813c,
+ 0x4408,0x4403,0x4402,0x4404,0x3b39,0x4409,0x43ff,UBOGON,0x813f,UBOGON,
+ 0x43fc,0x4401,0x440a,0x81f0,0x81f5,0x446b,0x446c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44de,UBOGON,UBOGON,0x44db,UBOGON,0x44dd,0x44e3,
+ UBOGON,0x44e0,0x44d9,0x44d8,0x44e4,UBOGON,UBOGON,0x44da,0x44ef,UBOGON,
+ 0x8415,0x83be,UBOGON,UBOGON,UBOGON,0x44d7,0x45b3,0x45bb,0x86e5,0x45b2,
+ 0x86d2,0x45ad,UBOGON,0x45af,UBOGON,0x86e0,UBOGON,0x4616,0x4628,0x4623,
+ 0x88b3,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ 0x4675,0x467e,0x467c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46ce,0x46cd,
+ 0x46cf,0x8a53,UBOGON,UBOGON,0x8a37,0x8a47,0x8a5c,UBOGON,0x46c4,0x46cc,
+ 0x46c8,0x46c7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x475a,
+ 0x476a,UBOGON,0x476b,0x476d,0x476f,UBOGON,0x479e,UBOGON,0x47a4,0x47a3,
+ 0x47e4,0x47e8,0x47e9,0x47e0,0x47e3,UBOGON,0x47ea,0x47e1,0x47ed,0x4834,
+ 0x4835,0x4851,0x8ef0,UBOGON,0x489d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48d0,UBOGON,0x48ee,UBOGON,UBOGON,UBOGON,0x48f2,UBOGON,0x921d,
+ 0x4988,UBOGON,UBOGON,UBOGON,0x498f,UBOGON,0x49d8,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a3f,UBOGON,0x4a52,0x976b,UBOGON,0x4a50,UBOGON,
+ 0x4ab1,UBOGON,UBOGON,0x4af9
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,0x4b26,0x4b28,UBOGON,UBOGON,0x3480,0x50c0,0x3481,
+ UBOGON,0x347e,0x347f,UBOGON,UBOGON,UBOGON,UBOGON,0x34be,UBOGON,0x34d6,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52e5,UBOGON,0x3534,
+ UBOGON,0x53af,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35d5,0x35d8,UBOGON,
+ UBOGON,0x35d4,0x55d8,0x35d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5711,
+ 0x5867,UBOGON,UBOGON,0x365d,0x5843,0x365e,0x3659,UBOGON,0x365a,0x36a0,
+ UBOGON,UBOGON,0x3705,UBOGON,0x3707,UBOGON,0x370e,0x370c,UBOGON,UBOGON,
+ 0x3745,UBOGON,UBOGON,0x3764,UBOGON,0x3765,UBOGON,0x5bdd,0x3766,UBOGON,
+ 0x3789,0x37ec,0x37f1,0x5d70,0x5d6a,0x37f0,0x37f8,0x5d74,0x5d5f,UBOGON,
+ 0x5d61,0x5d73,UBOGON,0x37f2
+ },
+ { /* ku 1d */
+ 0x37f4,UBOGON,0x3858,UBOGON,UBOGON,0x385a,0x3859,0x3857,0x385b,0x5e50,
+ UBOGON,UBOGON,0x38a6,0x38c2,0x38c1,0x5f3f,UBOGON,UBOGON,0x38ef,0x5fb0,
+ UBOGON,UBOGON,0x3968,0x6135,0x612d,0x3973,0x396e,0x3974,0x6102,0x3966,
+ UBOGON,UBOGON,UBOGON,0x39b9,0x6226,UBOGON,0x3a0c,UBOGON,UBOGON,0x3a20,
+ UBOGON,0x3a1d,UBOGON,0x3a1c,UBOGON,0x3a21,0x3a1a,0x3a19,UBOGON,UBOGON,
+ UBOGON,0x3a7e,UBOGON,UBOGON,UBOGON,UBOGON,0x3a9d,UBOGON,0x3a9e,UBOGON,
+ 0x656e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x65b1,0x65d4,0x3acd,UBOGON,
+ 0x3b0b,0x3b0a,0x6685,UBOGON,0x3b8f,0x6972,0x3b95,0x3b90,0x3b91,UBOGON,
+ 0x693a,0x3bb9,UBOGON,UBOGON,0x3b97,0x3b9e,UBOGON,0x3b8b,UBOGON,UBOGON,
+ 0x3c3b,0x3c3a,0x3c3c,0x3c3d
+ },
+ { /* ku 1e */
+ 0x3c39,0x3c3e,0x3c6b,0x3c6c,UBOGON,UBOGON,0x3ca2,0x3ca1,0x3c9f,UBOGON,
+ UBOGON,UBOGON,0x3d2d,UBOGON,0x3d36,0x3d2b,UBOGON,0x3d37,UBOGON,UBOGON,
+ UBOGON,0x6ead,0x3d25,0x3d2f,0x3d2c,UBOGON,UBOGON,0x3d32,UBOGON,UBOGON,
+ 0x6e95,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dd5,UBOGON,0x3dd4,
+ 0x3dd6,UBOGON,0x3dd1,0x7243,UBOGON,0x3e46,0x728f,UBOGON,UBOGON,0x3e8c,
+ 0x3e8a,0x3e88,UBOGON,UBOGON,UBOGON,UBOGON,0x3edd,UBOGON,UBOGON,UBOGON,
+ 0x3f2d,UBOGON,UBOGON,0x3f2e,0x3f2c,0x3f2b,0x3f30,UBOGON,0x3f4e,UBOGON,
+ UBOGON,0x3f64,0x3f61,UBOGON,0x7575,UBOGON,0x3f70,0x3fa6,0x3fa4,UBOGON,
+ UBOGON,UBOGON,0x3fa8,0x3fa2,UBOGON,0x3fa7,0x75ec,0x3fa5,UBOGON,0x3fa9,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,0x403d,UBOGON,UBOGON,0x4044,0x4045,0x4046,UBOGON,0x7757,UBOGON,
+ 0x4047,0x4048,0x4042,UBOGON,UBOGON,0x4041,UBOGON,UBOGON,0x4094,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x40c0,0x40b8,0x40c1,0x40c2,0x40bb,0x40bd,0x40bf,
+ 0x40b9,0x40b7,UBOGON,0x40c7,UBOGON,0x410c,0x410b,0x797b,0x4110,UBOGON,
+ 0x415d,0x7a21,0x415a,0x4158,0x4156,UBOGON,0x4154,0x7a16,UBOGON,0x41a8,
+ 0x41a7,0x41cf,0x41d0,UBOGON,0x7ae8,UBOGON,UBOGON,0x41d1,0x41eb,UBOGON,
+ 0x41fb,0x7b6a,UBOGON,0x41fd,0x41f8,0x41f7,0x4200,UBOGON,UBOGON,0x41f6,
+ 0x7b5f,UBOGON,UBOGON,0x42df,UBOGON,UBOGON,UBOGON,0x42e2,0x42e4,UBOGON,
+ 0x7d82,UBOGON,0x42e3,UBOGON,0x4359,0x4371,0x438e,0x438c,UBOGON,0x43a4,
+ UBOGON,0x8055,0x4414,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,0x4411,UBOGON,0x441b,0x4412,0x440e,0x4415,0x8168,0x4410,
+ UBOGON,0x4417,0x8246,0x8243,0x4470,0x44ed,UBOGON,0x44ee,UBOGON,UBOGON,
+ UBOGON,0x8481,UBOGON,UBOGON,UBOGON,0x44f4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x847c,UBOGON,UBOGON,0x846a,UBOGON,0x8488,0x44f2,0x44f8,
+ 0x44f3,UBOGON,UBOGON,0x44fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8710,
+ UBOGON,UBOGON,0x871f,0x45b6,0x45b7,UBOGON,0x870f,UBOGON,0x45ba,UBOGON,
+ 0x45bc,UBOGON,UBOGON,0x463b,0x88d3,0x462f,UBOGON,UBOGON,UBOGON,0x4637,
+ 0x4699,UBOGON,UBOGON,UBOGON,UBOGON,0x46d9,0x46d8,0x46d7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4736,UBOGON,UBOGON,UBOGON,0x8c87,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8cc6
+ },
+ { /* ku 21 */
+ 0x4770,UBOGON,UBOGON,UBOGON,UBOGON,0x47a5,0x47a6,0x47a9,0x47ee,0x4854,
+ UBOGON,0x4857,UBOGON,UBOGON,0x48a1,UBOGON,UBOGON,UBOGON,UBOGON,0x48d3,
+ UBOGON,0x48d4,UBOGON,0x48d7,0x90cc,0x916d,0x9170,0x48f7,0x48f6,0x48f9,
+ 0x48f8,0x9258,0x9242,0x9268,0x9269,UBOGON,UBOGON,0x9243,UBOGON,0x9247,
+ 0x498a,UBOGON,UBOGON,UBOGON,UBOGON,0x4994,UBOGON,0x4993,UBOGON,UBOGON,
+ 0x959d,0x49dd,0x49dc,0x49f7,0x96cf,UBOGON,UBOGON,0x4a42,UBOGON,UBOGON,
+ 0x4a54,UBOGON,0x4a55,UBOGON,0x4a8f,UBOGON,0x97f4,0x4ab4,0x4ab3,UBOGON,
+ UBOGON,0x9809,UBOGON,UBOGON,UBOGON,UBOGON,0x4afb,0x4afd,UBOGON,UBOGON,
+ 0x98ab,0x4afc,UBOGON,0x4b2c,0x4b2f,UBOGON,0x4b2b,UBOGON,0x4b33,0x4b34,
+ 0x98fb,UBOGON,0x9aac,0x9aae
+ },
+ { /* ku 22 */
+ 0x9aaa,0x4be8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b5c,UBOGON,
+ UBOGON,0x4d5d,0x50d2,0x3485,0x3488,UBOGON,UBOGON,0x348e,0x3484,UBOGON,
+ 0x50df,UBOGON,0x3483,UBOGON,UBOGON,UBOGON,UBOGON,0x3502,UBOGON,0x3506,
+ 0x3505,UBOGON,0x34fe,0x3501,0x3500,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x35e7,0x5619,UBOGON,UBOGON,UBOGON,0x35e6,UBOGON,0x35ed,0x35e2,
+ 0x35eb,UBOGON,0x35e8,0x35ec,0x560a,0x3624,0x589a,UBOGON,0x3662,UBOGON,
+ 0x3661,0x3660,0x3664,0x368b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3719,
+ 0x3716,0x3718,0x3722,UBOGON,0x371d,0x3717,0x371e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3769,0x376a,UBOGON,0x376c,0x377a,0x378a,UBOGON,
+ UBOGON,UBOGON,0x379c,UBOGON
+ },
+ { /* ku 23 */
+ 0x37fd,0x37f9,UBOGON,0x37ff,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37fc,
+ UBOGON,0x5d85,0x37fb,0x3802,0x385f,0x5e56,0x385e,0x385d,0x385c,UBOGON,
+ 0x5e51,0x3892,UBOGON,UBOGON,0x3894,0x3895,0x38d1,UBOGON,0x38f1,UBOGON,
+ 0x5fb1,UBOGON,UBOGON,0x3977,0x396f,UBOGON,UBOGON,0x3987,0x397d,0x397c,
+ 0x397e,0x3985,0x398b,0x3986,0x3980,UBOGON,UBOGON,0x3978,UBOGON,UBOGON,
+ UBOGON,0x39ba,UBOGON,0x3a33,UBOGON,0x3a2d,UBOGON,UBOGON,UBOGON,0x3a37,
+ 0x645a,0x6463,UBOGON,UBOGON,0x3a2e,UBOGON,UBOGON,0x3a3d,UBOGON,0x3aa0,
+ UBOGON,UBOGON,0x3aa3,UBOGON,0x669b,UBOGON,0x66a3,0x3b0e,0x669e,UBOGON,
+ 0x3bb6,UBOGON,0x3bab,0x3bad,0x3ba6,UBOGON,0x69b8,0x3baa,0x69ba,0x3bb1,
+ UBOGON,0x3ba8,0x3baf,0x3bb0
+ },
+ { /* ku 24 */
+ 0x3ba7,0x3bb2,0x3b9d,0x3ba5,0x3bb5,UBOGON,0x69c7,0x69d7,UBOGON,0x3c41,
+ UBOGON,UBOGON,0x6b70,UBOGON,UBOGON,0x3c72,0x6b9d,0x3c6f,0x3c71,UBOGON,
+ 0x3c85,UBOGON,UBOGON,0x3ca4,0x3ca5,0x3ca6,UBOGON,0x3ca8,UBOGON,UBOGON,
+ 0x3ca3,UBOGON,UBOGON,UBOGON,0x6f16,0x6f24,UBOGON,0x3d43,UBOGON,UBOGON,
+ 0x3d3d,0x3d45,UBOGON,UBOGON,0x3d44,UBOGON,UBOGON,UBOGON,UBOGON,0x6f45,
+ UBOGON,UBOGON,UBOGON,0x3de3,0x7179,UBOGON,0x3ddf,0x3de4,0x717a,0x3de5,
+ UBOGON,0x7254,0x3e22,0x3e4a,UBOGON,0x3e49,0x3e44,0x3e4b,0x3e87,0x3e89,
+ 0x3e92,0x3e91,0x3e90,0x3e8e,UBOGON,UBOGON,UBOGON,UBOGON,0x3f12,0x3f10,
+ 0x3f11,UBOGON,0x3f32,0x3f34,0x3f37,0x3f33,0x3f36,0x3f35,0x3f65,UBOGON,
+ 0x757c,0x757b,UBOGON,0x7612
+ },
+ { /* ku 25 */
+ 0x3fb0,UBOGON,0x3faf,0x3faa,UBOGON,UBOGON,UBOGON,0x3fab,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ff3,UBOGON,0x3ff4,UBOGON,0x76b6,
+ 0x76e0,0x4008,0x404e,0x4055,0x404b,UBOGON,UBOGON,0x404d,0x7773,UBOGON,
+ 0x4052,0x7772,0x404c,0x7770,0x4050,0x4053,UBOGON,0x4051,UBOGON,UBOGON,
+ UBOGON,0x4089,UBOGON,0x4095,0x40cc,0x40c8,0x40ce,UBOGON,0x40ca,UBOGON,
+ 0x789d,UBOGON,0x40cd,UBOGON,0x415c,0x4167,0x4169,0x4165,0x4162,UBOGON,
+ 0x7a27,0x7a35,UBOGON,0x41aa,UBOGON,UBOGON,0x41d2,0x7ba2,0x4203,0x420c,
+ UBOGON,0x4209,0x4206,0x4205,0x7b89,UBOGON,0x420b,0x4208,UBOGON,0x7ba5,
+ UBOGON,0x428e,UBOGON,0x7cb6,0x42e8,UBOGON,UBOGON,UBOGON,0x42ea,UBOGON,
+ 0x7da5,0x7dc3,UBOGON,0x42e9
+ },
+ { /* ku 26 */
+ 0x42eb,UBOGON,0x42f0,UBOGON,0x434b,0x7fab,0x4373,0x4375,0x4392,0x4391,
+ 0x4393,0x8025,0x43a7,0x43a6,0x43a8,0x43aa,UBOGON,0x43a9,0x8059,0x43bb,
+ 0x43bc,0x43ba,0x43bd,0x4427,0x8185,UBOGON,0x4424,0x441e,0x441f,0x441d,
+ 0x4420,0x4423,0x4429,0x4422,UBOGON,UBOGON,0x441c,0x818e,0x4428,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4471,0x4473,0x4472,UBOGON,
+ 0x4502,UBOGON,UBOGON,UBOGON,0x44fe,0x84be,UBOGON,UBOGON,UBOGON,0x4508,
+ UBOGON,UBOGON,UBOGON,0x4507,0x4504,UBOGON,UBOGON,0x4500,0x44fc,UBOGON,
+ 0x4544,UBOGON,0x44f1,UBOGON,UBOGON,0x84a6,0x4506,UBOGON,UBOGON,0x45c6,
+ 0x45c3,0x45c1,0x45c2,UBOGON,0x45c4,0x45c7,UBOGON,0x45bf,0x45d2,UBOGON,
+ 0x45ca,UBOGON,UBOGON,0x872f
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,0x4613,UBOGON,0x4630,0x463e,0x4639,UBOGON,UBOGON,0x463c,
+ 0x463f,UBOGON,0x4634,0x463d,UBOGON,UBOGON,0x4638,UBOGON,UBOGON,UBOGON,
+ 0x89a0,0x4682,UBOGON,0x4683,UBOGON,0x469b,UBOGON,0x46e0,0x46dd,UBOGON,
+ UBOGON,0x46de,UBOGON,0x46e3,0x46e5,UBOGON,0x8a97,0x46e2,UBOGON,UBOGON,
+ UBOGON,0x46df,UBOGON,0x472e,UBOGON,0x4737,0x4738,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8c8b,UBOGON,0x3562,UBOGON,0x4794,0x4793,0x47ab,0x47ad,UBOGON,
+ UBOGON,0x47f5,0x47f7,UBOGON,0x47f6,0x47f8,UBOGON,0x47fb,0x47f9,0x4858,
+ 0x485a,UBOGON,0x4859,0x8f0f,0x4885,0x48a4,0x48d8,0x48d9,UBOGON,0x48dd,
+ 0x48c8,UBOGON,0x48fa,0x48fb,0x9275,0x4927,0x929f,0x492a,0x4925,UBOGON,
+ 0x4928,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,0x95a6,0x4995,0x969a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a0f,0x4a11,UBOGON,UBOGON,0x4a10,UBOGON,0x4a15,0x4a13,
+ 0x9757,UBOGON,0x4a47,0x4a46,UBOGON,0x4a59,0x4a5b,UBOGON,0x4a5e,UBOGON,
+ UBOGON,0x4a5a,0x4a91,0x4a92,0x4a90,0x4a93,UBOGON,0x97f7,0x4abe,UBOGON,
+ UBOGON,UBOGON,0x4abc,0x4abb,0x4ab7,0x4ab9,UBOGON,0x4b01,0x4afe,UBOGON,
+ UBOGON,0x4b02,UBOGON,0x4aff,0x98b0,UBOGON,0x4b00,UBOGON,0x4b37,0x4b3a,
+ 0x4b6f,0x4b77,0x4b79,0x99c6,UBOGON,0x4bc8,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4bf2,UBOGON,0x4bf1,0x4bf0,0x9b62,UBOGON,0x4c34,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d2c,0x4d2d,UBOGON,UBOGON,UBOGON,UBOGON,0x50fa,UBOGON,
+ 0x3491,UBOGON,0x3494,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,0x34c4,0x350a,UBOGON,0x5285,UBOGON,0x3552,UBOGON,0x3559,0x366f,
+ UBOGON,0x35f2,0x35f4,0x5643,UBOGON,0x35f1,0x563c,UBOGON,0x366a,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3724,UBOGON,0x3723,UBOGON,UBOGON,
+ UBOGON,0x3727,UBOGON,UBOGON,0x376d,0x5bed,0x376e,0x376f,UBOGON,UBOGON,
+ 0x5c35,0x379f,0x380a,0x3806,0x380e,UBOGON,0x380d,0x3805,UBOGON,UBOGON,
+ 0x380b,0x3810,0x382e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3896,0x3897,
+ 0x38c4,0x5f47,0x38c5,UBOGON,0x38d2,UBOGON,UBOGON,UBOGON,0x3981,UBOGON,
+ 0x398e,0x3990,0x398f,UBOGON,0x3991,0x3995,0x3993,UBOGON,0x616d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3a3b,0x3a48,UBOGON,UBOGON,0x3a46,0x3a47,
+ UBOGON,UBOGON,UBOGON,0x3a4c
+ },
+ { /* ku 2a */
+ UBOGON,0x3a4a,0x3a50,0x3a43,UBOGON,UBOGON,UBOGON,0x3a49,0x3aa6,0x3aa5,
+ 0x3aa4,UBOGON,UBOGON,0x3ab9,UBOGON,0x3ace,0x3acf,0x3b13,UBOGON,UBOGON,
+ UBOGON,0x3bc6,0x3bc5,0x3bca,0x3bd9,0x3bc1,UBOGON,UBOGON,0x69f5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3bcb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6a03,
+ UBOGON,UBOGON,0x6a65,0x3c42,UBOGON,0x6b75,0x3c74,0x3c73,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6f8a,0x6f56,0x3d52,UBOGON,UBOGON,UBOGON,
+ 0x3d50,UBOGON,0x3d4b,UBOGON,0x3d4c,0x3d6d,UBOGON,0x6f98,UBOGON,UBOGON,
+ UBOGON,0x3d4f,0x6f68,0x3df0,UBOGON,0x7234,0x7245,0x3e4d,0x3e4c,UBOGON,
+ 0x3e4f,0x3e4e,0x3e50,UBOGON,0x735c,0x3e96,0x7356,UBOGON,UBOGON,0x3e97,
+ 0x3e95,0x3e98,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ 0x3eec,0x3eeb,0x3f13,0x3f14,0x3f38,0x3f3a,0x3f39,UBOGON,0x3f68,0x3f67,
+ UBOGON,UBOGON,UBOGON,0x3fbe,0x3fbc,UBOGON,UBOGON,UBOGON,0x3fbb,UBOGON,
+ 0x3fba,UBOGON,0x3fb9,0x3fb7,UBOGON,UBOGON,0x3fc1,UBOGON,0x3ff7,UBOGON,
+ 0x4060,UBOGON,UBOGON,0x4059,0x405c,0x405a,0x4058,UBOGON,0x405b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x405d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x40d4,0x40d3,0x78bf,0x40d2,0x78bd,UBOGON,0x40d7,0x40d1,
+ 0x78e4,0x40d5,UBOGON,UBOGON,0x416d,0x416f,0x7a34,0x4175,0x416c,0x4174,
+ UBOGON,0x4173,UBOGON,UBOGON,0x7a36,0x41ac,UBOGON,UBOGON,UBOGON,0x4210,
+ UBOGON,UBOGON,0x7bba,0x7bbc,0x420f,0x7bc8,0x4223,0x7bc3,0x421d,0x7bb6,
+ 0x420e,UBOGON,UBOGON,0x4215
+ },
+ { /* ku 2c */
+ 0x7bc2,0x4213,UBOGON,UBOGON,0x421b,0x7bc5,0x4222,0x4226,UBOGON,0x7bbd,
+ 0x7bb0,0x4221,0x421c,0x4217,UBOGON,0x421a,0x7bbb,UBOGON,UBOGON,UBOGON,
+ 0x4299,UBOGON,0x4297,UBOGON,UBOGON,0x42fd,UBOGON,0x42f6,0x42fe,0x42f5,
+ 0x42ff,0x42f7,UBOGON,UBOGON,UBOGON,0x4301,0x7e04,UBOGON,UBOGON,UBOGON,
+ 0x4377,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43ab,UBOGON,UBOGON,0x43c0,
+ UBOGON,0x4431,0x442e,UBOGON,UBOGON,0x442c,UBOGON,UBOGON,0x4432,0x442f,
+ 0x442b,0x442d,0x4433,UBOGON,UBOGON,0x81f1,0x4457,0x445c,0x447b,UBOGON,
+ UBOGON,0x447a,UBOGON,0x8522,0x4513,0x451e,0x4517,0x4520,0x452a,0x4511,
+ 0x4515,0x450f,0x4518,0x8538,UBOGON,UBOGON,0x452c,0x8532,UBOGON,0x8510,
+ UBOGON,UBOGON,0x451c,UBOGON
+ },
+ { /* ku 2d */
+ 0x4529,UBOGON,UBOGON,0x4512,0x854f,0x4597,UBOGON,0x8772,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45d4,0x45d0,0x877c,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x45d3,0x4614,UBOGON,0x4646,0x4645,UBOGON,0x4643,UBOGON,0x890d,
+ 0x4644,0x4648,UBOGON,0x4647,UBOGON,UBOGON,UBOGON,0x8908,0x4649,0x4685,
+ UBOGON,0x4684,UBOGON,0x469d,UBOGON,0x469e,0x46a0,UBOGON,0x469c,UBOGON,
+ 0x469f,UBOGON,0x46f7,0x46ea,UBOGON,0x46ef,0x46e9,0x46f3,0x46f0,0x46eb,
+ UBOGON,0x46ec,0x46f2,0x46f5,0x46ee,UBOGON,UBOGON,0x473a,0x474b,UBOGON,
+ 0x474a,0x474c,UBOGON,UBOGON,UBOGON,0x4779,0x477b,0x4778,UBOGON,UBOGON,
+ 0x47b5,UBOGON,0x47b4,0x47b7,0x8d9e,0x4809,0x47fe,0x4808,0x4807,UBOGON,
+ UBOGON,UBOGON,0x4806,0x4804
+ },
+ { /* ku 2e */
+ 0x4805,0x47ff,0x480b,UBOGON,UBOGON,0x483b,0x485d,0x485c,0x485f,0x485e,
+ 0x8f28,UBOGON,0x8f21,0x4883,UBOGON,UBOGON,0x48a7,0x9066,0x906c,UBOGON,
+ 0x48a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x90f6,0x48e0,0x48df,0x48fe,
+ 0x48fc,0x48ff,0x48fd,UBOGON,0x492c,0x92ec,0x92ba,0x92e3,0x92bd,0x499d,
+ UBOGON,0x95b4,UBOGON,0x4a40,UBOGON,UBOGON,0x4a5f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x97d1,UBOGON,0x4ac0,0x9823,UBOGON,0x4ac1,
+ 0x4ac6,UBOGON,UBOGON,0x4b04,0x4b05,UBOGON,0x990b,0x4b3e,0x4b3d,0x4b40,
+ 0x4b3f,UBOGON,0x4b42,UBOGON,UBOGON,UBOGON,0x4b84,0x4b82,0x4b7f,0x4b85,
+ UBOGON,UBOGON,0x4bcc,0x9ab2,0x4bcb,0x4bcd,UBOGON,UBOGON,0x9adb,UBOGON,
+ 0x4bf5,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ 0x9af0,UBOGON,UBOGON,0x4c20,0x4c21,UBOGON,UBOGON,0x4c37,0x4c3e,0x9b73,
+ 0x4c3d,0x9b6e,UBOGON,UBOGON,UBOGON,0x9b65,UBOGON,0x4c3c,UBOGON,0x4c38,
+ 0x9b6a,UBOGON,0x9b6d,UBOGON,0x4c3b,UBOGON,0x4cb0,UBOGON,UBOGON,UBOGON,
+ 0x4cad,0x4cb2,0x4cb8,0x9d0b,UBOGON,0x4caf,UBOGON,UBOGON,0x4d1a,0x9e76,
+ 0x4d20,0x4d21,0x4d30,0x9ea8,0x4d2f,UBOGON,UBOGON,UBOGON,UBOGON,0x4d5f,
+ 0x4d60,UBOGON,UBOGON,0x9f11,UBOGON,UBOGON,0x348a,0x5119,0x349c,UBOGON,
+ 0x349a,UBOGON,UBOGON,UBOGON,0x350c,0x350b,0x350d,0x5292,UBOGON,UBOGON,
+ 0x35fe,UBOGON,UBOGON,0x35ff,0x35fb,0x35fc,0x3609,UBOGON,0x3600,UBOGON,
+ 0x5675,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3671,UBOGON,UBOGON,
+ 0x596f,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0x372b,UBOGON,UBOGON,UBOGON,0x3814,0x3811,0x3812,UBOGON,0x3863,UBOGON,
+ 0x386e,0x389a,UBOGON,0x389b,UBOGON,UBOGON,0x38c8,UBOGON,UBOGON,0x38f6,
+ UBOGON,UBOGON,0x61a5,0x398c,0x3997,0x39a2,0x61a0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a54,UBOGON,UBOGON,UBOGON,
+ 0x3aa8,UBOGON,UBOGON,0x3aa9,UBOGON,0x65b4,0x65d8,0x66c2,0x3b18,0x3b17,
+ 0x3b1d,UBOGON,UBOGON,0x3b31,UBOGON,UBOGON,0x3bd8,0x3bd5,UBOGON,UBOGON,
+ UBOGON,0x3be1,UBOGON,0x3bd4,UBOGON,UBOGON,UBOGON,0x3be3,UBOGON,0x3c44,
+ 0x3c45,UBOGON,UBOGON,0x3c76,0x3c75,UBOGON,0x6ba8,0x3c88,0x3caa,UBOGON,
+ 0x3cab,0x3cac,UBOGON,0x3d57,0x6f83,0x3d60,0x3d5d,0x3d6b,UBOGON,0x3d63,
+ 0x3d67,UBOGON,0x3d5e,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,0x6fc5,0x71cd,0x3df9,0x3df3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3df6,UBOGON,0x729c,0x3e51,0x3e53,0x3e52,UBOGON,0x3e9b,UBOGON,0x3e9c,
+ UBOGON,UBOGON,0x3ef7,0x7499,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3f3b,UBOGON,0x3f3e,0x3f3d,UBOGON,0x3f69,UBOGON,UBOGON,UBOGON,0x3fc3,
+ 0x3fc4,0x3fc7,UBOGON,UBOGON,0x7639,0x3fc6,0x762e,0x3fc8,UBOGON,UBOGON,
+ UBOGON,0x769f,0x76a0,0x3fe6,0x3ff8,UBOGON,UBOGON,0x4007,UBOGON,0x4064,
+ 0x4068,UBOGON,0x7794,0x4065,0x77ae,UBOGON,UBOGON,0x4069,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40da,0x40e0,0x78e6,UBOGON,0x40de,
+ UBOGON,UBOGON,UBOGON,0x411c,0x411d,0x411a,UBOGON,0x417b,0x417a,0x417c,
+ UBOGON,0x4178,0x4177,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,0x41b1,UBOGON,0x41b2,0x41b0,UBOGON,0x7abc,UBOGON,0x4236,UBOGON,
+ 0x422e,UBOGON,0x7bd6,UBOGON,0x4234,UBOGON,UBOGON,UBOGON,UBOGON,0x422a,
+ UBOGON,0x4233,0x422d,0x422f,0x4231,0x422b,0x4232,UBOGON,0x4235,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ccf,UBOGON,UBOGON,0x4308,UBOGON,
+ 0x4306,UBOGON,UBOGON,0x7e18,UBOGON,0x434d,0x4361,UBOGON,UBOGON,UBOGON,
+ 0x4378,UBOGON,0x4394,0x4395,UBOGON,UBOGON,UBOGON,0x43c1,0x43c3,0x806d,
+ UBOGON,0x4439,UBOGON,UBOGON,0x443a,0x443b,0x4435,0x4436,UBOGON,0x443c,
+ 0x8190,UBOGON,UBOGON,UBOGON,0x4458,0x447c,0x447d,0x448d,0x448c,UBOGON,
+ UBOGON,0x453b,0x453f,UBOGON,UBOGON,0x4532,0x452d,UBOGON,0x452f,0x4539,
+ 0x452e,0x453a,UBOGON,0x4536
+ },
+ { /* ku 33 */
+ 0x4531,0x453e,0x4538,0x8552,0x4534,UBOGON,0x4541,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4530,UBOGON,UBOGON,UBOGON,UBOGON,0x4543,UBOGON,0x8550,UBOGON,
+ UBOGON,0x4598,UBOGON,UBOGON,0x87a0,UBOGON,UBOGON,0x8786,0x45da,0x45d7,
+ UBOGON,UBOGON,0x8795,UBOGON,UBOGON,0x878c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4618,0x8860,UBOGON,UBOGON,UBOGON,0x4652,0x8928,UBOGON,0x464e,
+ 0x8920,UBOGON,0x464f,0x4650,UBOGON,UBOGON,0x89a8,0x4686,0x4687,0x4689,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46a2,0x46a3,UBOGON,0x46a1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x46f9,0x46fd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x473e,0x473c,UBOGON,0x474d,UBOGON,0x474e,UBOGON,0x4781,0x4783,
+ 0x4782,UBOGON,0x4780,0x4788
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b9,UBOGON,UBOGON,0x8e3a,UBOGON,
+ UBOGON,UBOGON,0x4811,0x480d,0x4810,0x4813,UBOGON,0x483c,0x4862,0x4863,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4903,0x4906,0x4902,0x4901,UBOGON,
+ UBOGON,0x4905,UBOGON,0x9194,0x9311,UBOGON,0x9337,0x4936,UBOGON,0x4935,
+ 0x9343,UBOGON,0x49a1,0x49a3,UBOGON,UBOGON,0x49a0,UBOGON,0x49ea,0x96a6,
+ 0x49e8,UBOGON,UBOGON,UBOGON,UBOGON,0x4a21,0x4a1b,UBOGON,UBOGON,0x4a49,
+ 0x4a48,UBOGON,0x9795,0x4a62,0x4a61,0x4a64,0x4a60,0x4a63,UBOGON,UBOGON,
+ 0x9796,0x4a66,0x4aac,0x4aab,UBOGON,UBOGON,0x4ac3,UBOGON,0x4ac4,0x9825,
+ UBOGON,UBOGON,0x4b08,0x4b09,0x4b0a,0x4b06,0x4b07,0x4b41,UBOGON,0x4b45,
+ UBOGON,0x4b43,0x4b44,0x9926
+ },
+ { /* ku 35 */
+ 0x9934,0x4b47,UBOGON,UBOGON,UBOGON,0x4b71,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4b8b,0x4bd4,0x4bd3,0x4bd1,0x9aba,0x4bd2,UBOGON,UBOGON,
+ 0x4bf7,0x4bf8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c22,UBOGON,0x4c45,0x4c41,0x9b81,0x4c40,0x9b8a,UBOGON,0x9b7f,0x4c42,
+ UBOGON,0x4cc1,0x4cc5,UBOGON,0x4cbb,0x4cb9,0x4cbd,0x4cc9,UBOGON,0x9d11,
+ UBOGON,0x4cbf,0x4cc7,UBOGON,0x4cc3,0x4d24,0x4d31,0x4d33,UBOGON,0x4d32,
+ 0x4d34,0x4d52,0x4d61,0x9ed9,0x4d7a,0x4d82,0x9f3c,UBOGON,0x5123,UBOGON,
+ UBOGON,UBOGON,0x512c,UBOGON,UBOGON,0x350f,0x5295,UBOGON,0x3523,0x3525,
+ UBOGON,0x3606,0x3608,0x5688,UBOGON,UBOGON,UBOGON,0x568b,UBOGON,UBOGON,
+ 0x367a,0x3677,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x372e,UBOGON,0x372f,UBOGON,UBOGON,0x381b,0x3813,UBOGON,UBOGON,
+ 0x3866,UBOGON,0x3865,UBOGON,0x38f7,UBOGON,UBOGON,0x61e1,0x61d7,UBOGON,
+ UBOGON,0x399c,UBOGON,UBOGON,0x3a53,UBOGON,UBOGON,UBOGON,UBOGON,0x3aba,
+ 0x65a3,0x3b22,0x66d3,UBOGON,UBOGON,UBOGON,0x6a8b,UBOGON,UBOGON,0x3beb,
+ 0x3bdd,UBOGON,0x3bef,UBOGON,UBOGON,0x3c47,0x3c46,UBOGON,0x3c78,0x6bac,
+ 0x3c89,UBOGON,UBOGON,0x3d68,0x3d76,0x3d74,0x3d79,UBOGON,0x3d7a,0x3d77,
+ UBOGON,0x3d71,UBOGON,0x3d72,UBOGON,0x3dff,UBOGON,0x3e05,UBOGON,UBOGON,
+ 0x3e54,UBOGON,UBOGON,0x3e9e,0x3e9f,0x7374,UBOGON,UBOGON,0x3efa,UBOGON,
+ 0x3f44,0x3f3f,0x3f40,UBOGON,0x3f42,UBOGON,UBOGON,0x3f51,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ 0x7640,0x3fca,UBOGON,0x7641,0x3fce,0x3fc9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4009,0x76e8,0x406c,0x406e,0x4070,0x406d,0x406b,0x4071,0x4072,
+ UBOGON,UBOGON,UBOGON,0x408c,0x40e4,UBOGON,0x40e1,UBOGON,UBOGON,0x78f6,
+ 0x40e7,0x7900,0x40e2,0x411f,UBOGON,UBOGON,0x417e,UBOGON,0x4180,0x7a59,
+ 0x7a55,UBOGON,0x41b9,0x41b7,0x41b8,UBOGON,0x41ba,0x7af4,UBOGON,0x41d3,
+ 0x423f,0x7c04,0x4245,0x4241,0x7c15,0x4242,0x4243,0x423b,0x4238,UBOGON,
+ UBOGON,0x423a,0x7bf5,UBOGON,UBOGON,0x423c,UBOGON,UBOGON,0x423e,UBOGON,
+ UBOGON,UBOGON,0x429e,0x429f,0x42a1,UBOGON,0x429b,0x4312,UBOGON,UBOGON,
+ UBOGON,0x4318,0x430c,UBOGON,0x4362,UBOGON,0x437a,UBOGON,UBOGON,0x43ae,
+ 0x43af,UBOGON,0x43ad,UBOGON
+ },
+ { /* ku 38 */
+ 0x43c4,0x43c7,0x43c6,0x43c5,UBOGON,UBOGON,0x81c1,0x4440,UBOGON,UBOGON,
+ 0x443f,0x4441,UBOGON,UBOGON,UBOGON,0x447f,UBOGON,0x4486,0x4481,0x4480,
+ 0x448e,0x454a,UBOGON,0x4547,UBOGON,UBOGON,0x454b,0x4546,0x454e,0x857d,
+ UBOGON,0x85a5,UBOGON,0x4548,UBOGON,0x4545,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45db,0x45e7,0x45e4,UBOGON,UBOGON,0x45e1,
+ UBOGON,0x45e9,UBOGON,0x45e5,0x45e0,0x45e3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45ea,UBOGON,UBOGON,0x893a,0x4654,0x4658,0x465c,UBOGON,
+ 0x4655,0x468b,0x468c,0x46a6,0x46a5,UBOGON,UBOGON,UBOGON,UBOGON,0x46ff,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4730,0x4740,0x4741,UBOGON,UBOGON,0x4786,
+ UBOGON,0x47bf,0x47bd,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x47be,0x4819,UBOGON,0x481c,UBOGON,0x481b,
+ 0x4817,0x4818,0x8e51,UBOGON,UBOGON,0x483d,0x486a,0x4866,UBOGON,UBOGON,
+ 0x4867,UBOGON,0x4868,0x48ad,0x48ae,UBOGON,UBOGON,0x48d6,0x4909,UBOGON,
+ 0x9198,UBOGON,0x490c,0x490a,UBOGON,0x493b,0x493a,0x9384,0x9381,UBOGON,
+ 0x936f,UBOGON,UBOGON,UBOGON,0x49af,0x49aa,0x49ab,UBOGON,UBOGON,0x49b1,
+ UBOGON,0x49ac,0x49ec,UBOGON,UBOGON,0x4a01,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a23,UBOGON,0x4a24,0x4a1e,UBOGON,0x4a4a,0x4a65,0x4a6a,UBOGON,UBOGON,
+ UBOGON,0x4a69,UBOGON,UBOGON,0x4a95,UBOGON,UBOGON,UBOGON,UBOGON,0x9842,
+ UBOGON,UBOGON,0x4acc,UBOGON,UBOGON,UBOGON,0x4acf,UBOGON,UBOGON,0x4b0f,
+ UBOGON,0x4b0e,0x4b0b,0x4b10
+ },
+ { /* ku 3a */
+ 0x4b0d,0x4b0c,UBOGON,UBOGON,UBOGON,UBOGON,0x4b46,0x4b48,0x9937,0x4b49,
+ UBOGON,UBOGON,0x4b91,0x4b8e,UBOGON,0x4bd8,0x4bd6,UBOGON,0x4bda,UBOGON,
+ 0x4bd7,UBOGON,0x9aff,0x4bf9,UBOGON,UBOGON,0x4bfc,UBOGON,UBOGON,UBOGON,
+ 0x9ba9,0x4c4a,0x9ba7,0x4c4e,0x9bb3,0x9bac,0x9bb0,UBOGON,UBOGON,UBOGON,
+ 0x9b9c,UBOGON,UBOGON,UBOGON,0x9d3c,0x9d1c,0x9d3a,0x4cd3,0x4ccd,0x4cd1,
+ UBOGON,UBOGON,0x9d32,0x9d34,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9ec7,UBOGON,0x4d62,UBOGON,UBOGON,UBOGON,0x4d83,0x9f3f,UBOGON,0x4d92,
+ 0x349f,0x34a0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3527,UBOGON,0x360b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x368d,
+ UBOGON,UBOGON,0x3770,UBOGON
+ },
+ { /* ku 3b */
+ 0x5eeb,UBOGON,0x399a,0x399f,0x399d,UBOGON,UBOGON,UBOGON,0x399b,UBOGON,
+ 0x61d5,UBOGON,0x3a60,0x3a64,0x3a69,0x3a63,0x3a67,0x3a62,UBOGON,UBOGON,
+ UBOGON,0x6502,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3b26,0x3b23,0x3b25,UBOGON,UBOGON,0x3bf8,UBOGON,UBOGON,UBOGON,0x3bf7,
+ 0x3bfb,0x3bfa,UBOGON,UBOGON,UBOGON,UBOGON,0x3cb0,0x3caf,UBOGON,UBOGON,
+ UBOGON,0x3d7e,UBOGON,0x3d7d,0x3d80,UBOGON,UBOGON,0x3d7f,0x3d86,UBOGON,
+ UBOGON,0x7012,UBOGON,0x3d81,UBOGON,UBOGON,UBOGON,0x3e24,0x3e58,0x3e57,
+ 0x3ea0,UBOGON,0x3efe,UBOGON,UBOGON,0x3f15,UBOGON,0x3f47,0x3f46,UBOGON,
+ UBOGON,0x3f6b,0x3f6c,0x7585,0x7654,UBOGON,0x3fcc,UBOGON,0x7655,UBOGON,
+ 0x3fcb,0x76a7,0x76a8,0x3ff9
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4078,0x407a,0x4075,UBOGON,0x4076,0x4077,
+ UBOGON,UBOGON,0x40ea,0x40ee,0x40ed,UBOGON,0x40ec,0x790f,UBOGON,UBOGON,
+ 0x4184,0x4185,0x4183,UBOGON,0x41bc,0x41bd,0x41d4,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4255,UBOGON,0x4250,0x424c,0x4248,UBOGON,0x4253,UBOGON,0x4257,
+ 0x4254,0x424e,0x424a,0x4251,UBOGON,UBOGON,0x4249,0x424b,0x4263,UBOGON,
+ UBOGON,0x42a7,0x42a6,0x42a4,UBOGON,UBOGON,UBOGON,0x7ce4,0x7ce5,UBOGON,
+ UBOGON,0x7e65,0x7e4e,0x4317,UBOGON,0x4316,UBOGON,UBOGON,0x4363,UBOGON,
+ UBOGON,0x7f82,UBOGON,0x437b,0x437c,UBOGON,UBOGON,UBOGON,UBOGON,0x43b0,
+ 0x802d,UBOGON,UBOGON,0x4442,UBOGON,0x4444,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4488,0x448f,0x4553
+ },
+ { /* ku 3d */
+ 0x455b,UBOGON,0x4559,UBOGON,UBOGON,UBOGON,UBOGON,0x85ca,UBOGON,UBOGON,
+ 0x4554,0x85bc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4599,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45f1,UBOGON,0x45ef,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4662,UBOGON,0x4663,UBOGON,0x4660,
+ 0x4661,0x465f,UBOGON,UBOGON,UBOGON,0x468d,UBOGON,0x468e,UBOGON,UBOGON,
+ UBOGON,0x4709,UBOGON,UBOGON,0x4705,UBOGON,UBOGON,0x4703,0x4706,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4731,UBOGON,UBOGON,0x474f,UBOGON,UBOGON,
+ UBOGON,0x4766,0x8cff,0x47c4,UBOGON,0x47c3,0x47c1,0x47c5,UBOGON,UBOGON,
+ UBOGON,0x4821,UBOGON,0x481f,0x4822,UBOGON,0x4827,0x4820,UBOGON,0x486d,
+ 0x486c,0x486b,0x486f,0x4870
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,0x91a6,UBOGON,UBOGON,0x4942,UBOGON,0x93b6,UBOGON,
+ 0x4944,0x4940,UBOGON,UBOGON,0x493f,UBOGON,0x93ab,0x498b,UBOGON,UBOGON,
+ 0x4a25,0x4a28,UBOGON,0x9721,UBOGON,UBOGON,0x4a75,0x4a72,UBOGON,0x4a6f,
+ UBOGON,UBOGON,0x4a76,0x4a71,0x97a7,UBOGON,0x4a97,UBOGON,0x4ad7,UBOGON,
+ 0x4ad6,UBOGON,0x4ad8,0x4adc,0x4adb,0x4ad4,0x983e,0x4b13,0x4b11,0x4b14,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4b51,0x4b50,0x4b53,0x4b54,0x4b52,UBOGON,
+ UBOGON,UBOGON,0x4b6d,UBOGON,UBOGON,0x4b95,0x4b99,UBOGON,0x4b9a,UBOGON,
+ 0x4b93,0x4b97,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bdc,UBOGON,0x4bfd,
+ UBOGON,UBOGON,0x4bfe,0x4c00,0x4c02,0x4c01,0x4c03,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c27,0x4c26,0x4c24
+ },
+ { /* ku 3f */
+ 0x4c4c,0x9bbc,0x4c50,0x4c55,0x4c53,0x9bb7,0x4c52,UBOGON,0x4c57,0x9bbe,
+ 0x4c58,0x4cd6,UBOGON,UBOGON,0x4cd4,UBOGON,0x4cda,0x4cd9,UBOGON,0x9d62,
+ 0x4cd5,0x4ce4,UBOGON,0x4cdc,0x4d1b,0x9e8f,0x4d37,0x4d36,0x4d4b,0x9ecb,
+ 0x4d66,0x4d76,UBOGON,0x4d7e,0x4d7d,0x4d7f,0x4d84,0x4d8b,UBOGON,0x4d94,
+ 0x34a1,0x3511,UBOGON,0x3610,0x56a9,UBOGON,UBOGON,UBOGON,UBOGON,0x5913,
+ UBOGON,0x3732,0x5bf4,UBOGON,UBOGON,UBOGON,UBOGON,0x3820,UBOGON,UBOGON,
+ UBOGON,0x389d,UBOGON,0x61ec,0x61ef,UBOGON,UBOGON,0x39a5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c02,0x3bfe,0x3c01,
+ 0x6ad6,0x3c03,0x3bff,UBOGON,0x3c04,UBOGON,0x3c4a,UBOGON,0x3d87,UBOGON,
+ 0x3d84,UBOGON,0x3d85,0x7209
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,0x3e59,0x7379,UBOGON,0x74c6,UBOGON,UBOGON,0x3f04,
+ 0x3f49,0x3f48,UBOGON,0x3f6d,0x3fd2,0x3fd3,UBOGON,0x3fd1,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fe7,0x400a,0x77c3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x40f0,UBOGON,UBOGON,0x791f,UBOGON,UBOGON,0x7a65,UBOGON,UBOGON,0x41be,
+ 0x41bf,UBOGON,0x7ac6,0x7c3a,UBOGON,0x7c36,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x425e,UBOGON,UBOGON,0x425b,UBOGON,UBOGON,UBOGON,0x7ceb,0x42ab,UBOGON,
+ 0x42ac,UBOGON,UBOGON,UBOGON,UBOGON,0x431f,0x431d,UBOGON,UBOGON,0x431c,
+ UBOGON,0x431e,UBOGON,UBOGON,UBOGON,UBOGON,0x4364,UBOGON,0x7f84,UBOGON,
+ UBOGON,UBOGON,0x4448,0x4447,UBOGON,UBOGON,0x455e,0x4561,UBOGON,0x85e0,
+ 0x85f3,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ 0x45f7,UBOGON,UBOGON,0x45f4,0x45f8,0x881e,UBOGON,UBOGON,UBOGON,0x4664,
+ UBOGON,0x4692,0x468f,0x4690,0x89b4,0x4693,0x46a8,UBOGON,UBOGON,0x46a9,
+ UBOGON,UBOGON,0x89f9,UBOGON,UBOGON,0x8b44,0x470e,UBOGON,0x470f,UBOGON,
+ UBOGON,0x470b,UBOGON,UBOGON,UBOGON,UBOGON,0x4710,0x4751,UBOGON,0x4750,
+ UBOGON,0x4763,UBOGON,UBOGON,0x47c7,UBOGON,0x8e71,0x4824,0x4826,0x8e6e,
+ UBOGON,0x8e79,UBOGON,0x8ec4,0x4874,0x4873,0x4872,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48b1,0x908c,UBOGON,0x490e,0x4911,0x4910,0x490f,0x4912,0x4949,
+ 0x93c9,0x494f,0x494d,UBOGON,UBOGON,0x4955,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a2c,0x4a2b,UBOGON,UBOGON,0x4a79,UBOGON,UBOGON,UBOGON,
+ 0x4a7a,0x97b0,0x4a99,0x4a9a
+ },
+ { /* ku 42 */
+ UBOGON,0x4aae,0x4aaf,UBOGON,0x4ae4,0x4ae1,0x4ade,0x4ae6,0x4adf,UBOGON,
+ 0x4ae7,0x4ae2,0x4ae0,UBOGON,0x4ae5,0x985a,UBOGON,0x4b18,UBOGON,UBOGON,
+ UBOGON,0x4b56,UBOGON,0x9946,UBOGON,UBOGON,UBOGON,0x4b6e,UBOGON,UBOGON,
+ 0x4b9d,0x4ba0,0x4b9c,UBOGON,UBOGON,0x4ba1,0x4ba2,0x4b9f,0x4bdf,0x4bde,
+ 0x9ac3,0x4bea,UBOGON,0x4c06,UBOGON,0x4c04,0x9b0f,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c5f,0x9bf4,0x9bfa,0x4c5c,UBOGON,0x4c5e,UBOGON,UBOGON,0x9bdd,
+ 0x4c59,UBOGON,UBOGON,0x4c64,0x4c5d,0x4c62,UBOGON,0x4c65,UBOGON,0x9bed,
+ 0x4c5b,0x9bef,UBOGON,0x4cdd,UBOGON,0x4cdf,UBOGON,UBOGON,0x4ce2,UBOGON,
+ UBOGON,0x4d27,UBOGON,UBOGON,0x9e96,0x4d3a,0x4d3c,UBOGON,0x4d39,UBOGON,
+ UBOGON,0x4d3d,0x4d3b,0x9eb3
+ },
+ { /* ku 43 */
+ 0x4d4c,UBOGON,0x4d68,0x9ee2,UBOGON,0x4d80,0x4d85,UBOGON,0x4d95,UBOGON,
+ 0x4d96,UBOGON,0x9f8f,UBOGON,0x34a4,0x3512,0x56b1,0x3625,UBOGON,0x5b41,
+ 0x3737,UBOGON,UBOGON,UBOGON,UBOGON,0x3868,0x3867,0x389e,UBOGON,UBOGON,
+ UBOGON,0x39aa,UBOGON,0x39a9,0x39a4,UBOGON,UBOGON,0x3a71,0x3a6f,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3aad,UBOGON,0x6af6,0x3c0c,0x6af2,0x3c0b,UBOGON,
+ UBOGON,0x3c0f,0x3c79,UBOGON,UBOGON,UBOGON,UBOGON,0x3d8d,0x3d8f,UBOGON,
+ UBOGON,0x3d8e,0x3e0c,UBOGON,UBOGON,UBOGON,0x3ea6,UBOGON,0x3ea3,0x3ea4,
+ 0x3ea5,0x7588,0x3f6e,UBOGON,UBOGON,0x3ffa,UBOGON,0x407c,0x407e,0x407b,
+ 0x407d,UBOGON,UBOGON,0x408d,0x40f4,0x40f3,UBOGON,UBOGON,0x4189,UBOGON,
+ UBOGON,0x41c0,UBOGON,0x4265
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,0x42ad,0x4325,UBOGON,UBOGON,UBOGON,0x43c9,UBOGON,0x444a,
+ UBOGON,0x8267,0x4489,UBOGON,0x4566,0x4570,UBOGON,0x456d,0x4569,0x4567,
+ UBOGON,0x4572,0x860e,0x456e,UBOGON,0x459c,0x45fc,0x45fd,0x4604,0x45ff,
+ UBOGON,0x45fe,0x4600,UBOGON,0x4666,0x4669,UBOGON,0x46aa,0x46ab,0x4717,
+ UBOGON,UBOGON,UBOGON,0x4715,0x8b5e,0x4712,0x8d0e,UBOGON,UBOGON,UBOGON,
+ 0x47ca,UBOGON,0x47c9,0x47cb,UBOGON,UBOGON,UBOGON,0x4829,0x4828,UBOGON,
+ UBOGON,UBOGON,0x4840,0x4875,0x4876,UBOGON,0x4888,UBOGON,0x91b6,0x4957,
+ 0x9401,UBOGON,0x495f,UBOGON,0x941d,0x4958,0x495b,UBOGON,0x942f,UBOGON,
+ 0x49b3,UBOGON,0x49ef,UBOGON,0x4a30,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a41,0x4a4b,UBOGON,0x4a7d
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,0x4a7c,UBOGON,UBOGON,UBOGON,0x97e0,UBOGON,0x97db,UBOGON,
+ UBOGON,0x9861,UBOGON,UBOGON,0x4ae8,0x4aea,0x4ae9,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b1b,UBOGON,UBOGON,0x4b55,0x994a,0x4b59,0x4b58,UBOGON,UBOGON,
+ UBOGON,0x4ba4,0x4ba3,UBOGON,UBOGON,UBOGON,UBOGON,0x9a33,0x4ba7,UBOGON,
+ 0x4be0,UBOGON,UBOGON,UBOGON,0x4c08,0x4c0a,0x4c09,UBOGON,UBOGON,UBOGON,
+ 0x4c71,0x9c0f,0x4c6c,UBOGON,0x9c11,UBOGON,0x9c03,0x9c01,0x4c6e,UBOGON,
+ 0x9c16,UBOGON,UBOGON,UBOGON,0x4ce0,0x4cee,UBOGON,0x4ceb,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9d93,0x4cea,0x4cef,0x4ce7,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4d48,0x4d49,UBOGON,UBOGON,UBOGON,0x4d4d,UBOGON,UBOGON,0x4d55,UBOGON,
+ UBOGON,0x4d6a,0x4d6c,UBOGON
+ },
+ { /* ku 46 */
+ 0x4d6b,UBOGON,UBOGON,UBOGON,0x4d98,0x4d99,0x4d97,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x535b,UBOGON,0x3616,UBOGON,0x56bf,
+ UBOGON,UBOGON,0x3739,UBOGON,UBOGON,0x3825,0x5dce,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3a74,UBOGON,UBOGON,0x3aae,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3d92,0x3d94,UBOGON,0x3d95,UBOGON,0x3e0d,UBOGON,0x3e25,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fd5,0x3fd6,0x76ac,0x3fe8,UBOGON,
+ 0x407f,0x77d2,0x40f5,0x40f6,0x40f7,UBOGON,0x4124,0x418d,0x418a,UBOGON,
+ UBOGON,0x426c,0x4266,0x426a,UBOGON,0x4267,0x426d,0x4268,0x7c52,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4365,UBOGON,0x439a,UBOGON
+ },
+ { /* ku 47 */
+ 0x43b1,0x444b,0x444d,0x444c,0x444e,UBOGON,0x4573,0x4575,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4603,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x471e,UBOGON,0x8b73,UBOGON,0x4719,0x471c,
+ 0x471a,0x471d,0x8b76,UBOGON,UBOGON,0x4743,0x4752,UBOGON,UBOGON,0x4795,
+ UBOGON,0x47cc,UBOGON,0x482b,UBOGON,UBOGON,UBOGON,0x482a,0x8ec7,0x4877,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4913,0x4914,0x9434,
+ UBOGON,UBOGON,0x495d,UBOGON,0x4960,0x943e,0x4962,UBOGON,UBOGON,0x49b2,
+ 0x49f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a4c,UBOGON,0x4a82,
+ 0x97bc,0x4a81,0x4a9b,UBOGON,0x4aa4,0x4aee,0x4aec,UBOGON,0x4aed,UBOGON,
+ 0x4af0,0x4aef,UBOGON,0x4b1d
+ },
+ { /* ku 48 */
+ UBOGON,0x4b60,0x4b5e,0x4b5d,UBOGON,UBOGON,UBOGON,0x4bb1,0x4bab,0x4bac,
+ 0x4bad,UBOGON,0x4bae,UBOGON,UBOGON,0x4be2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9b39,UBOGON,UBOGON,0x9c2a,0x4c7b,0x9c26,0x4c78,0x4c75,0x9c27,
+ UBOGON,0x4cf2,0x4cf4,0x4cf3,0x9dc0,0x9dc9,UBOGON,UBOGON,UBOGON,0x4d3f,
+ 0x4d3e,0x4d40,0x4d4e,0x4d57,0x4d59,0x4d58,0x4d56,UBOGON,UBOGON,0x4d6e,
+ UBOGON,UBOGON,UBOGON,0x9eec,UBOGON,UBOGON,0x4d81,0x4d86,UBOGON,0x4d8f,
+ UBOGON,UBOGON,UBOGON,0x9f68,0x4d9b,0x4db1,0x4db3,UBOGON,0x373a,UBOGON,
+ UBOGON,0x3827,UBOGON,UBOGON,0x386a,0x39ac,UBOGON,0x3c18,UBOGON,UBOGON,
+ 0x3c4c,UBOGON,0x3d96,UBOGON,0x3f4a,UBOGON,UBOGON,0x4081,UBOGON,0x4083,
+ 0x40f9,0x40f8,UBOGON,0x418e
+ },
+ { /* ku 49 */
+ 0x418f,0x41c1,UBOGON,UBOGON,UBOGON,0x4270,UBOGON,0x4271,UBOGON,0x432a,
+ 0x432d,0x437d,0x8032,0x8031,UBOGON,0x444f,UBOGON,UBOGON,0x4490,UBOGON,
+ UBOGON,UBOGON,0x4579,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4605,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x89fd,UBOGON,UBOGON,0x4721,
+ UBOGON,UBOGON,UBOGON,0x4732,UBOGON,UBOGON,0x47cf,UBOGON,UBOGON,0x908e,
+ 0x4916,0x4915,0x49b5,0x4a08,UBOGON,0x4a32,UBOGON,0x4a33,0x4a34,0x4a3c,
+ UBOGON,0x97c2,UBOGON,0x4a9c,UBOGON,0x4af4,0x4af2,UBOGON,0x4b62,UBOGON,
+ 0x4b61,0x4b64,0x4bb5,0x9a4b,0x4bb4,UBOGON,UBOGON,0x4be3,UBOGON,UBOGON,
+ 0x9b1c,0x4c0e,UBOGON,0x9b1b,UBOGON,0x4c2c,0x4c2b,UBOGON,UBOGON,UBOGON,
+ 0x4c85,0x4c81,0x4c7e,0x4c83
+ },
+ { /* ku 4a */
+ 0x4c80,UBOGON,0x9c42,UBOGON,0x9dd4,0x4cfb,0x4cf7,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4cf8,UBOGON,UBOGON,UBOGON,UBOGON,0x4d5a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d78,UBOGON,UBOGON,UBOGON,UBOGON,0x4d9d,0x4d9c,UBOGON,
+ UBOGON,UBOGON,0x34a9,0x34bf,0x56d0,0x56cf,UBOGON,0x5dda,UBOGON,0x3a77,
+ 0x3a76,UBOGON,0x3abb,0x66ea,UBOGON,0x3d9b,UBOGON,0x3e0f,0x3e5b,UBOGON,
+ 0x3f4c,0x3f6f,0x3fd9,UBOGON,0x4082,UBOGON,UBOGON,UBOGON,UBOGON,0x4274,
+ 0x4272,UBOGON,UBOGON,UBOGON,0x4273,UBOGON,UBOGON,UBOGON,0x42b1,0x432e,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x434e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x460b,UBOGON,UBOGON,
+ 0x466c,0x8b89,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ 0x478b,UBOGON,0x47d0,0x482d,UBOGON,0x48e4,0x4971,UBOGON,0x9458,0x496f,
+ UBOGON,0x4a87,0x4aa5,UBOGON,UBOGON,0x4b1e,0x4b65,0x4bb9,0x4bb7,0x4bb8,
+ 0x4be4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c8c,0x4c89,0x4c8a,UBOGON,
+ UBOGON,0x4c8b,UBOGON,UBOGON,UBOGON,0x4d01,0x4cfe,0x9de7,0x4d03,0x4d06,
+ UBOGON,0x9dea,0x9df1,UBOGON,0x4d1d,0x4d43,UBOGON,UBOGON,UBOGON,0x4d4f,
+ UBOGON,UBOGON,0x4d5b,0x4d70,UBOGON,0x4d88,UBOGON,UBOGON,0x4d89,0x9f44,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9f6d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5dd9,UBOGON,UBOGON,UBOGON,UBOGON,0x3d9e,0x3d9f,0x3ea7,0x3f4b,
+ 0x3fdb,0x3fda,UBOGON,0x77d6,0x408e,0x4276,UBOGON,0x4330,0x432f,UBOGON,
+ 0x4366,UBOGON,0x457e,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,0x883a,UBOGON,0x8975,0x466f,UBOGON,0x47d1,0x482f,UBOGON,
+ UBOGON,0x48b2,0x4918,0x4917,UBOGON,0x4976,UBOGON,UBOGON,0x4a4f,0x4a89,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4af5,0x4b1f,UBOGON,UBOGON,0x9a5d,0x4be5,
+ UBOGON,UBOGON,0x4c10,UBOGON,0x4c0f,UBOGON,UBOGON,UBOGON,UBOGON,0x4c2f,
+ 0x4c30,0x9c64,UBOGON,UBOGON,0x4c93,0x4c94,UBOGON,0x4d07,0x4d09,0x4d08,
+ UBOGON,0x4d0b,UBOGON,0x9e0a,UBOGON,UBOGON,UBOGON,0x4d50,0x4d71,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d7b,0x4d7c,UBOGON,0x9f73,UBOGON,0x4da1,
+ UBOGON,UBOGON,0x4da0,0x4da2,UBOGON,0x361b,UBOGON,0x3682,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fe9,UBOGON,0x4084,0x77e1,UBOGON,UBOGON,UBOGON,0x42b3,
+ 0x4334,0x4333,0x4580,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,0x46ad,UBOGON,0x4744,0x4755,UBOGON,0x47d2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a8a,UBOGON,UBOGON,0x4b67,UBOGON,UBOGON,0x4be6,UBOGON,
+ 0x4c13,UBOGON,0x9b2d,UBOGON,0x4c97,0x9e0c,UBOGON,UBOGON,0x4d0c,UBOGON,
+ UBOGON,UBOGON,0x4d46,0x4d5c,0x4d74,0x4d72,UBOGON,UBOGON,UBOGON,0x9f1f,
+ UBOGON,UBOGON,0x4da4,0x4da3,UBOGON,UBOGON,UBOGON,UBOGON,0x4db4,UBOGON,
+ 0x3536,UBOGON,UBOGON,UBOGON,0x3cb2,UBOGON,UBOGON,0x3f16,0x7c70,0x4277,
+ UBOGON,0x457f,UBOGON,UBOGON,0x487d,0x9479,UBOGON,0x974a,0x4a8c,UBOGON,
+ 0x4b68,0x4bbe,0x4c15,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d75,0x4da5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4278,0x4335,0x7e9d,0x4582,UBOGON,
+ 0x4583,UBOGON,0x4671,UBOGON
+ },
+ { /* ku 4e */
+ 0x487e,0x4a8e,UBOGON,0x9960,0x4b69,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c9a,0x4c9b,UBOGON,UBOGON,UBOGON,0x4d90,UBOGON,0x9f9e,UBOGON,
+ UBOGON,0x4586,0x4585,UBOGON,0x460e,UBOGON,0x4695,UBOGON,0x4919,UBOGON,
+ 0x4bc0,UBOGON,UBOGON,UBOGON,0x9ef8,0x9f3a,0x9f7d,UBOGON,UBOGON,0x400d,
+ 0x4c16,UBOGON,0x4da9,0x4daa,0x4085,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9f96,UBOGON,0x4bc2,0x4c31,0x4d11,0x4dab,0x4c9c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 5 conversion table */
+
+static const unsigned short
+ cns11643_5tab[MAX_CNS11643_KU_5][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x355a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6729,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3cbc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x49b9,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34de,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3543,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37ac,0x37aa,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5e07,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5fcb,
+ 0x38fe,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3441,
+ UBOGON,UBOGON,0x34b4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37b3,
+ UBOGON,UBOGON,UBOGON,0x37b4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c1d,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c7c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f55,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4126,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x49bc,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x344a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x344e,UBOGON,UBOGON,0x34c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x52b7,0x52b8,UBOGON,0x52b6,0x52ba,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x357b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3620,UBOGON,UBOGON,UBOGON,UBOGON,0x3689,0x3695,UBOGON,UBOGON,UBOGON,
+ 0x36be,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37c3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3877,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39d4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b4c,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3c20,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c5b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e2d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3e63,UBOGON,UBOGON,UBOGON,0x3f18,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3f74,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4128,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43da,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44a4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x488e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x345b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3753,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3880,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38dd,UBOGON,UBOGON,UBOGON,0x38de,UBOGON,
+ 0x3922,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6306,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3adb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x6b85,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3cda,0x3cdb,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,0x3cd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x401a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ 0x41d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43de,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x43e5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4672,UBOGON,
+ 0x46af,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x49c4,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3463,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x34ec,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3598,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3758,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x37d3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38e2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3954,UBOGON,UBOGON,0x392f,UBOGON,UBOGON,
+ 0x39b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3b35,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3b5d,UBOGON,UBOGON,0x3c29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e1f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e72,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f0b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40a2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43ec,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x44bb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4621,UBOGON,UBOGON,UBOGON,0x461f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8c38,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4791,0x4796,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34bc,UBOGON,UBOGON,0x34d8,UBOGON,
+ UBOGON,0x34f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3785,UBOGON,0x3783,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38ba,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3969,UBOGON,UBOGON,0x3945,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b6c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d04,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e3a,UBOGON,UBOGON,UBOGON,0x3e79,UBOGON,
+ UBOGON,0x7309,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f5d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f8a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4027,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4146,0x4140,UBOGON,0x413f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x419d,0x41cb,UBOGON,0x41e1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x427f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4346,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x441a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,0x44d3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44d0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x458e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x46c3,0x46b6,UBOGON,UBOGON,UBOGON,0x8a2f,UBOGON,
+ 0x46c0,0x46b8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x47d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48ec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x52dc,UBOGON,UBOGON,UBOGON,0x35cc,UBOGON,0x35a2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35b6,UBOGON,UBOGON,0x35c5,0x35c6,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3683,0x5921,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36f8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x379b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5e3f,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x388d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3956,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x395b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3a96,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b7e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3b81,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c35,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c9b,UBOGON,
+ UBOGON,0x3d00,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ed3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f9f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40b1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x414b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,0x7b3f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4389,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4400,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83d0,0x4590,
+ UBOGON,UBOGON,UBOGON,0x45b1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x86e7,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45aa,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x467d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4769,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47a1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47e7,
+ UBOGON,UBOGON,0x47ec,UBOGON,UBOGON,UBOGON,0x47df,UBOGON,UBOGON,UBOGON,
+ 0x4833,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bee,
+ UBOGON,0x4c32,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34fc,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x352a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35d3,UBOGON,UBOGON,UBOGON,0x35d7,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37f3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ 0x3891,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38c0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6117,UBOGON,0x3963,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b02,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d35,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ee0,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3f9a,UBOGON,UBOGON,UBOGON,0x3fa3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4005,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x415e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x41a6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x435d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43b8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4434,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x446f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4594,0x4593,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8714,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x88d1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8ccb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4792,
+ UBOGON,UBOGON,UBOGON,0x47aa,UBOGON,UBOGON,UBOGON,0x47a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x47ef,UBOGON,UBOGON,UBOGON,UBOGON,0x8eed,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4922,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ab5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b75,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3482,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52ec,UBOGON,UBOGON,UBOGON,
+ 0x52e8,UBOGON,UBOGON,UBOGON,UBOGON,0x3535,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,0x35f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x38f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,0x3a3e,UBOGON,0x3a39,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3aa1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b3b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3bb7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3c57,0x3c70,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3ca7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d54,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f31,
+ 0x7527,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fbf,UBOGON,0x3fe4,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x404a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40cf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x798c
+ },
+ { /* ku 28 */
+ UBOGON,0x7991,UBOGON,0x4114,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x420d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4201,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42f1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4476,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x450a,0x4503,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8660,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4635,0x4636,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4773,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b1,UBOGON,0x47af,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47f4,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4999,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ab6,
+ 0x4abd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9904,UBOGON,0x999b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bf4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c35,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4caa,UBOGON,0x4d1f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3550,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x377b,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3809,UBOGON,UBOGON,UBOGON,0x3807,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x39bb,UBOGON,UBOGON,0x3a4b,UBOGON,UBOGON,UBOGON,0x3a4d,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ac2,UBOGON,UBOGON,UBOGON,UBOGON,0x3b1a,UBOGON,UBOGON,
+ 0x3b12,UBOGON,UBOGON,UBOGON,UBOGON,0x3b3c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3bc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bc0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x729f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fe5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x405f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4118,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,0x41ad,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x421e,UBOGON,UBOGON,
+ UBOGON,0x4227,UBOGON,UBOGON,UBOGON,UBOGON,0x4218,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4220,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x42f4,0x4302,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4524,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8770,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x477d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8e37,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4864,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9703,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4ac2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4cac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x349b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52f6,0x3526,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3815,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x389c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x64cc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3d64,0x3d6e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3d5f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ef6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x764a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x4073,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40dd,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ab9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7bd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4688,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x47bb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47ba,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4812,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4861,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48e1
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x999f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b89,UBOGON,0x4b8c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9b8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9e77,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x381a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3998,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3bf1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3f6a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x764b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x76a2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4485,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4239,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x423d,0x4244
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4246,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x430e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4310,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x443e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4484,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4549,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45df,UBOGON,UBOGON,
+ 0x45f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x468a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4702,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47c0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4815,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48e2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b4d,UBOGON,UBOGON,UBOGON,UBOGON,0x4b4a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,0x4d63,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d93,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x350e,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x360a,UBOGON,UBOGON,UBOGON,UBOGON,0x360d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4074,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4256,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x425a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42a3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x453c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x459b,0x459a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x87f1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x465e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46a7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x486e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x490d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4943,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a73,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a74,UBOGON,UBOGON,0x4a70
+ },
+ { /* ku 46 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bdb,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c51,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9bba,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cdb,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d65,UBOGON,
+ UBOGON,0x4d64,0x4d67,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3611,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3733,UBOGON,UBOGON,UBOGON,UBOGON,0x3821,0x3822,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x39a6,UBOGON,UBOGON,UBOGON,UBOGON,0x39a3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ea1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x425f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x425c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45f5,UBOGON,0x8804,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4711,0x470c,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x494e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4b94,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c63,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c61,UBOGON,0x9bd8,UBOGON,
+ 0x4c5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ce3,UBOGON,UBOGON,
+ 0x9d7c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4daf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3613,UBOGON,UBOGON,0x3680,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4db0,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7c46,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,0x4323,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4449,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4959,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a7e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ba8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ba9,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9d8d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d54,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d6d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d8e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 50 */
+ UBOGON,UBOGON,UBOGON,0x3a72,UBOGON,UBOGON,0x3c14,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d93,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x426f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x352e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 51 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4969,0x957e,UBOGON,UBOGON,UBOGON,UBOGON,0x49b4,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a4d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a83,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 52 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4bb0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c0c,UBOGON,0x4c18,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9c20,UBOGON,UBOGON,0x9c22,UBOGON,UBOGON,0x9c1e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cf5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 53 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e0e,UBOGON,UBOGON,UBOGON,UBOGON,0x3fd8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 54 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x482c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x496e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a86,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bb3,UBOGON,UBOGON,UBOGON,0x4beb,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 55 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9c43,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9de0,UBOGON,UBOGON,0x4d42,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d41,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4dac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b2f,0x3b2e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c4d,0x3c7b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 56 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x42b2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4722,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9459,UBOGON,0x4970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 57 */
+ UBOGON,UBOGON,UBOGON,0x4c95,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4d00,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d02,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d9f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 58 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x457d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4af6,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c12,
+ UBOGON,UBOGON,UBOGON,0x4c91,UBOGON,UBOGON,UBOGON,UBOGON,0x4c90,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 59 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a79,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c4e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d1e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9c72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3828,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d91,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4587,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 6 conversion table */
+
+static const unsigned short
+ cns11643_6tab[MAX_CNS11643_KU_6][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,0x3405,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x353f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x382a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38a7,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x38fa
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3400,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x34db,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3438,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6530,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x353a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38c9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c4f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x43cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4492,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3445,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x34b5,UBOGON,0x34b6,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3537,UBOGON,0x3530,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x373e,0x374d,0x3751,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3873,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39be,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c1e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cc8,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3cc3,0x3cc7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f56,UBOGON,0x3540,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x49bd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x351a,UBOGON,
+ UBOGON,0x352c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3549,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x357c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37bf,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x37ba,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38d8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39b2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ac4,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e2b,UBOGON,UBOGON,UBOGON,0x3e61,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x72dd
+ },
+ { /* ku 0d */
+ 0x3eb2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4129,UBOGON,UBOGON,UBOGON,UBOGON,0x4192,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3458,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x351c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3637,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3843,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38b4,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38e0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ae4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6804,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c26,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e6f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x401c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4100,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42b6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x43e4,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44ad,UBOGON,0x82ff,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8fec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48c3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3467,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53de,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3596,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5a30,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3759,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5bb2,
+ UBOGON,0x3776,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x37d2,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3920,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3929,UBOGON,UBOGON,
+ UBOGON,0x3938,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3a89,0x3ab0,UBOGON,UBOGON,UBOGON,0x3abf,UBOGON,UBOGON,UBOGON,0x3ac5,
+ UBOGON,UBOGON,0x3aea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c37,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3ce4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ce8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3e20,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3f86,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4356,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4367,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ 0x8038,UBOGON,UBOGON,0x8081,UBOGON,UBOGON,UBOGON,0x43d1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43eb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45a3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4610,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x491b,UBOGON,0x4987,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3557,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x369d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x375f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3779,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3798,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x37dc,UBOGON,UBOGON,0x37df,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39fd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3af4,UBOGON,0x3aef,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c31,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3d03,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3d09,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e3e,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e77,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e7b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x413a,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41a0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41e5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,0x427d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x436d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43f8,UBOGON,0x8158,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4450,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4468,
+ 0x4467,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44cf,UBOGON,UBOGON,
+ UBOGON,0x44cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45ab,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46c1,UBOGON,UBOGON,0x8a24,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,0x4747,UBOGON,0x4757,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8dc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x484e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3477,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x51f2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x55b6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35c4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3652,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x386b,0x5ec3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3941,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3950,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3ac0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3afc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3479,UBOGON,UBOGON,0x440b
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d1e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e85,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f97,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4093,UBOGON,UBOGON,UBOGON,0x7861,UBOGON,UBOGON,UBOGON,0x40b2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a01,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x41a4,UBOGON,UBOGON,0x41f2,UBOGON,UBOGON,UBOGON,UBOGON,0x41f1,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4281,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x42dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x440c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x446d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44e6,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8849,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8999,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46d1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x472c,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x476e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x47e5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47e6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x489f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x921f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a0d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34f8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5313,UBOGON,0x3533,UBOGON,UBOGON,UBOGON,0x353c,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,0x354f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35dc,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x55e0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37f5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38c3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6139,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3971,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b01,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6ed7,UBOGON,0x3d41,UBOGON,UBOGON,
+ UBOGON,0x3dd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e45,UBOGON,UBOGON,
+ UBOGON,0x733d,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ff2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4049,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41a9,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43cb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x440d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4631,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x46d5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,0x4856,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,0x9775,UBOGON,UBOGON,UBOGON,0x4a58,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3487,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37fa,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x38f2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a2f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ac1,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c40,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d3f,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3d46,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e8f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4168,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41ab,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4291,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7fe4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8088,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4430,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ 0x445d,UBOGON,UBOGON,0x4475,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44ff,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x450b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45c8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4774,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x47ac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a16,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b3b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4bef,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 7 conversion table */
+
+static const unsigned short
+ cns11643_7tab[MAX_CNS11643_KU_7][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5655,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3667,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x617f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3992,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a45,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bc9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3d55,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ee8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fb6,UBOGON,UBOGON,UBOGON,0x3fbd,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,0x40d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4171,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4298,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x45cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46f6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x477a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4801,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x492f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4931,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x499c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x49e6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4b38,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4c3a,UBOGON,UBOGON,0x4cb1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d47,UBOGON,0x4d51,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3747,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3817,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38a3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b1e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x71d7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3e9a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3fc2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40dc,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41b3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8666,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45d9,UBOGON,0x45dd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46fc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8f3a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4907,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x933d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x49a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4aca,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4b8d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4cc0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cca,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d25,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d4a,UBOGON,
+ 0x4d53,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3605,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3bf0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e02,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3e23,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4315,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x455d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x465a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4785,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x490b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4939,UBOGON,UBOGON,UBOGON,
+ 0x4937,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4acd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c4d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3510,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x367b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x381d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x64f5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b27,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3fcf,UBOGON,UBOGON,UBOGON,0x3fcd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4182,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4252,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7f80,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,0x4451,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x455a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,0x4665,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d01,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48af,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4941,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a29,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a2a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a96,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4b12
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x367e,0x58e1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x39a7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4320,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7ce9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x81cb,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4565,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4704,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4764,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4823,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x95d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34b9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,0x3c4b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40f1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4667,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4714,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4889,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c0b,UBOGON,
+ UBOGON,UBOGON,0x4c17,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c6d,UBOGON,0x4c70,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4ced,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4d8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x34a7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6707,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42af,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x456b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x471b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4963,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4a80,0x4a84,0x4a7f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4af1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9a47,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d1c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x34a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x418b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x432b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x457a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4609,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x466d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x471f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x496a,0x496c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bb2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4c1d,UBOGON,UBOGON,UBOGON,0x4c2d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d45,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x460a,UBOGON,UBOGON,UBOGON,UBOGON,0x460c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x482e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,0x4c8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4331,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4bbc,0x4bbb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4c14,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x361c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3772,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d0e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7674,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4bec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4279,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+#if CNS_OBSOLETE
+/* CNS 11643 plane 14 conversion table */
+
+static const unsigned short
+ cns11643_14tab[MAX_CNS11643_KU_14][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e28,0x4e36,0x4e3f,0x4e85,0x4e05,0x4e04,0x5182,0x5196,0x5338,0x5369,
+ 0x53b6,0x4e2a,0x4e87,0x4e49,0x51e2,0x4e46,0x4e8f,0x4ebc,0x4ebe,0x5166,
+ 0x51e3,0x5204,0x529c,UBOGON,0x5902,0x590a,0x5b80,0x5ddb,0x5e7a,0x5e7f,
+ 0x5ef4,0x5f50,0x5f51,0x5f61,0x961d,UBOGON,0x4e63,0x4e62,0x4ea3,0x5185,
+ 0x4ec5,0x4ecf,0x4ece,0x4ecc,0x5184,0x5186,UBOGON,UBOGON,0x51e4,0x5205,
+ 0x529e,0x529d,0x52fd,0x5300,0x533a,UBOGON,0x5346,0x535d,0x5386,0x53b7,
+ UBOGON,0x53cc,UBOGON,0x53ce,0x5721,UBOGON,0x5e00,0x5f0c,0x6237,0x6238,
+ 0x6534,0x6535,0x65e0,UBOGON,0x738d,0x4e97,0x4ee0,UBOGON,UBOGON,0x4ee7,
+ UBOGON,0x4ee6,UBOGON,UBOGON,UBOGON,UBOGON,0x56d8,0x518b,0x518c,0x5199,
+ 0x51e5,UBOGON,0x520b,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,0x5304,0x5303,0x5307,UBOGON,0x531e,0x535f,0x536d,0x5389,0x53ba,
+ 0x53d0,UBOGON,0x53f6,0x53f7,0x53f9,UBOGON,0x53f4,UBOGON,UBOGON,0x5724,
+ 0x5904,0x5918,0x5932,0x5930,0x5934,UBOGON,0x5975,UBOGON,0x5b82,0x5bf9,
+ 0x5c14,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5e81,0x5e83,
+ 0x5f0d,0x5f52,UBOGON,0x5fca,0x5fc7,0x6239,UBOGON,0x624f,0x65e7,0x672f,
+ 0x6b7a,0x6c39,UBOGON,UBOGON,0x6c37,0x6c44,0x6c45,0x738c,0x7592,0x7676,
+ 0x9093,0x9092,UBOGON,UBOGON,0x4e21,0x4e20,0x4e22,0x4e68,0x4e89,0x4e98,
+ 0x4ef9,0x4eef,UBOGON,UBOGON,0x4ef8,0x4f06,0x4f03,0x4efc,0x4eee,0x4f16,
+ UBOGON,0x4f28,0x4f1c,0x4f07,0x4f1a,0x4efa,0x4f17,0x514a,UBOGON,0x5172,
+ UBOGON,0x51b4,0x51b3,0x51b2
+ },
+ { /* ku 03 */
+ UBOGON,0x51e8,UBOGON,0x5214,0x520f,0x5215,0x5218,0x52a8,UBOGON,0x534b,
+ 0x534f,UBOGON,0x5350,UBOGON,0x538b,UBOGON,0x53be,UBOGON,0x53d2,0x5416,
+ 0x53ff,UBOGON,0x5400,UBOGON,0x5405,0x5413,0x5415,UBOGON,UBOGON,0x56e3,
+ 0x5735,0x5736,0x5731,0x5732,0x58ee,0x5905,0x4e54,UBOGON,0x5936,UBOGON,
+ UBOGON,UBOGON,0x597a,UBOGON,0x5986,UBOGON,UBOGON,0x5b86,0x5f53,0x5c18,
+ UBOGON,0x5c3d,0x5c78,UBOGON,UBOGON,UBOGON,UBOGON,0x5c80,UBOGON,0x5e08,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5ef5,0x5f0e,UBOGON,UBOGON,UBOGON,0x5fd3,
+ 0x5fda,UBOGON,0x5fdb,UBOGON,0x620f,0x625d,0x625f,0x6267,0x6257,0x9f50,
+ UBOGON,0x65eb,0x65ea,UBOGON,0x6737,UBOGON,0x6732,0x6736,0x6b22,0x6bce,
+ UBOGON,0x6c58,0x6c51,0x6c77
+ },
+ { /* ku 04 */
+ 0x6c3c,UBOGON,0x6c5a,UBOGON,0x6c53,0x706f,0x7072,0x706e,UBOGON,UBOGON,
+ 0x7073,0x72b1,0x72b2,UBOGON,0x738f,UBOGON,UBOGON,UBOGON,0x793c,UBOGON,
+ 0x808d,0x808e,UBOGON,0x827b,UBOGON,0x8d71,0x8fb9,0x9096,0x909a,UBOGON,
+ 0x4e24,0x4e71,UBOGON,0x4e9c,0x4f45,0x4f4a,0x4f39,0x4f37,UBOGON,0x4f32,
+ 0x4f42,UBOGON,0x4f44,0x4f4b,UBOGON,0x4f40,0x4f35,0x4f31,0x5151,UBOGON,
+ 0x5150,0x514e,UBOGON,UBOGON,0x519d,UBOGON,0x51b5,0x51b8,0x51ec,0x5223,
+ 0x5227,0x5226,0x521f,0x522b,0x5220,0x52b4,0x52b3,UBOGON,0x5325,0x533b,
+ 0x5374,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x544d,UBOGON,UBOGON,0x543a,
+ UBOGON,UBOGON,0x5444,0x544c,0x5423,0x541a,0x5432,0x544b,0x5421,UBOGON,
+ 0x5434,0x5449,0x5450,0x5422
+ },
+ { /* ku 05 */
+ 0x543f,0x5451,0x545a,0x542f,UBOGON,0x56e9,0x56f2,0x56f3,0x56ef,0x56ed,
+ 0x56ec,0x56e6,0x5748,UBOGON,0x5744,0x573f,0x573c,0x5753,0x5756,UBOGON,
+ 0x575f,0x5743,0x5758,0x5757,UBOGON,UBOGON,UBOGON,0x5746,UBOGON,0x573d,
+ UBOGON,0x5742,0x5754,0x5755,0x58f1,0x58f2,0x58f0,0x590b,0x9ea6,0x56f1,
+ 0x593d,UBOGON,0x5994,0x598c,UBOGON,0x599c,UBOGON,UBOGON,0x599f,UBOGON,
+ 0x599b,UBOGON,0x5989,0x599a,UBOGON,0x6588,UBOGON,0x5b8d,UBOGON,0x5bfe,
+ 0x5bff,0x5bfd,0x5c2b,UBOGON,0x5c84,0x5c8e,0x5c9c,UBOGON,UBOGON,0x5c85,
+ 0x5df5,0x5e09,UBOGON,UBOGON,0x5e0b,UBOGON,0x5e92,0x5e90,0x5f03,UBOGON,
+ 0x5f1e,0x5f63,UBOGON,0x5fe7,0x5ffe,0x5fe6,0x5fdc,0x5fce,UBOGON,0x5ffc,
+ 0x5fdf,0x5fec,0x5ff6,UBOGON
+ },
+ { /* ku 06 */
+ 0x5ff2,0x5ff0,0x5ff9,UBOGON,0x6213,UBOGON,UBOGON,0x623b,0x623c,0x6282,
+ UBOGON,UBOGON,UBOGON,0x6278,0x628b,UBOGON,0x629e,0x62a5,0x629b,0x629c,
+ 0x6299,0x628d,0x6285,0x629d,0x6275,UBOGON,UBOGON,UBOGON,0x65f6,UBOGON,
+ UBOGON,UBOGON,0x66f5,0x675b,UBOGON,0x6754,0x6752,UBOGON,0x6758,0x6744,
+ 0x674a,0x6761,UBOGON,0x6c7f,0x6c91,0x6c9e,UBOGON,0x6c6e,0x6c7c,0x6c9f,
+ 0x6c75,UBOGON,0x6c56,0x6ca2,0x6c79,UBOGON,0x6ca1,UBOGON,0x6caa,0x6ca0,
+ UBOGON,0x7079,0x7077,0x707e,UBOGON,0x7075,0x707b,0x7264,UBOGON,0x72bb,
+ 0x72bc,0x72c7,0x72b9,0x72be,0x72b6,UBOGON,UBOGON,0x7398,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7593,0x7680,UBOGON,0x7683,0x76c0,0x76c1,UBOGON,UBOGON,
+ 0x77f4,0x77f5,UBOGON,0x7acc
+ },
+ { /* ku 07 */
+ 0x7acd,0x7cfa,0x809f,0x8091,0x8097,0x8094,UBOGON,0x8286,0x828c,UBOGON,
+ 0x8295,UBOGON,0x866c,UBOGON,0x8fb5,0x8fbe,0x8fc7,UBOGON,0x8fc1,0x90a9,
+ 0x90a4,UBOGON,UBOGON,UBOGON,0x90a8,0x9627,0x9626,0x962b,0x9633,0x9634,
+ 0x9629,0x4e3d,UBOGON,0x4e9d,0x4f93,0x4f8a,UBOGON,UBOGON,0x4f6d,0x4f8e,
+ 0x4fa0,0x4fa2,0x4fa1,0x4f9f,0x4fa3,UBOGON,0x4f72,UBOGON,0x4f8c,0x5156,
+ UBOGON,UBOGON,0x5190,UBOGON,UBOGON,UBOGON,0x51ed,0x51fe,0x522f,UBOGON,
+ 0x523c,0x5234,0x5239,0x52b9,0x52b5,0x52bf,0x5355,UBOGON,0x5376,0x537a,
+ 0x5393,UBOGON,0x53c1,0x53c2,0x53d5,0x5485,UBOGON,0x545f,0x5493,0x5489,
+ 0x5479,0x9efe,0x548f,0x5469,0x546d,UBOGON,0x5494,0x546a,0x548a,UBOGON,
+ 0x56fd,0x56fb,0x56f8,UBOGON
+ },
+ { /* ku 08 */
+ 0x56fc,0x56f6,0x5765,0x5781,0x5763,0x5767,UBOGON,0x576e,0x5778,0x577f,
+ UBOGON,UBOGON,0x58f3,0x594b,0x594c,UBOGON,UBOGON,UBOGON,0x59ad,UBOGON,
+ 0x59c4,UBOGON,0x59c2,0x59b0,UBOGON,UBOGON,UBOGON,UBOGON,0x59bf,UBOGON,
+ 0x59c9,0x59b8,0x59ac,UBOGON,UBOGON,UBOGON,0x59b7,0x59d7,UBOGON,0x5b60,
+ UBOGON,0x5b96,0x5b9e,0x5b94,0x5b9f,0x5b9d,UBOGON,0x5c00,0x5c19,UBOGON,
+ UBOGON,0x5c49,0x5c4a,UBOGON,0x5cbb,0x5cc1,UBOGON,UBOGON,UBOGON,0x5cb9,
+ 0x5c9e,0x5cb4,0x5cba,0x5df6,0x5e13,0x5e12,0x5e77,UBOGON,0x5e98,UBOGON,
+ 0x5e99,0x5e9d,0x5ef8,UBOGON,0x5ef9,UBOGON,0x5f06,0x5f21,UBOGON,0x5f25,
+ 0x5f55,UBOGON,UBOGON,UBOGON,0x5f84,0x5f83,0x6030,0x6007,UBOGON,0x6036,
+ UBOGON,UBOGON,UBOGON,0x5fe9
+ },
+ { /* ku 09 */
+ 0x603d,0x6008,UBOGON,UBOGON,0x62ba,0x62b2,UBOGON,0x62b7,0x62e4,0x62a7,
+ UBOGON,UBOGON,UBOGON,0x62d5,0x62e1,0x62dd,0x62a6,0x62c1,0x62c5,0x62c0,
+ 0x62df,0x62e0,0x62de,UBOGON,0x6589,UBOGON,0x65a6,0x65ba,UBOGON,0x65ff,
+ UBOGON,0x6617,0x6618,0x6601,0x65fe,UBOGON,0x670c,UBOGON,0x676b,0x6796,
+ 0x6782,0x678a,UBOGON,0x67a3,UBOGON,0x67a2,0x678f,UBOGON,0x67f9,0x6780,
+ 0x6b26,0x6b27,0x6b68,0x6b69,UBOGON,0x6b81,0x6bb4,0x6bd1,UBOGON,UBOGON,
+ 0x6c1c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c97,0x6c6c,0x6cdf,UBOGON,
+ 0x6cea,UBOGON,0x6ce4,0x6cd8,0x6cb2,0x6cce,0x6cc8,UBOGON,0x708b,0x7088,
+ 0x7090,0x708f,UBOGON,0x7087,0x7089,0x708d,0x7081,UBOGON,0x708c,UBOGON,
+ UBOGON,0x7240,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ 0x7265,0x7266,0x7268,UBOGON,UBOGON,0x72cd,0x72d3,0x72db,UBOGON,0x72cf,
+ 0x73a7,0x73a3,0x739e,UBOGON,0x73af,UBOGON,UBOGON,0x73aa,0x739c,UBOGON,
+ 0x7542,0x7544,0x753b,0x7541,UBOGON,0x759b,0x759e,UBOGON,0x79c4,0x79c3,
+ 0x79c6,UBOGON,UBOGON,0x79c7,UBOGON,0x79ca,UBOGON,UBOGON,0x7acf,0x7c76,
+ 0x7c74,0x7cff,0x7cfc,UBOGON,UBOGON,0x7f59,0x80a8,UBOGON,UBOGON,0x80b0,
+ UBOGON,0x80b3,UBOGON,0x80a4,0x80b6,0x80a7,0x80ac,UBOGON,0x80a6,0x5367,
+ 0x820e,0x82c4,0x833e,0x829c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82aa,
+ UBOGON,0x82c9,UBOGON,UBOGON,0x82a6,0x82b2,UBOGON,UBOGON,UBOGON,0x8fcc,
+ 0x8fd9,0x8fca,0x8fd8,0x8fcf,0x90b7,UBOGON,0x90ad,0x90b9,0x9637,UBOGON,
+ 0x9641,0x963e,0x96b6,0x9751
+ },
+ { /* ku 0b */
+ 0x9763,0x4e57,0x4e79,0x4eb2,0x4eb0,0x4eaf,0x4eb1,0x4fd2,0x4fd5,UBOGON,
+ 0x4fbe,0x4fb8,0x4fb0,0x4fb1,0x4fc8,UBOGON,UBOGON,0x4fc6,0x4fcc,0x4fe5,
+ 0x4fe3,0x4fb4,0x516a,UBOGON,0x519f,UBOGON,0x51c1,UBOGON,0x51c2,0x51c3,
+ 0x5245,0x5248,UBOGON,UBOGON,0x524f,UBOGON,UBOGON,0x52c5,0x52ca,0x52c4,
+ 0x5327,0x5358,0x537d,UBOGON,0x53dd,0x53dc,0x53da,0x53d9,0x54b9,UBOGON,
+ 0x54d0,0x54b4,0x54ca,UBOGON,0x54a3,0x54da,0x54a4,UBOGON,0x54b2,0x549e,
+ 0x549f,0x54b5,UBOGON,UBOGON,0x54cd,UBOGON,0x54cc,UBOGON,0x5700,0x57ac,
+ 0x5791,0x578e,0x578d,0x5792,0x57a1,0x5790,0x57a6,0x57a8,UBOGON,0x579c,
+ 0x5796,0x57a7,UBOGON,UBOGON,UBOGON,UBOGON,0x58f5,UBOGON,0x5909,0x5908,
+ UBOGON,0x5952,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ 0x59df,UBOGON,0x59eb,0x59ef,0x59f0,0x59d5,0x5a0d,0x5a04,0x59f9,0x5a02,
+ 0x59f8,0x59e2,0x59d9,0x59e7,0x5b6a,UBOGON,UBOGON,0x5bab,UBOGON,0x5c1b,
+ 0x5c2f,UBOGON,0x663c,UBOGON,UBOGON,UBOGON,0x5cd1,0x5cdc,0x5ce6,0x5ce1,
+ 0x5ccd,UBOGON,0x5ce2,0x5cdd,0x5ce5,0x5dfb,0x5dfa,0x5e1e,UBOGON,0x5ea1,
+ UBOGON,UBOGON,0x5efc,0x5efb,0x5f2f,UBOGON,UBOGON,0x5f66,UBOGON,UBOGON,
+ UBOGON,0x605c,UBOGON,0x604e,0x6051,UBOGON,UBOGON,0x6023,0x6031,0x607c,
+ 0x6052,UBOGON,0x6060,0x604a,0x6061,UBOGON,0x6218,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x631f,0x6317,0x62ea,0x6321,0x6304,0x6305,
+ UBOGON,0x6531,0x6544,0x6540,UBOGON,0x6542,0x65be,UBOGON,0x6629,0x661b,
+ UBOGON,0x6623,0x662c,0x661a
+ },
+ { /* ku 0d */
+ 0x6630,0x663b,0x661e,0x6637,0x6638,UBOGON,0x670e,UBOGON,UBOGON,0x67e8,
+ 0x67d6,UBOGON,0x67c7,0x67bc,0x6852,0x67bf,0x67d5,0x67fe,0x8363,0x67fb,
+ UBOGON,0x67b1,0x6801,0x6805,0x6800,0x67d7,UBOGON,0x6b2a,0x6b6b,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6be1,UBOGON,UBOGON,0x6d23,0x6cff,0x6d14,0x6d05,
+ 0x6d13,0x6d06,0x6d21,UBOGON,0x6d15,0x6caf,0x6cf4,0x6d02,0x6d45,UBOGON,
+ 0x6d26,UBOGON,0x6d44,UBOGON,0x6d24,0x70a5,UBOGON,0x70a3,UBOGON,0x70a2,
+ 0x70bb,0x70a0,0x70aa,UBOGON,UBOGON,0x70a8,0x70b6,0x70b2,0x70a7,UBOGON,
+ UBOGON,0x70b9,0x722e,UBOGON,0x723c,UBOGON,0x726d,UBOGON,UBOGON,0x72e7,
+ 0x72ed,UBOGON,0x72ec,0x72e5,0x72e2,UBOGON,0x73c4,0x73bd,0x73cf,0x73c9,
+ 0x73c1,0x73d0,UBOGON,0x73ce
+ },
+ { /* ku 0e */
+ 0x74ed,0x74eb,UBOGON,0x74ef,0x7549,0x7550,0x7546,0x754a,UBOGON,0x754d,
+ 0x75a6,UBOGON,UBOGON,UBOGON,0x75a8,UBOGON,UBOGON,0x76c7,0x76ff,UBOGON,
+ 0x76fd,0x77e6,0x780a,UBOGON,0x7804,0x780b,0x7807,UBOGON,0x7815,0x7808,
+ UBOGON,0x79d3,0x79d4,0x79d0,0x79d7,0x7a7c,UBOGON,UBOGON,0x7a7d,0x7a83,
+ 0x7a82,UBOGON,0x7ad4,0x7ad5,0x7ad3,0x7ad0,0x7ad2,0x7afe,0x7afc,0x7c77,
+ 0x7c7c,0x7c7b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7f8f,0x80d3,UBOGON,0x80cb,0x80d2,UBOGON,0x8109,0x80e2,0x80df,0x80c6,
+ UBOGON,0x8224,0x82f7,0x82d8,0x82dd,UBOGON,UBOGON,0x82f8,0x82fc,UBOGON,
+ UBOGON,0x82e9,UBOGON,0x82ee,UBOGON,0x82d0,0x830e,0x82e2,0x830b,0x82fd,
+ 0x5179,0x8676,UBOGON,0x8678
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,0x8675,0x867d,UBOGON,0x8842,0x8866,UBOGON,0x898c,0x8a05,
+ UBOGON,0x8a06,UBOGON,0x8c9f,UBOGON,0x8ff1,0x8fe7,0x8fe9,0x8fef,0x90c2,
+ 0x90bc,UBOGON,0x90c6,0x90c0,UBOGON,UBOGON,0x90cd,0x90c9,UBOGON,0x90c4,
+ UBOGON,0x9581,UBOGON,0x9cec,0x5032,0x4ff9,0x501d,0x4fff,0x5004,0x4ff0,
+ 0x5003,UBOGON,0x5002,0x4ffc,0x4ff2,0x5024,0x5008,0x5036,0x502e,UBOGON,
+ 0x5010,0x5038,0x5039,0x4ffd,0x5056,0x4ffb,0x51a3,0x51a6,0x51a1,UBOGON,
+ UBOGON,0x51c7,0x51c9,0x5260,0x5264,0x5259,0x5265,0x5267,0x5257,0x5263,
+ UBOGON,0x5253,UBOGON,0x52cf,UBOGON,0x52ce,0x52d0,0x52d1,0x52cc,UBOGON,
+ UBOGON,UBOGON,0x550d,0x54f4,UBOGON,0x5513,0x54ef,0x54f5,0x54f9,0x5502,
+ 0x5500,UBOGON,UBOGON,0x5518
+ },
+ { /* ku 10 */
+ 0x54f0,0x54f6,UBOGON,UBOGON,0x5519,UBOGON,0x5705,0x57c9,UBOGON,0x57b7,
+ 0x57cd,UBOGON,UBOGON,UBOGON,0x57be,0x57bb,UBOGON,0x57db,0x57c8,0x57c4,
+ 0x57c5,0x57d1,0x57ca,0x57c0,UBOGON,UBOGON,0x5a21,0x5a2a,UBOGON,0x5a1d,
+ UBOGON,0x5a0b,UBOGON,UBOGON,UBOGON,UBOGON,0x5a22,UBOGON,UBOGON,0x5a24,
+ UBOGON,0x5a14,0x5a31,UBOGON,0x5a2f,0x5a1a,0x5a12,UBOGON,UBOGON,0x5a26,
+ UBOGON,UBOGON,0x5bbc,0x5bbb,0x5bb7,0x5c05,0x5c06,0x5c52,0x5c53,UBOGON,
+ UBOGON,0x5cfa,0x5ceb,UBOGON,0x5cf3,0x5cf5,0x5ce9,0x5cef,UBOGON,0x5e2a,
+ 0x5e30,0x5e2e,0x5e2c,0x5e2f,0x5eaf,0x5ea9,UBOGON,0x5efd,0x5f32,0x5f8e,
+ 0x5f93,0x5f8f,0x604f,0x6099,UBOGON,0x607e,UBOGON,0x6074,0x604b,0x6073,
+ 0x6075,UBOGON,UBOGON,0x6056
+ },
+ { /* ku 11 */
+ 0x60a9,0x608b,0x60a6,UBOGON,0x6093,0x60ae,0x609e,0x60a7,0x6245,UBOGON,
+ UBOGON,0x632e,UBOGON,0x6352,0x6330,0x635b,UBOGON,0x6319,0x631b,UBOGON,
+ 0x6331,0x635d,0x6337,0x6335,0x6353,UBOGON,0x635c,0x633f,0x654b,UBOGON,
+ UBOGON,0x658b,UBOGON,0x659a,0x6650,0x6646,0x664e,0x6640,UBOGON,0x664b,
+ 0x6648,UBOGON,0x6660,0x6644,0x664d,UBOGON,0x6837,0x6824,UBOGON,UBOGON,
+ 0x681b,0x6836,UBOGON,0x682c,0x6819,0x6856,0x6847,0x683e,0x681e,UBOGON,
+ 0x6815,0x6822,0x6827,0x6859,0x6858,0x6855,0x6830,0x6823,0x6b2e,0x6b2b,
+ 0x6b30,0x6b6c,UBOGON,0x6b8b,UBOGON,0x6be9,0x6bea,0x6be5,0x6d6b,UBOGON,
+ UBOGON,0x6d73,0x6d57,UBOGON,UBOGON,0x6d5d,0x6d56,0x6d8f,0x6d5b,0x6d1c,
+ 0x6d9a,0x6d9b,0x6d99,UBOGON
+ },
+ { /* ku 12 */
+ 0x6d81,0x6d71,UBOGON,UBOGON,0x6d72,0x6d5c,0x6d96,0x70c4,0x70db,0x70cc,
+ 0x70d0,0x70e3,0x70df,UBOGON,0x70d6,0x70ee,0x70d5,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x727a,UBOGON,0x72f5,0x7302,UBOGON,UBOGON,0x73e2,0x73ec,0x73d5,
+ 0x73f9,0x73df,0x73e6,UBOGON,UBOGON,UBOGON,UBOGON,0x73e4,0x73e1,0x74f3,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7556,0x7555,0x7558,0x7557,0x755e,0x75c3,
+ UBOGON,UBOGON,0x75b4,UBOGON,0x75b1,UBOGON,UBOGON,0x76cb,0x76cc,0x772a,
+ UBOGON,0x7716,0x770f,UBOGON,UBOGON,0x773f,0x772b,0x770e,0x7724,UBOGON,
+ 0x7721,0x7718,0x77dd,UBOGON,UBOGON,0x7824,0x7836,UBOGON,0x7958,0x7959,
+ UBOGON,0x7962,0x79da,0x79d9,UBOGON,0x79e1,0x79e5,0x79e8,0x79db,UBOGON,
+ 0x79e2,0x79f0,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,0x7ada,0x7add,UBOGON,0x7adb,0x7adc,UBOGON,UBOGON,0x7b0d,
+ 0x7b0b,0x7b14,0x7c8e,0x7c86,UBOGON,0x7c87,0x7c83,0x7c8b,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7d24,UBOGON,UBOGON,UBOGON,0x7d25,0x7f62,0x7f93,0x7f99,
+ 0x7f97,UBOGON,UBOGON,0x7fc4,0x7fc6,0x800a,UBOGON,UBOGON,0x8040,0x803c,
+ 0x803b,0x80f6,0x80ff,0x80ee,0x8104,0x8103,0x8107,UBOGON,UBOGON,0x80f7,
+ UBOGON,UBOGON,0x822d,UBOGON,0x8227,0x8229,0x831f,0x8357,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8321,UBOGON,UBOGON,0x8318,0x8358,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8684,0x869f,0x869b,0x8689,0x86a6,0x8692,0x868f,0x86a0,
+ 0x884f,0x8878,0x887a,0x886e,0x887b,0x8884,0x8873,UBOGON,UBOGON,0x8a0d,
+ 0x8a0b,0x8a19,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8ff9,0x9009,0x9008,UBOGON,0x90de,0x9151,
+ UBOGON,UBOGON,0x91db,0x91df,0x91de,0x91d6,0x91e0,0x9585,0x9660,0x9659,
+ UBOGON,0x9656,UBOGON,UBOGON,0x96bd,UBOGON,UBOGON,0x5042,0x5059,UBOGON,
+ 0x5044,0x5066,0x5052,0x5054,0x5071,0x5050,0x507b,0x507c,0x5058,UBOGON,
+ UBOGON,0x5079,0x506c,0x5078,0x51a8,0x51d1,0x51cf,0x5268,0x5276,0x52d4,
+ UBOGON,0x53a0,0x53c4,UBOGON,0x5558,0x554c,0x5568,UBOGON,0x5549,UBOGON,
+ UBOGON,0x555d,0x5529,UBOGON,0x5554,0x5553,UBOGON,0x555a,UBOGON,0x553a,
+ 0x553f,0x552b,0x57ea,UBOGON,0x57ef,UBOGON,UBOGON,0x57dd,0x57fe,UBOGON,
+ 0x57de,0x57e6,UBOGON,0x57e8,0x57ff,0x5803,0x58f7,0x68a6,0x591f,UBOGON,
+ 0x595b,0x595d,0x595e,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,0x5a2b,UBOGON,0x5a3b,UBOGON,UBOGON,0x5a61,0x5a3a,0x5a6e,0x5a4b,
+ 0x5a6b,UBOGON,UBOGON,0x5a45,0x5a4e,0x5a68,0x5a3d,0x5a71,0x5a3f,0x5a6f,
+ 0x5a75,UBOGON,0x5a73,0x5a2c,0x5a59,0x5a54,0x5a4f,0x5a63,UBOGON,UBOGON,
+ 0x5bc8,UBOGON,0x5bc3,UBOGON,0x5c5b,0x5c61,UBOGON,0x5d21,0x5d0a,0x5d09,
+ UBOGON,0x5d2c,0x5d08,UBOGON,UBOGON,0x5d2a,0x5d15,UBOGON,0x5d10,0x5d13,
+ UBOGON,0x5d2f,0x5d18,UBOGON,0x5de3,0x5e39,0x5e35,0x5e3a,0x5e32,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5ebb,0x5eba,0x5f34,0x5f39,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6098,UBOGON,0x60d0,UBOGON,UBOGON,UBOGON,0x60d7,0x60aa,UBOGON,
+ 0x60a1,0x60a4,UBOGON,0x60ee,UBOGON,0x60e7,UBOGON,UBOGON,0x60de,UBOGON,
+ UBOGON,0x637e,0x638b,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,0x6379,0x6386,0x6393,UBOGON,0x6373,0x636a,UBOGON,0x636c,UBOGON,
+ 0x637f,UBOGON,0x63b2,0x63ba,UBOGON,UBOGON,0x6366,0x6374,UBOGON,0x655a,
+ UBOGON,0x654e,0x654d,0x658d,0x658e,0x65ad,UBOGON,0x65c7,0x65ca,UBOGON,
+ 0x65c9,UBOGON,0x65e3,0x6657,UBOGON,0x6663,0x6667,0x671a,0x6719,0x6716,
+ UBOGON,UBOGON,0x689e,0x68b6,0x6898,0x6873,UBOGON,0x689a,0x688e,0x68b7,
+ 0x68db,0x68a5,0x686c,0x68c1,0x6884,UBOGON,UBOGON,0x6895,0x687a,0x6899,
+ UBOGON,0x68b8,0x68b9,0x6870,UBOGON,0x6b35,UBOGON,0x6b90,0x6bbb,0x6bed,
+ UBOGON,UBOGON,UBOGON,0x6dc1,0x6dc3,0x6dce,UBOGON,UBOGON,0x6dad,0x6e04,
+ UBOGON,0x6db9,UBOGON,0x6de7,UBOGON,0x6e08,0x6e06,UBOGON,0x6e0a,0x6db0,
+ UBOGON,0x6df8,0x6e0c,UBOGON
+ },
+ { /* ku 17 */
+ 0x6db1,UBOGON,0x6e02,0x6e07,0x6e09,0x6e01,0x6e17,0x6dff,0x6e12,UBOGON,
+ UBOGON,0x7103,0x7107,0x7101,0x70f5,0x70f1,0x7108,0x70f2,0x710f,UBOGON,
+ 0x70fe,UBOGON,UBOGON,UBOGON,0x731a,0x7310,0x730e,0x7402,0x73f3,UBOGON,
+ UBOGON,0x73fb,UBOGON,UBOGON,UBOGON,0x751b,0x7523,0x7561,0x7568,UBOGON,
+ 0x7567,0x75d3,UBOGON,UBOGON,0x7690,UBOGON,UBOGON,0x76d5,0x76d7,0x76d6,
+ 0x7730,UBOGON,0x7726,UBOGON,0x7740,UBOGON,0x771e,UBOGON,UBOGON,UBOGON,
+ 0x7847,UBOGON,0x784b,0x7851,0x784f,0x7842,0x7846,UBOGON,0x796e,0x796c,
+ 0x79f2,UBOGON,0x79f1,0x79f5,0x79f3,0x79f9,UBOGON,UBOGON,UBOGON,0x7a9a,
+ 0x7a93,0x7a91,0x7ae1,UBOGON,UBOGON,0x7b21,0x7b1c,0x7b16,0x7b17,0x7b36,
+ 0x7b1f,UBOGON,0x7c93,0x7c99
+ },
+ { /* ku 18 */
+ 0x7c9a,0x7c9c,UBOGON,0x7d49,UBOGON,0x7d34,0x7d37,UBOGON,0x7d2d,UBOGON,
+ 0x7d4c,UBOGON,UBOGON,0x7d48,UBOGON,UBOGON,0x7f3b,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8008,0x801a,UBOGON,0x801d,UBOGON,0x8049,0x8045,0x8044,0x7c9b,
+ UBOGON,UBOGON,0x812a,0x812e,UBOGON,UBOGON,0x8131,UBOGON,0x811a,0x8134,
+ 0x8117,UBOGON,UBOGON,UBOGON,0x831d,0x8371,0x8384,0x8380,0x8372,0x83a1,
+ UBOGON,0x8379,0x8391,UBOGON,0x839f,0x83ad,UBOGON,UBOGON,0x8323,UBOGON,
+ 0x8385,0x839c,0x83b7,0x8658,0x865a,UBOGON,0x8657,0x86b2,UBOGON,0x86ae,
+ UBOGON,UBOGON,UBOGON,0x8845,0x889c,0x8894,0x88a3,0x888f,0x88a5,0x88a9,
+ 0x88a6,0x888a,0x88a0,0x8890,0x8992,0x8991,0x8994,UBOGON,0x8a26,0x8a32,
+ 0x8a28,UBOGON,UBOGON,0x8a1c
+ },
+ { /* ku 19 */
+ UBOGON,0x8a2b,0x8a20,UBOGON,0x8a29,UBOGON,UBOGON,UBOGON,0x8a21,0x8c3a,
+ UBOGON,0x8c5b,0x8c58,0x8c7c,UBOGON,0x8ca6,0x8cae,0x8cad,0x8d65,UBOGON,
+ 0x8d7e,UBOGON,0x8d7c,0x8d7f,0x8d7a,0x8dbd,UBOGON,UBOGON,0x8dc0,0x8dbb,
+ 0x8ead,0x8eaf,0x8ed6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8ed9,UBOGON,
+ UBOGON,0x9012,0x900e,0x9025,UBOGON,0x9013,0x90ee,UBOGON,0x90ab,0x90f7,
+ UBOGON,0x9159,0x9154,0x91f2,0x91f0,0x91e5,0x91f6,UBOGON,UBOGON,0x9587,
+ UBOGON,0x965a,UBOGON,UBOGON,0x966e,UBOGON,UBOGON,UBOGON,0x9679,UBOGON,
+ 0x98e1,0x98e6,UBOGON,0x9ec4,0x9ed2,0x4e80,UBOGON,0x4e81,0x508f,0x5097,
+ 0x5088,0x5089,UBOGON,UBOGON,0x5081,0x5160,UBOGON,UBOGON,0x5e42,0x51d3,
+ UBOGON,UBOGON,0x51d2,0x51d6
+ },
+ { /* ku 1a */
+ 0x5273,UBOGON,0x5270,UBOGON,UBOGON,UBOGON,0x53a8,0x53a6,0x53c5,0x5597,
+ 0x55de,UBOGON,UBOGON,0x5596,0x55b4,UBOGON,0x5585,UBOGON,0x559b,0x55a0,
+ UBOGON,0x5559,UBOGON,0x5586,UBOGON,UBOGON,0x55af,0x557a,UBOGON,UBOGON,
+ UBOGON,0x559e,UBOGON,0x55a9,0x570f,0x570e,0x581a,UBOGON,0x581f,UBOGON,
+ 0x583c,0x5818,0x583e,0x5826,UBOGON,0x583a,UBOGON,0x5822,UBOGON,0x58fb,
+ 0x5963,0x5964,UBOGON,0x5aa8,0x5aa3,0x5a82,0x5a88,0x5aa1,0x5a85,0x5a98,
+ UBOGON,0x5a99,UBOGON,0x5a89,0x5a81,0x5a96,0x5a80,UBOGON,UBOGON,0x5a91,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5acf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5a87,0x5aa0,UBOGON,0x5a79,UBOGON,0x5a86,0x5aab,0x5aaa,0x5aa4,
+ 0x5a8d,0x5a7e,UBOGON,0x5bd5
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,0x5c1e,0x5c5f,0x5c5e,0x5d44,0x5d3e,UBOGON,0x5d48,
+ 0x5d1c,UBOGON,0x5d5b,0x5d4d,UBOGON,UBOGON,0x5d57,UBOGON,0x5d53,0x5d4f,
+ UBOGON,0x5d3b,0x5d46,UBOGON,UBOGON,0x5e46,0x5e47,UBOGON,0x5e48,0x5ec0,
+ 0x5ebd,0x5ebf,UBOGON,0x5f11,UBOGON,0x5f3e,0x5f3b,UBOGON,0x5f3a,UBOGON,
+ UBOGON,UBOGON,0x5fa7,UBOGON,0x60ea,UBOGON,0x6107,0x6122,0x610c,UBOGON,
+ UBOGON,0x60b3,0x60d6,0x60d2,UBOGON,0x60e3,0x60e5,0x60e9,UBOGON,UBOGON,
+ 0x6111,0x60fd,UBOGON,UBOGON,0x611e,0x6120,0x6121,0x621e,UBOGON,0x63e2,
+ 0x63de,0x63e6,UBOGON,UBOGON,UBOGON,UBOGON,0x63f8,UBOGON,0x63fe,0x63c1,
+ 0x63bf,0x63f7,0x63d1,0x655f,0x6560,0x6561,UBOGON,UBOGON,0x65d1,UBOGON,
+ UBOGON,0x667d,0x666b,0x667f
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,0x6673,0x6681,0x666d,0x6669,UBOGON,UBOGON,0x671e,0x68ed,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6903,UBOGON,0x68fe,0x68e5,0x691e,0x6902,
+ UBOGON,UBOGON,0x6909,0x68ca,0x6900,UBOGON,0x6901,0x6918,0x68e2,0x68cf,
+ UBOGON,0x692e,0x68c5,0x68ff,UBOGON,0x691c,0x68c3,UBOGON,0x6b6f,UBOGON,
+ 0x6b6e,UBOGON,0x6bbe,UBOGON,0x6bf4,0x6c2d,UBOGON,0x6db6,0x6e75,0x6e1e,
+ UBOGON,0x6e18,UBOGON,0x6e48,UBOGON,0x6e4f,UBOGON,0x6e42,0x6e6a,0x6e70,
+ 0x6dfe,UBOGON,UBOGON,0x6e6d,UBOGON,0x6e7b,0x6e7e,0x6e59,UBOGON,0x6e57,
+ UBOGON,0x6e80,0x6e50,UBOGON,0x6e29,0x6e76,0x6e2a,0x6e4c,0x712a,UBOGON,
+ 0x7135,0x712c,0x7137,0x711d,UBOGON,UBOGON,0x7138,UBOGON,0x7134,0x712b,
+ 0x7133,0x7127,0x7124,UBOGON
+ },
+ { /* ku 1d */
+ 0x712d,0x7232,0x7283,0x7282,0x7287,0x7306,0x7324,0x7338,0x732a,0x732c,
+ 0x732b,UBOGON,0x732f,0x7328,0x7417,UBOGON,UBOGON,0x7419,0x7438,UBOGON,
+ 0x741f,0x7414,0x743c,0x73f7,0x741c,0x7415,0x7418,0x7439,0x74f9,0x7524,
+ UBOGON,UBOGON,UBOGON,0x756e,0x756d,0x7571,0x758e,UBOGON,0x75e5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7694,0x76b3,UBOGON,0x76d9,UBOGON,0x7748,0x7749,
+ 0x7743,UBOGON,UBOGON,0x7742,0x77df,UBOGON,0x7863,0x7876,UBOGON,0x785f,
+ 0x7866,0x7966,0x7971,UBOGON,UBOGON,0x7976,0x7984,0x7975,0x79ff,0x7a07,
+ UBOGON,0x7a0e,0x7a09,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ae7,
+ 0x7ae2,0x7b55,UBOGON,UBOGON,0x7b43,0x7b57,0x7b6c,0x7b42,0x7b53,UBOGON,
+ 0x7b41,UBOGON,UBOGON,0x7ca7
+ },
+ { /* ku 1e */
+ 0x7ca0,0x7ca6,0x7ca4,0x7d74,UBOGON,0x7d59,UBOGON,0x7d60,0x7d57,0x7d6c,
+ 0x7d7e,0x7d64,UBOGON,0x7d5a,0x7d5d,UBOGON,UBOGON,UBOGON,0x7d76,0x7d4d,
+ 0x7d75,UBOGON,0x7fd3,0x7fd6,UBOGON,UBOGON,0x8060,0x804e,0x8145,0x813b,
+ UBOGON,0x8148,0x8142,0x8149,0x8140,0x8114,0x8141,UBOGON,0x81ef,0x81f6,
+ 0x8203,UBOGON,0x83ed,UBOGON,0x83da,0x8418,0x83d2,0x8408,UBOGON,0x8400,
+ UBOGON,UBOGON,UBOGON,0x8417,0x8346,0x8414,0x83d3,0x8405,0x841f,0x8402,
+ 0x8416,0x83cd,0x83e6,UBOGON,0x865d,0x86d5,0x86e1,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x86ee,0x8847,0x8846,UBOGON,UBOGON,0x88bb,UBOGON,0x88bf,0x88b4,
+ UBOGON,0x88b5,UBOGON,0x899a,0x8a43,UBOGON,UBOGON,0x8a5a,UBOGON,UBOGON,
+ UBOGON,0x8a35,0x8a38,0x8a42
+ },
+ { /* ku 1f */
+ 0x8a49,0x8a5d,0x8a4b,0x8a3d,UBOGON,UBOGON,UBOGON,UBOGON,0x8c60,0x8c5e,
+ 0x8c7f,0x8c7e,0x8c83,UBOGON,0x8cb1,0x8d87,UBOGON,UBOGON,0x8d88,0x8d83,
+ UBOGON,UBOGON,0x8d86,0x8d8b,0x8d82,0x8dca,0x8dd2,UBOGON,UBOGON,0x8dd4,
+ 0x8dc9,0x8eb0,UBOGON,UBOGON,UBOGON,0x8ef2,0x8ee4,0x8ef3,0x8eea,UBOGON,
+ 0x8efd,UBOGON,0x8f9d,0x902b,0x902a,UBOGON,0x9028,0x9029,0x902c,UBOGON,
+ UBOGON,0x903a,0x9030,0x9037,0x903b,UBOGON,0x910a,UBOGON,UBOGON,UBOGON,
+ 0x91fe,0x9220,UBOGON,0x920b,UBOGON,0x9218,0x9222,UBOGON,0x921b,0x9208,
+ UBOGON,0x920e,0x9213,UBOGON,UBOGON,0x9595,UBOGON,UBOGON,UBOGON,0x968c,
+ 0x967b,0x967f,0x9681,UBOGON,0x9682,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x96ee,0x96ed,UBOGON,0x96ec
+ },
+ { /* ku 20 */
+ 0x975f,0x976f,UBOGON,0x976d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x98f0,UBOGON,UBOGON,UBOGON,0x9aa9,UBOGON,UBOGON,0x9ae0,0x4eb7,UBOGON,
+ UBOGON,0x50cc,0x50bc,UBOGON,0x50aa,0x50b9,UBOGON,0x50ab,0x50c3,0x50cd,
+ 0x517e,0x527e,0x5279,UBOGON,UBOGON,0x52e1,0x52e0,0x52e7,0x5380,0x53ab,
+ 0x53aa,0x53a9,0x53e0,0x55ea,UBOGON,0x55d7,UBOGON,UBOGON,0x55c1,0x5715,
+ UBOGON,0x586c,UBOGON,0x585c,0x5850,0x5861,0x586a,0x5869,0x5856,0x5860,
+ 0x5866,0x585f,0x5923,0x5966,0x5968,UBOGON,UBOGON,0x5ace,UBOGON,0x5ac5,
+ 0x5ac3,UBOGON,UBOGON,0x5ad0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x5b74,0x5b76,0x5bdc,0x5bd7,0x5bda,0x5bdb,UBOGON,0x5c20,0x5d6d,0x5d66,
+ UBOGON,0x5d64,0x5d6e,UBOGON
+ },
+ { /* ku 21 */
+ 0x5d60,0x5f42,0x5f5a,0x5f6e,UBOGON,UBOGON,0x6130,0x613a,0x612a,0x6143,
+ 0x6119,0x6131,UBOGON,0x613d,UBOGON,UBOGON,UBOGON,0x6408,0x6432,0x6438,
+ UBOGON,0x6431,UBOGON,0x6419,UBOGON,0x6411,UBOGON,UBOGON,0x6429,0x641d,
+ UBOGON,UBOGON,UBOGON,0x643c,UBOGON,0x6446,0x6447,UBOGON,UBOGON,0x643a,
+ 0x6407,UBOGON,0x656b,UBOGON,0x6570,0x656d,UBOGON,0x65e4,0x6693,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x668f,UBOGON,UBOGON,0x6692,UBOGON,0x668e,UBOGON,
+ 0x6946,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6931,UBOGON,
+ UBOGON,0x693e,UBOGON,0x697c,0x6943,UBOGON,0x6973,UBOGON,0x6955,UBOGON,
+ UBOGON,0x6985,0x694d,0x6950,0x6947,0x6967,0x6936,0x6964,0x6961,UBOGON,
+ 0x697d,0x6b44,0x6b40,0x6b71
+ },
+ { /* ku 22 */
+ 0x6b73,0x6b9c,UBOGON,UBOGON,UBOGON,0x6bc1,UBOGON,0x6bfa,0x6c31,0x6c32,
+ UBOGON,UBOGON,0x6eb8,0x6ea8,UBOGON,0x6e91,0x6ebb,UBOGON,0x6e9a,UBOGON,
+ UBOGON,0x6ea9,UBOGON,UBOGON,0x6eb5,0x6e6c,0x6ee8,UBOGON,0x6edd,0x6eda,
+ 0x6ee6,0x6eac,UBOGON,UBOGON,UBOGON,0x6ed9,0x6ee3,0x6ee9,0x6edb,UBOGON,
+ 0x716f,UBOGON,UBOGON,0x7148,UBOGON,0x714a,0x716b,UBOGON,0x714f,0x7157,
+ 0x7174,UBOGON,UBOGON,UBOGON,0x7145,0x7151,0x716d,UBOGON,0x7251,0x7250,
+ 0x724e,UBOGON,0x7341,UBOGON,0x732e,0x7346,UBOGON,0x7427,UBOGON,0x7448,
+ 0x7453,0x743d,UBOGON,0x745d,0x7456,UBOGON,0x741e,0x7447,0x7443,0x7458,
+ 0x7449,UBOGON,0x744c,0x7445,0x743e,UBOGON,0x7501,0x751e,UBOGON,UBOGON,
+ 0x757a,0x75ee,0x7602,0x7697
+ },
+ { /* ku 23 */
+ 0x7698,UBOGON,UBOGON,UBOGON,0x775d,0x7764,0x7753,0x7758,0x7882,0x7890,
+ 0x788a,UBOGON,0x787a,0x787d,UBOGON,0x788b,0x7878,UBOGON,UBOGON,0x788d,
+ 0x7888,0x7892,0x7881,0x797e,0x7983,UBOGON,UBOGON,UBOGON,0x7980,UBOGON,
+ UBOGON,UBOGON,0x7a0f,UBOGON,UBOGON,0x7a1d,UBOGON,0x7aa1,0x7aa4,UBOGON,
+ 0x7ae9,0x7aea,UBOGON,0x7b62,0x7b6b,UBOGON,0x7b5e,UBOGON,0x7b79,UBOGON,
+ UBOGON,0x7b6f,0x7b68,UBOGON,UBOGON,0x7cae,UBOGON,UBOGON,UBOGON,0x7cb0,
+ UBOGON,0x7d90,UBOGON,0x7d8a,UBOGON,0x7d8b,0x7d99,0x7d95,UBOGON,0x7d87,
+ 0x7d78,0x7d97,0x7d89,0x7d98,UBOGON,UBOGON,UBOGON,0x7fa3,UBOGON,UBOGON,
+ UBOGON,0x7fdd,0x8057,UBOGON,0x8163,0x816a,0x816c,UBOGON,UBOGON,UBOGON,
+ 0x815d,0x8175,UBOGON,0x815f
+ },
+ { /* ku 24 */
+ UBOGON,0x817d,0x816d,UBOGON,UBOGON,0x8241,0x844f,0x8484,UBOGON,0x847f,
+ UBOGON,0x8448,0x842a,0x847b,0x8472,0x8464,0x842e,0x845c,0x8453,UBOGON,
+ 0x8441,0x84c8,UBOGON,0x8462,0x8480,0x843e,0x8483,0x8471,UBOGON,0x844a,
+ 0x8455,0x8458,UBOGON,UBOGON,UBOGON,0x86fc,0x86fd,0x8715,UBOGON,0x8716,
+ 0x86ff,UBOGON,UBOGON,UBOGON,0x8858,0x88cf,0x88e0,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x89e7,0x8a6a,0x8a80,UBOGON,0x8a6f,0x8a65,UBOGON,0x8a78,0x8a7d,
+ 0x8a88,UBOGON,UBOGON,0x8a64,0x8a7e,UBOGON,0x8a67,0x8c63,0x8c88,UBOGON,
+ 0x8ccd,UBOGON,0x8cc9,UBOGON,0x8ded,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8eb1,UBOGON,UBOGON,0x8f04,0x8f9e,0x8fa0,0x9043,0x9046,
+ 0x9048,0x9045,0x9040,0x904c
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,0x910c,0x9113,0x9115,UBOGON,0x916b,0x9167,0x925d,0x9255,
+ 0x9235,UBOGON,0x9259,0x922f,0x923c,0x928f,0x925c,0x926a,0x9262,0x925f,
+ 0x926b,0x926e,0x923b,0x9244,0x9241,0x959a,UBOGON,0x9599,UBOGON,UBOGON,
+ UBOGON,0x968f,UBOGON,0x9696,UBOGON,UBOGON,UBOGON,0x96f4,0x96fc,UBOGON,
+ 0x9755,UBOGON,0x9779,UBOGON,UBOGON,UBOGON,0x97ee,0x97f5,UBOGON,0x980b,
+ UBOGON,0x98f3,UBOGON,UBOGON,0x98f7,0x98ff,0x98f5,UBOGON,0x98ec,0x98f1,
+ UBOGON,UBOGON,0x999a,UBOGON,0x9ae2,0x9b3d,0x9b5d,0x9ce8,UBOGON,0x9ceb,
+ 0x9cef,0x9cee,0x9e81,0x9f14,0x50d0,0x50d9,0x50dc,0x50d8,UBOGON,0x50e1,
+ 0x50eb,UBOGON,UBOGON,0x50f4,0x50e2,0x50de,UBOGON,UBOGON,UBOGON,0x51f4,
+ UBOGON,UBOGON,UBOGON,0x52ed
+ },
+ { /* ku 26 */
+ 0x52ea,UBOGON,0x5332,UBOGON,0x53ae,0x53b0,UBOGON,0x55fb,0x5603,0x560b,
+ UBOGON,0x5607,UBOGON,0x55f8,UBOGON,0x5628,0x561e,UBOGON,0x5618,0x5611,
+ 0x5651,0x5605,0x5717,0x5892,UBOGON,0x588c,UBOGON,0x5878,0x5884,0x5873,
+ 0x58ad,0x5897,0x5895,0x5877,0x5872,0x5896,0x588d,0x5910,UBOGON,0x596c,
+ UBOGON,0x5ae7,UBOGON,0x5ae4,UBOGON,UBOGON,0x5aef,0x5626,UBOGON,UBOGON,
+ 0x5af0,0x5d7b,UBOGON,0x5d83,UBOGON,UBOGON,0x5d8b,0x5d8c,UBOGON,0x5d78,
+ 0x5e52,UBOGON,UBOGON,0x5ed0,0x5ecf,UBOGON,0x5fb3,0x5fb4,UBOGON,UBOGON,
+ UBOGON,0x617b,UBOGON,0x616f,0x6181,0x613c,0x6142,0x6138,0x6133,UBOGON,
+ 0x6160,0x6169,0x617d,0x6186,0x622c,0x6228,UBOGON,0x644c,UBOGON,0x6457,
+ 0x647c,UBOGON,UBOGON,0x6455
+ },
+ { /* ku 27 */
+ 0x6462,0x6471,0x646a,0x6456,0x643b,0x6481,UBOGON,0x644f,0x647e,0x6464,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6571,UBOGON,UBOGON,0x66a5,0x669a,
+ 0x669c,UBOGON,0x66a6,UBOGON,0x66a4,0x698f,0x69c5,0x69c8,0x6992,0x69b2,
+ UBOGON,UBOGON,UBOGON,0x69e3,0x69c0,0x69d6,0x69d1,0x699f,0x69a2,0x69d2,
+ UBOGON,UBOGON,UBOGON,0x69e1,0x69d5,0x699d,UBOGON,UBOGON,0x6998,UBOGON,
+ 0x6b74,0x6ba1,UBOGON,0x6ef0,0x6ef3,UBOGON,UBOGON,0x6f1b,0x6f0c,0x6f1d,
+ 0x6f34,0x6f28,0x6f17,UBOGON,0x6f44,0x6f42,0x6f04,0x6f11,0x6efa,0x6f4a,
+ 0x7191,0x718e,UBOGON,0x718b,0x718d,0x717f,0x718c,0x717e,0x717c,0x7183,
+ UBOGON,0x7188,UBOGON,UBOGON,0x7294,UBOGON,0x7355,0x7353,0x734f,0x7354,
+ 0x746c,0x7465,0x7466,0x7461
+ },
+ { /* ku 28 */
+ 0x746b,0x7468,0x7476,UBOGON,0x7460,UBOGON,0x7474,0x7506,0x760e,UBOGON,
+ 0x7607,UBOGON,UBOGON,0x76b9,UBOGON,0x76b7,0x76e2,UBOGON,0x7774,0x7777,
+ 0x7776,0x7775,UBOGON,0x7778,0x7771,UBOGON,0x777a,0x715b,0x777b,0x78a6,
+ 0x78ae,0x78b8,UBOGON,UBOGON,UBOGON,0x78b1,0x78af,UBOGON,0x7989,0x7987,
+ UBOGON,UBOGON,0x7a29,UBOGON,0x7a2a,UBOGON,0x7a2d,0x7a2c,UBOGON,0x7a32,
+ UBOGON,0x7aec,0x7af0,0x7b81,0x7b9e,0x7b83,UBOGON,0x7b92,UBOGON,0x7ba3,
+ 0x7b9f,0x7b93,UBOGON,0x7b86,0x7cb8,0x7cb7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7dc8,0x7db6,UBOGON,0x7dd1,UBOGON,0x7da8,0x7dab,UBOGON,0x7db3,
+ 0x7dcd,UBOGON,0x7dcf,0x7da4,UBOGON,UBOGON,0x7f41,0x7f6f,0x7f71,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,0x8023,0x805b,UBOGON,0x8061,0x805f,0x8181,UBOGON,UBOGON,0x8184,
+ 0x8213,UBOGON,0x824a,0x824c,UBOGON,UBOGON,UBOGON,0x84bd,0x8495,UBOGON,
+ 0x8492,0x84c3,UBOGON,0x8496,0x84a5,0x84b5,0x84b3,0x84a3,0x84e4,0x84d8,
+ 0x84d5,UBOGON,0x84b7,0x84ad,0x84da,0x8493,0x8736,UBOGON,UBOGON,UBOGON,
+ 0x873d,0x872b,0x8747,0x8739,UBOGON,0x8745,0x871d,UBOGON,0x88ff,0x88ea,
+ UBOGON,0x88f5,UBOGON,0x8900,0x88ed,0x8903,0x88e9,UBOGON,UBOGON,0x89ea,
+ UBOGON,0x8a9b,0x8a8e,0x8aa2,UBOGON,0x8a9c,0x8a94,0x8a90,0x8aa9,0x8aac,
+ UBOGON,0x8a9f,UBOGON,UBOGON,0x8a9d,UBOGON,0x8c67,UBOGON,UBOGON,0x8cd0,
+ 0x8cd6,0x8cd4,0x8d98,0x8d9a,0x8d97,UBOGON,UBOGON,UBOGON,0x8e0b,0x8e08,
+ 0x8e01,0x8eb4,0x8eb3,UBOGON
+ },
+ { /* ku 2a */
+ 0x8fa1,0x8fa2,UBOGON,0x905a,UBOGON,0x9061,0x905f,UBOGON,UBOGON,0x9125,
+ 0x917b,0x9176,0x917c,UBOGON,0x9289,0x92f6,0x92b1,0x92ad,0x9292,0x9281,
+ 0x9284,UBOGON,0x92ae,0x9290,0x929e,UBOGON,UBOGON,UBOGON,0x95a2,0x95a7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96a0,0x969d,0x969f,0x96d0,UBOGON,
+ 0x96d1,UBOGON,UBOGON,0x9759,UBOGON,0x9764,UBOGON,UBOGON,UBOGON,0x9819,
+ UBOGON,0x9814,0x9815,0x981a,UBOGON,UBOGON,UBOGON,UBOGON,0x9906,UBOGON,
+ 0x98f8,0x9901,UBOGON,0x99be,0x99bc,0x99b7,0x99b6,0x99c0,UBOGON,0x99b8,
+ UBOGON,UBOGON,UBOGON,0x99c4,UBOGON,0x99bf,UBOGON,0x9ada,0x9ae4,0x9ae9,
+ 0x9ae8,0x9aea,0x9ae5,UBOGON,0x9b26,UBOGON,UBOGON,0x9b40,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,0x9ebd,UBOGON,UBOGON,UBOGON,UBOGON,0x510e,UBOGON,0x50f7,UBOGON,
+ 0x50fc,0x510d,0x5101,0x51da,0x51d9,0x51db,0x5286,0x528e,0x52ee,0x5333,
+ 0x53b1,UBOGON,0x5647,0x562d,0x5654,UBOGON,0x564b,0x5652,0x5631,0x5644,
+ 0x5656,0x5650,0x562b,UBOGON,0x564d,0x5637,0x564f,0x58a2,0x58b7,UBOGON,
+ 0x58b2,UBOGON,0x58aa,0x58b5,0x58b0,UBOGON,0x58b4,0x58a4,0x58a7,UBOGON,
+ 0x5926,0x5afe,UBOGON,0x5b04,UBOGON,0x5afc,UBOGON,0x5b06,0x5b0a,0x5afa,
+ 0x5b0d,0x5b00,0x5b0e,UBOGON,UBOGON,UBOGON,0x5d91,UBOGON,0x5d8f,0x5d90,
+ 0x5d98,0x5da4,0x5d9b,0x5da3,0x5d96,0x5de4,0x5e5a,UBOGON,UBOGON,0x5e5e,
+ UBOGON,0x5fb8,0x6157,0x615c,0x61a6,0x6195,0x6188,UBOGON,0x61a3,0x618f,
+ UBOGON,0x6164,UBOGON,0x6159
+ },
+ { /* ku 2c */
+ 0x6178,UBOGON,0x6185,0x6187,0x619e,UBOGON,UBOGON,0x6198,0x619c,UBOGON,
+ UBOGON,0x622f,0x6480,0x649b,0x648e,0x648d,0x6494,0x64c6,UBOGON,0x64a8,
+ 0x6483,UBOGON,0x64b9,0x6486,0x64b4,0x64af,0x6491,UBOGON,0x64aa,0x64a1,
+ 0x64a7,0x66b6,0x66b3,UBOGON,0x66bc,0x66ac,UBOGON,0x66ad,0x6a0e,UBOGON,
+ 0x6a1c,0x6a1a,UBOGON,UBOGON,0x6a0b,UBOGON,0x69ef,0x6a0c,0x69f0,0x6a22,
+ UBOGON,0x69d8,UBOGON,0x6a12,0x69fa,UBOGON,0x6a2a,UBOGON,0x6a10,UBOGON,
+ UBOGON,0x6a29,0x69f9,0x69ea,0x6a2c,0x6a24,UBOGON,0x69e9,0x6b52,0x6b4f,
+ 0x6b53,UBOGON,UBOGON,0x6f10,0x6f65,0x6f75,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6fd0,UBOGON,0x6f5c,0x6f3d,0x6f71,UBOGON,0x6f91,0x6f0b,0x6f79,0x6f81,
+ 0x6f8f,UBOGON,0x6f59,0x6f74
+ },
+ { /* ku 2d */
+ UBOGON,0x71ae,UBOGON,0x71a3,0x71ad,UBOGON,UBOGON,0x71ab,0x71a6,0x71a2,
+ UBOGON,0x52f2,0x7257,0x7255,0x7299,0x734b,0x747a,UBOGON,UBOGON,UBOGON,
+ 0x748c,0x7484,UBOGON,UBOGON,0x7482,0x7493,0x747b,UBOGON,0x7509,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x778a,UBOGON,0x7790,UBOGON,0x78c6,
+ 0x78d3,0x78c0,0x78d2,0x78c7,0x78c2,UBOGON,0x799f,0x799d,0x799e,UBOGON,
+ 0x7a41,UBOGON,0x7a38,0x7a3a,0x7a42,UBOGON,UBOGON,0x7a3e,0x7ab0,0x7bae,
+ 0x7bb3,UBOGON,UBOGON,0x7bbf,UBOGON,UBOGON,0x7bcd,UBOGON,0x7bb2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7cc4,0x7ccd,0x7cc2,0x7cc6,
+ 0x7cc3,0x7cc9,0x7cc7,UBOGON,0x7df8,UBOGON,0x7ded,0x7de2,UBOGON,UBOGON,
+ UBOGON,0x7ddc,0x7e02,0x7e01
+ },
+ { /* ku 2e */
+ UBOGON,0x7dd6,UBOGON,0x7de4,0x7dfe,UBOGON,0x7e00,0x7dfc,0x7dfd,UBOGON,
+ 0x7df5,0x7dff,UBOGON,0x7deb,0x7de5,0x7f78,0x7fae,0x7fe7,UBOGON,0x8065,
+ 0x806a,0x8066,0x8068,0x806b,0x8194,0x81a1,0x8192,0x8196,0x8193,UBOGON,
+ UBOGON,0x8501,UBOGON,0x84f8,UBOGON,0x84f5,UBOGON,0x8504,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x851b,0x8503,0x8533,0x8534,0x84ed,UBOGON,UBOGON,0x8535,
+ UBOGON,0x8505,UBOGON,UBOGON,UBOGON,UBOGON,0x877d,UBOGON,UBOGON,UBOGON,
+ 0x8771,UBOGON,0x885c,0x88e6,0x890f,0x891b,UBOGON,0x89a9,0x89a5,0x89ee,
+ 0x8ab1,UBOGON,0x8acc,0x8ace,UBOGON,0x8ab7,UBOGON,0x8ab5,0x8ae9,0x8ab4,
+ UBOGON,0x8ab3,0x8ac1,0x8aaf,0x8aca,0x8ad0,UBOGON,UBOGON,UBOGON,0x8c8e,
+ UBOGON,UBOGON,0x8ce9,0x8cdb
+ },
+ { /* ku 2f */
+ UBOGON,0x8ceb,0x8da4,UBOGON,0x8da2,0x8d9d,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8e2a,0x8e28,UBOGON,UBOGON,0x8eb8,0x8eb6,0x8eb9,0x8eb7,0x8f22,0x8f2b,
+ 0x8f27,0x8f19,0x8fa4,UBOGON,0x8fb3,UBOGON,0x9071,0x906a,UBOGON,UBOGON,
+ 0x9188,0x918c,0x92bf,0x92b8,0x92be,0x92dc,0x92e5,UBOGON,UBOGON,0x92d4,
+ 0x92d6,UBOGON,0x92da,0x92ed,0x92f3,0x92db,UBOGON,0x92b9,0x92e2,0x92eb,
+ 0x95af,UBOGON,0x95b2,0x95b3,UBOGON,UBOGON,UBOGON,0x96a3,0x96a5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x970a,UBOGON,0x9787,0x9789,0x978c,0x97ef,0x982a,
+ 0x9822,UBOGON,0x981f,UBOGON,0x9919,UBOGON,0x99ca,0x99da,UBOGON,UBOGON,
+ UBOGON,0x99de,0x99c8,0x99e0,UBOGON,0x9ab6,0x9ab5,UBOGON,0x9af4,UBOGON,
+ 0x9b6b,0x9b69,0x9b72,0x9b63
+ },
+ { /* ku 30 */
+ UBOGON,0x9d0d,UBOGON,0x9d01,0x9d0c,UBOGON,0x9cf8,UBOGON,UBOGON,0x9cfe,
+ 0x9d02,0x9e84,UBOGON,0x9eab,0x9eaa,0x511d,0x5116,UBOGON,0x512b,0x511e,
+ 0x511b,0x5290,0x5294,0x5314,UBOGON,UBOGON,0x5667,UBOGON,0x567b,UBOGON,
+ 0x565f,0x5661,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x58c3,
+ 0x58ca,0x58bb,0x58c0,0x58c4,0x5901,0x5b1f,0x5b18,0x5b11,0x5b15,UBOGON,
+ 0x5b12,0x5b1c,UBOGON,0x5b22,0x5b79,0x5da6,UBOGON,0x5db3,0x5dab,0x5eea,
+ UBOGON,0x5f5b,UBOGON,UBOGON,0x61b7,0x61ce,0x61b9,0x61bd,0x61cf,0x61c0,
+ 0x6199,0x6197,UBOGON,0x61bb,0x61d0,0x61c4,0x6231,UBOGON,0x64d3,0x64c0,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x64dc,0x64d1,0x64c8,UBOGON,0x64d5,0x66c3,
+ UBOGON,UBOGON,0x66bf,0x66c5
+ },
+ { /* ku 31 */
+ UBOGON,0x66cd,0x66c1,0x6706,UBOGON,0x6724,0x6a63,0x6a42,0x6a52,UBOGON,
+ 0x6a43,0x6a33,UBOGON,0x6a6c,0x6a57,UBOGON,0x6a4c,0x6a6e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6a37,UBOGON,0x6a71,0x6a4a,0x6a36,UBOGON,0x6a53,
+ UBOGON,0x6a45,0x6a70,UBOGON,UBOGON,0x6a5c,0x6b58,0x6b57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6fbb,UBOGON,UBOGON,0x6fbe,UBOGON,UBOGON,
+ UBOGON,0x6fb5,0x6fd3,0x6f9f,UBOGON,0x6fb7,0x6ff5,0x71b7,UBOGON,0x71bb,
+ UBOGON,0x71d1,UBOGON,0x71ba,UBOGON,0x71b6,0x71cc,UBOGON,UBOGON,0x71d3,
+ 0x749b,UBOGON,UBOGON,0x7496,0x74a2,0x749d,0x750a,0x750e,UBOGON,0x7581,
+ 0x762c,0x7637,0x7636,0x763b,UBOGON,0x76a1,UBOGON,UBOGON,0x7798,UBOGON,
+ 0x7796,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ 0x78d6,0x78eb,UBOGON,0x78dc,UBOGON,0x79a5,0x79a9,0x9834,0x7a53,0x7a45,
+ UBOGON,0x7a4f,UBOGON,0x7abd,0x7abb,0x7af1,UBOGON,UBOGON,0x7bec,0x7bed,
+ UBOGON,UBOGON,0x7cd3,UBOGON,0x7ce1,UBOGON,0x7e19,UBOGON,UBOGON,UBOGON,
+ 0x7e27,0x7e26,UBOGON,UBOGON,0x806e,0x81af,UBOGON,UBOGON,0x81ad,UBOGON,
+ 0x81aa,0x8218,UBOGON,UBOGON,UBOGON,UBOGON,0x856f,0x854c,UBOGON,0x8542,
+ UBOGON,0x855c,0x8570,0x855f,UBOGON,0x855a,0x854b,0x853f,0x878a,UBOGON,
+ 0x878b,0x87a1,0x878e,UBOGON,UBOGON,0x8799,0x885e,0x885f,0x8924,0x89a7,
+ 0x8aea,0x8afd,0x8af9,0x8ae3,0x8ae5,UBOGON,UBOGON,0x8aec,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8cf2,UBOGON,0x8cef,UBOGON,0x8da6,UBOGON,UBOGON,UBOGON,
+ 0x8e3b,0x8e43,UBOGON,0x8e32
+ },
+ { /* ku 33 */
+ 0x8f31,0x8f30,UBOGON,0x8f2d,0x8f3c,0x8fa7,0x8fa5,UBOGON,UBOGON,UBOGON,
+ 0x9137,0x9195,0x918e,UBOGON,0x9196,UBOGON,0x9345,0x930a,UBOGON,UBOGON,
+ 0x92fd,0x9317,0x931c,0x9307,0x9331,0x9332,0x932c,0x9330,0x9303,0x9305,
+ UBOGON,0x95c2,UBOGON,0x95b8,UBOGON,0x95c1,UBOGON,UBOGON,UBOGON,0x96ab,
+ 0x96b7,UBOGON,UBOGON,0x9715,0x9714,UBOGON,UBOGON,0x970c,0x9717,UBOGON,
+ 0x9793,UBOGON,0x97d2,UBOGON,UBOGON,0x9836,0x9831,0x9833,0x983c,0x982e,
+ 0x983a,UBOGON,0x983d,UBOGON,0x98b5,0x9922,0x9923,0x9920,0x991c,0x991d,
+ UBOGON,0x99a0,UBOGON,0x99ef,0x99e8,0x99eb,UBOGON,UBOGON,UBOGON,0x99e1,
+ 0x99e6,UBOGON,UBOGON,0x9af8,0x9af5,UBOGON,UBOGON,0x9b83,0x9b94,0x9b84,
+ UBOGON,0x9b8b,0x9b8f,UBOGON
+ },
+ { /* ku 34 */
+ 0x9b8c,UBOGON,0x9b89,UBOGON,0x9b8e,UBOGON,UBOGON,UBOGON,0x9d24,0x9d0f,
+ UBOGON,0x9d13,0x9d0a,UBOGON,UBOGON,UBOGON,UBOGON,0x9d2a,0x9d1a,UBOGON,
+ 0x9d27,0x9d16,0x9d21,UBOGON,0x9e85,0x9eac,0x9ec6,0x9ec5,0x9ed7,0x9f53,
+ UBOGON,0x5128,0x5127,0x51df,UBOGON,0x5335,0x53b3,UBOGON,0x568a,0x567d,
+ 0x5689,UBOGON,0x58cd,0x58d0,UBOGON,0x5b2b,0x5b33,0x5b29,0x5b35,0x5b31,
+ 0x5b37,0x5c36,0x5dbe,UBOGON,0x5db9,UBOGON,0x5dbb,UBOGON,0x61e2,0x61db,
+ 0x61dd,0x61dc,0x61da,UBOGON,0x61d9,UBOGON,UBOGON,0x64df,UBOGON,UBOGON,
+ 0x64e1,UBOGON,0x64ee,UBOGON,0x65b5,0x66d4,0x66d5,UBOGON,0x66d0,0x66d1,
+ 0x66ce,0x66d7,UBOGON,UBOGON,0x6a7d,0x6a8a,UBOGON,0x6aa7,UBOGON,0x6a99,
+ 0x6a82,0x6a88,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ 0x6a86,UBOGON,0x6a98,0x6a9d,UBOGON,UBOGON,0x6a8f,UBOGON,0x6aaa,UBOGON,
+ 0x6b5d,UBOGON,0x6c0a,UBOGON,0x6fd7,0x6fd6,0x6fe5,UBOGON,UBOGON,UBOGON,
+ 0x6fd9,0x6fda,0x6fea,UBOGON,0x6ff6,UBOGON,UBOGON,0x71e3,UBOGON,0x71e9,
+ UBOGON,0x71eb,0x71ef,0x71f3,0x71ea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7371,UBOGON,0x74ae,UBOGON,0x74b3,UBOGON,0x74ac,UBOGON,UBOGON,0x7583,
+ 0x7645,0x764e,0x7644,0x76a3,0x76a5,0x77a6,0x77a4,UBOGON,0x77a9,0x77af,
+ UBOGON,UBOGON,UBOGON,0x78f0,0x78f8,0x78f1,UBOGON,0x7a49,UBOGON,UBOGON,
+ UBOGON,0x7ac2,0x7af2,0x7af3,0x7bfa,UBOGON,0x7bf6,0x7bfc,0x7c18,0x7c08,
+ 0x7c12,UBOGON,UBOGON,0x7cdb,0x7cda,UBOGON,UBOGON,UBOGON,0x7e2c,0x7e4d,
+ UBOGON,UBOGON,0x7f46,0x7ff6
+ },
+ { /* ku 36 */
+ 0x802b,0x8074,0x81b8,0x81c8,UBOGON,UBOGON,UBOGON,0x8592,0x8593,UBOGON,
+ 0x857f,0x85ab,0x8597,UBOGON,UBOGON,0x85ac,UBOGON,UBOGON,UBOGON,0x87ce,
+ UBOGON,0x87cd,UBOGON,UBOGON,0x87c1,0x87b1,0x87c7,UBOGON,0x8940,UBOGON,
+ 0x893f,0x8939,UBOGON,0x8943,UBOGON,UBOGON,UBOGON,0x89ab,UBOGON,0x8b1f,
+ 0x8b09,0x8b0c,UBOGON,UBOGON,0x8c40,UBOGON,0x8c96,UBOGON,0x8cf6,0x8cf7,
+ UBOGON,0x8e46,0x8e4f,UBOGON,UBOGON,UBOGON,0x8f3d,0x8f41,0x9366,0x9378,
+ 0x935d,0x9369,0x9374,0x937d,0x936e,0x9372,0x9373,0x9362,0x9348,0x9353,
+ 0x935f,0x9368,UBOGON,0x937f,0x936b,UBOGON,0x95c4,UBOGON,0x96af,0x96ad,
+ 0x96b2,UBOGON,UBOGON,0x971a,0x971b,UBOGON,UBOGON,UBOGON,UBOGON,0x979b,
+ 0x979f,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9840,UBOGON,0x9847,UBOGON,0x98b7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x99a2,UBOGON,UBOGON,0x9a00,0x99f3,
+ UBOGON,UBOGON,0x99f5,UBOGON,UBOGON,0x9abd,0x9b00,0x9b02,UBOGON,0x9b34,
+ 0x9b49,0x9b9f,UBOGON,0x9ba3,0x9bcd,0x9b99,0x9b9d,UBOGON,UBOGON,0x9d39,
+ UBOGON,0x9d44,UBOGON,UBOGON,0x9d35,UBOGON,UBOGON,0x9eaf,UBOGON,0x512f,
+ UBOGON,UBOGON,0x9f8e,UBOGON,0x569f,0x569b,0x569e,0x5696,0x5694,0x56a0,
+ UBOGON,0x5b3b,UBOGON,UBOGON,0x5b3a,0x5dc1,0x5f4d,0x5f5d,0x61f3,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x64f6,0x64e5,0x64ea,0x64e7,0x6505,UBOGON,0x64f9,
+ UBOGON,UBOGON,UBOGON,0x6aab,0x6aed,0x6ab2,0x6ab0,0x6ab5,0x6abe,0x6ac1,
+ 0x6ac8,UBOGON,0x6ac0,0x6abc
+ },
+ { /* ku 38 */
+ 0x6ab1,0x6ac4,0x6abf,UBOGON,UBOGON,0x7008,0x7003,0x6ffd,0x7010,0x7002,
+ 0x7013,UBOGON,0x71fa,0x7200,0x74b9,0x74bc,UBOGON,0x765b,0x7651,0x764f,
+ 0x76eb,0x77b8,UBOGON,0x77b9,0x77c1,0x77c0,0x77be,0x790b,UBOGON,0x7907,
+ 0x790a,0x7908,UBOGON,0x790d,0x7906,0x7915,0x79af,UBOGON,UBOGON,UBOGON,
+ 0x7af5,UBOGON,UBOGON,0x7c2e,UBOGON,0x7c1b,UBOGON,0x7c1a,0x7c24,UBOGON,
+ UBOGON,0x7ce6,0x7ce3,UBOGON,UBOGON,0x7e5d,0x7e4f,0x7e66,0x7e5b,0x7f47,
+ 0x7fb4,UBOGON,UBOGON,UBOGON,0x7ffa,0x802e,UBOGON,UBOGON,0x81ce,UBOGON,
+ UBOGON,0x8219,UBOGON,UBOGON,0x85cc,0x85b2,UBOGON,0x85bb,0x85c1,UBOGON,
+ UBOGON,UBOGON,0x87e9,0x87ee,0x87f0,0x87d6,0x880e,0x87da,0x8948,0x894a,
+ 0x894e,0x894d,0x89b1,0x89b0
+ },
+ { /* ku 39 */
+ 0x89b3,UBOGON,0x8b38,0x8b32,UBOGON,0x8b2d,UBOGON,0x8b34,UBOGON,0x8b29,
+ 0x8c74,UBOGON,UBOGON,0x8d03,UBOGON,UBOGON,0x8da9,0x8e58,UBOGON,UBOGON,
+ 0x8ebf,0x8ec1,0x8f4a,0x8fac,UBOGON,0x9089,0x913d,0x913c,0x91a9,0x93a0,
+ UBOGON,0x9390,UBOGON,0x9393,0x938b,0x93ad,0x93bb,0x93b8,UBOGON,UBOGON,
+ 0x939c,0x95d8,0x95d7,UBOGON,UBOGON,UBOGON,0x975d,0x97a9,0x97da,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9854,UBOGON,0x9855,0x984b,UBOGON,0x983f,0x98b9,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9938,0x9936,0x9940,UBOGON,0x993b,0x9939,
+ 0x99a4,UBOGON,UBOGON,0x9a08,0x9a0c,UBOGON,0x9a10,UBOGON,0x9b07,UBOGON,
+ 0x9bd2,UBOGON,0x9bc2,0x9bbb,0x9bcc,0x9bcb,UBOGON,UBOGON,0x9d4d,0x9d63,
+ 0x9d4e,UBOGON,0x9d50,0x9d55
+ },
+ { /* ku 3a */
+ UBOGON,0x9d5e,UBOGON,0x9e90,0x9eb2,0x9eb1,UBOGON,0x9eca,0x9f02,0x9f27,
+ 0x9f26,UBOGON,0x56af,0x58e0,0x58dc,UBOGON,0x5b39,UBOGON,UBOGON,0x5b7c,
+ 0x5bf3,UBOGON,UBOGON,0x5c6b,0x5dc4,0x650b,0x6508,0x650a,UBOGON,UBOGON,
+ 0x65dc,UBOGON,UBOGON,0x66e1,0x66df,0x6ace,0x6ad4,0x6ae3,0x6ad7,0x6ae2,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6ad8,0x6ad5,0x6ad2,UBOGON,UBOGON,0x701e,
+ 0x702c,0x7025,0x6ff3,0x7204,0x7208,0x7215,UBOGON,0x74c4,0x74c9,0x74c7,
+ 0x74c8,0x76a9,0x77c6,0x77c5,0x7918,0x791a,0x7920,UBOGON,0x7a66,0x7a64,
+ 0x7a6a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7c35,0x7c34,UBOGON,
+ UBOGON,0x7e6c,UBOGON,0x7e6e,0x7e71,UBOGON,0x81d4,0x81d6,0x821a,0x8262,
+ 0x8265,0x8276,0x85db,0x85d6
+ },
+ { /* ku 3b */
+ UBOGON,0x85e7,UBOGON,UBOGON,0x85f4,UBOGON,0x87fd,0x87d5,0x8807,UBOGON,
+ 0x880f,0x87f8,UBOGON,UBOGON,0x8987,UBOGON,0x89b5,0x89f5,UBOGON,0x8b3f,
+ 0x8b43,0x8b4c,UBOGON,0x8d0b,0x8e6b,0x8e68,0x8e70,0x8e75,0x8e77,UBOGON,
+ 0x8ec3,UBOGON,0x93e9,0x93ea,0x93cb,0x93c5,0x93c6,UBOGON,0x93ed,0x93d3,
+ UBOGON,0x93e5,UBOGON,UBOGON,0x93db,0x93eb,0x93e0,0x93c1,UBOGON,UBOGON,
+ 0x95dd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x97b2,0x97b4,0x97b1,0x97b5,0x97f2,UBOGON,UBOGON,UBOGON,0x9856,UBOGON,
+ UBOGON,UBOGON,0x9944,UBOGON,0x9a26,0x9a1f,0x9a18,0x9a21,0x9a17,UBOGON,
+ 0x9b09,UBOGON,UBOGON,0x9bc5,0x9bdf,UBOGON,0x9be3,UBOGON,0x9be9,0x9bee,
+ UBOGON,UBOGON,0x9d66,0x9d7a
+ },
+ { /* ku 3c */
+ UBOGON,0x9d6e,0x9d91,0x9d83,0x9d76,0x9d7e,0x9d6d,UBOGON,0x9e95,0x9ee3,
+ UBOGON,UBOGON,0x9f03,0x9f04,UBOGON,0x9f17,UBOGON,0x5136,UBOGON,0x5336,
+ UBOGON,0x5b42,UBOGON,UBOGON,0x5b44,0x5b46,0x5b7e,0x5dca,0x5dc8,0x5dcc,
+ 0x5ef0,UBOGON,0x6585,0x66e5,0x66e7,UBOGON,UBOGON,UBOGON,0x6af4,UBOGON,
+ 0x6ae9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x703d,UBOGON,0x7036,UBOGON,
+ 0x7216,UBOGON,0x7212,0x720f,0x7217,0x7211,0x720b,UBOGON,UBOGON,0x74cd,
+ 0x74d0,0x74cc,0x74ce,0x74d1,UBOGON,0x7589,UBOGON,0x7a6f,0x7c4b,0x7c44,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7e7f,0x8b71,UBOGON,0x802f,0x807a,
+ 0x807b,0x807c,UBOGON,UBOGON,UBOGON,0x85fc,0x8610,0x8602,UBOGON,UBOGON,
+ 0x85ee,0x8603,UBOGON,0x860d
+ },
+ { /* ku 3d */
+ 0x8613,0x8608,0x860f,0x8818,0x8812,UBOGON,UBOGON,0x8967,0x8965,0x89bb,
+ 0x8b69,0x8b62,UBOGON,0x8b6e,UBOGON,0x8b61,UBOGON,0x8b64,0x8b4d,0x8c51,
+ UBOGON,UBOGON,0x8e83,0x8ec6,UBOGON,0x941f,UBOGON,0x9404,0x9417,0x9408,
+ 0x9405,UBOGON,0x93f3,0x941e,0x9402,0x941a,0x941b,0x9427,0x941c,UBOGON,
+ 0x96b5,UBOGON,UBOGON,0x9733,UBOGON,0x9734,0x9731,0x97b8,0x97ba,UBOGON,
+ 0x97fc,UBOGON,UBOGON,0x98c3,UBOGON,0x994d,UBOGON,0x9a2f,UBOGON,UBOGON,
+ UBOGON,0x9ac9,UBOGON,0x9ac8,0x9ac4,0x9b2a,0x9b38,0x9b50,UBOGON,0x9c0a,
+ 0x9bfb,0x9c04,0x9bfc,0x9bfe,UBOGON,UBOGON,UBOGON,0x9c02,0x9bf6,0x9c1b,
+ 0x9bf9,0x9c15,0x9c10,0x9bff,0x9c00,0x9c0c,UBOGON,UBOGON,0x9d95,0x9da5,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ 0x9e98,0x9ec1,UBOGON,0x9f5a,0x5164,0x56bb,UBOGON,0x58e6,0x5b49,0x5bf7,
+ UBOGON,UBOGON,0x5dd0,UBOGON,0x5fc2,UBOGON,0x6511,UBOGON,0x6aff,0x6afe,
+ 0x6afd,UBOGON,0x6b01,UBOGON,UBOGON,0x704b,0x704d,0x7047,0x74d3,0x7668,
+ 0x7667,UBOGON,UBOGON,0x77d1,0x7930,0x7932,0x792e,UBOGON,0x9f9d,0x7ac9,
+ 0x7ac8,UBOGON,0x7c56,0x7c51,UBOGON,UBOGON,UBOGON,0x7e85,0x7e89,0x7e8e,
+ 0x7e84,UBOGON,0x826a,0x862b,0x862f,0x8628,UBOGON,0x8616,0x8615,0x861d,
+ 0x881a,UBOGON,UBOGON,UBOGON,0x89bc,0x8b75,0x8b7c,UBOGON,0x8d11,0x8d12,
+ 0x8f5c,0x91bb,UBOGON,0x93f4,UBOGON,UBOGON,0x942d,UBOGON,UBOGON,0x96e4,
+ 0x9737,0x9736,0x9767,0x97be,0x97bd,0x97e2,0x9868,0x9866,0x98c8,0x98ca,
+ 0x98c7,0x98dc,UBOGON,0x994f
+ },
+ { /* ku 3f */
+ 0x99a9,0x9a3c,UBOGON,0x9a3b,0x9ace,UBOGON,0x9b14,0x9b53,UBOGON,0x9c2e,
+ UBOGON,0x9c1f,UBOGON,UBOGON,UBOGON,UBOGON,0x9db0,0x9dbd,UBOGON,UBOGON,
+ 0x9dae,0x9dc4,0x9e7b,UBOGON,UBOGON,0x9e9e,UBOGON,0x9f05,UBOGON,0x9f69,
+ 0x9fa1,0x56c7,0x571d,0x5b4a,0x5dd3,UBOGON,0x5f72,0x6202,UBOGON,0x6235,
+ 0x6527,0x651e,0x651f,UBOGON,UBOGON,0x6b07,0x6b06,UBOGON,UBOGON,0x7054,
+ 0x721c,0x7220,0x7af8,UBOGON,0x7c5d,0x7c58,UBOGON,0x7e92,0x7f4e,UBOGON,
+ UBOGON,UBOGON,0x8827,UBOGON,0x8b81,0x8b83,UBOGON,0x8c44,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9442,0x944d,0x9454,0x944e,UBOGON,0x9443,UBOGON,UBOGON,
+ 0x973c,0x9740,0x97c0,UBOGON,UBOGON,UBOGON,UBOGON,0x995a,0x9a51,UBOGON,
+ 0x9add,UBOGON,UBOGON,0x9c38
+ },
+ { /* ku 40 */
+ UBOGON,0x9c45,0x9c3a,UBOGON,0x9c35,UBOGON,UBOGON,UBOGON,0x9ef1,UBOGON,
+ 0x9f93,0x529a,UBOGON,UBOGON,0x8641,0x5dd7,UBOGON,0x6528,UBOGON,UBOGON,
+ UBOGON,0x7053,0x7059,UBOGON,0x7221,UBOGON,0x766f,0x7937,0x79b5,0x7c62,
+ 0x7c5e,0x7cf5,UBOGON,UBOGON,0x863d,UBOGON,0x882d,0x8989,0x8b8d,0x8b87,
+ 0x8b90,0x8d1a,0x8e99,UBOGON,UBOGON,UBOGON,0x945f,UBOGON,UBOGON,0x9456,
+ 0x9461,0x945b,0x945a,0x945c,0x9465,UBOGON,0x9741,UBOGON,UBOGON,0x986e,
+ 0x986c,0x986d,UBOGON,0x99aa,0x9a5c,0x9a58,0x9ade,UBOGON,0x9c4f,0x9c51,
+ UBOGON,0x9c53,UBOGON,UBOGON,UBOGON,0x9dfc,0x9f39,UBOGON,0x513e,UBOGON,
+ 0x56d2,UBOGON,0x5b4f,0x6b14,UBOGON,0x7a72,0x7a73,UBOGON,UBOGON,UBOGON,
+ 0x8b91,UBOGON,UBOGON,0x91bf
+ },
+ { /* ku 41 */
+ UBOGON,0x946c,UBOGON,UBOGON,0x96e6,0x9745,UBOGON,0x97c8,0x97e4,0x995d,
+ UBOGON,0x9b21,UBOGON,0x9b2c,0x9b57,UBOGON,UBOGON,0x9c5d,0x9c61,0x9c65,
+ 0x9e08,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9f45,UBOGON,UBOGON,0x6205,
+ 0x66ef,0x6b1b,0x6b1d,0x7225,0x7224,0x7c6d,UBOGON,0x8642,0x8649,UBOGON,
+ 0x8978,0x898a,0x8b97,UBOGON,0x8c9b,0x8d1c,UBOGON,0x8ea2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c6c,UBOGON,0x9c6f,UBOGON,
+ 0x9e0e,UBOGON,0x9f08,0x9f1d,0x9fa3,UBOGON,UBOGON,0x5f60,0x6b1c,UBOGON,
+ UBOGON,UBOGON,0x7cf3,UBOGON,0x8b9b,0x8ea7,0x91c4,UBOGON,0x947a,UBOGON,
+ UBOGON,0x9a61,0x9a63,0x9ad7,0x9c76,UBOGON,0x9fa5,UBOGON,0x7067,UBOGON,
+ 0x72ab,0x864a,0x897d,0x8b9d
+ },
+ { /* ku 42 */
+ 0x8c53,0x8f65,0x947b,UBOGON,0x98cd,0x98dd,UBOGON,0x9b30,0x9e16,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x96e7,0x9e18,0x9ea2,UBOGON,0x9f7c,UBOGON,
+ 0x7e9e,0x9484,UBOGON,0x9e1c,UBOGON,0x7c71,0x97ca,UBOGON,UBOGON,UBOGON,
+ 0x9ea3,UBOGON,0x9c7b,0x9f97,UBOGON,UBOGON,0x9750,UBOGON,UBOGON,UBOGON,
+ 0x5727,0x5c13,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5fc8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6765,UBOGON,UBOGON,0x52bd,UBOGON,0x5b66,
+ UBOGON,0x65f9,0x6788,0x6ce6,0x6ccb,UBOGON,0x4fbd,0x5f8d,UBOGON,0x6018,
+ 0x6048,UBOGON,0x6b29,0x70a6,UBOGON,0x7706,UBOGON,UBOGON,UBOGON,0x5a10,
+ 0x5cfc,0x5cfe,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x70c9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,0x9579,UBOGON,0x96ba,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7b29,0x8128,UBOGON,0x8a2e,UBOGON,UBOGON,UBOGON,0x9ad9,
+ UBOGON,0x582b,0x5845,UBOGON,0x63fa,UBOGON,UBOGON,UBOGON,0x6e86,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5867,UBOGON,0x5bdd,0x656e,UBOGON,UBOGON,
+ UBOGON,0x8c87,UBOGON,0x50d2,0x50df,UBOGON,UBOGON,UBOGON,UBOGON,0x69ba,
+ UBOGON,0x6b9d,UBOGON,0x8059,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6f8a,UBOGON,UBOGON,0x7bc3,
+ 0x7bc2,UBOGON,UBOGON,UBOGON,UBOGON,0x90f6,UBOGON,0x9823,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x71cd,0x7499,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9842,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,0x7f84,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d0e,UBOGON,0x9861,
+ UBOGON,UBOGON,0x8b73,UBOGON,0x9c27,UBOGON,0x9458,0x77d6,0x9b2d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f66,
+ 0x4f68,0x4fe7,0x503f,UBOGON,0x50a6,0x510f,0x523e,0x5324,0x5365,0x539b,
+ 0x517f,0x54cb,0x5573,0x5571,0x556b,0x55f4,0x5622,0x5620,0x5692,0x56ba,
+ 0x5691,0x56b0,0x5759,0x578a,0x580f,0x5812,0x5813,0x5847,0x589b,0x5900,
+ 0x594d,0x5ad1,0x5ad3,0x5b67,0x5c57,0x5c77,0x5cd5,0x5d75,0x5d8e,0x5da5,
+ 0x5db6,0x5dbf,0x5e65,0x5ecd,0x5eed,0x5f94,0x5f9a,0x5fba,0x6125,0x6150,
+ 0x62a3,0x6360,0x6364,0x63b6
+ },
+ { /* ku 45 */
+ 0x6403,0x64b6,0x651a,0x7a25,0x5c21,0x66e2,0x6702,0x67a4,0x67ac,0x6810,
+ 0x6806,0x685e,0x685a,0x692c,0x6929,0x6a2d,0x6a77,0x6a7a,0x6aca,0x6ae6,
+ 0x6af5,0x6b0d,0x6b0e,0x6bdc,0x6bdd,0x6bf6,0x6c1e,0x6c63,0x6da5,0x6e0f,
+ 0x6e8a,0x6e84,0x6e8b,0x6e7c,0x6f4c,0x6f48,0x6f49,0x6f9d,0x6f99,0x6ff8,
+ 0x702e,0x702d,0x705c,0x79cc,0x70bf,0x70ea,0x70e5,0x7111,0x7112,0x713f,
+ 0x7139,0x713b,0x713d,0x7177,0x7175,0x7176,0x7171,0x7196,0x7193,0x71b4,
+ 0x71dd,0x71de,0x720e,0x5911,0x7218,0x7347,0x7348,0x73ef,0x7412,0x743b,
+ 0x74a4,0x748d,0x74b4,0x7673,0x7677,0x76bc,0x7819,0x781b,0x783d,0x7853,
+ 0x7854,0x7858,0x78b7,0x78d8,0x78ee,0x7922,0x794d,0x7986,0x7999,0x79a3,
+ 0x79bc,0x7aa7,0x7b37,0x7b59
+ },
+ { /* ku 46 */
+ 0x7bd0,0x7c2f,0x7c32,0x7c42,0x7c4e,0x7c68,0x7ca9,0x7ced,0x7dd0,0x7e07,
+ 0x7dd3,0x7e64,0x7f40,UBOGON,0x8041,0x8063,0x80bb,0x6711,0x6725,0x8248,
+ 0x8310,0x8362,0x8312,0x8421,0x841e,0x84e2,0x84de,0x84e1,0x8573,0x85d4,
+ 0x85f5,0x8637,0x8645,0x8672,0x874a,0x87a9,0x87a5,0x87f5,0x8834,0x8850,
+ 0x8887,0x8954,0x8984,0x8b03,0x8c52,0x8cd8,0x8d0c,0x8d18,0x8db0,0x8ebc,
+ 0x8ed5,0x8faa,0x909c,UBOGON,0x915c,0x922b,0x9221,0x9273,0x92f4,0x92f5,
+ 0x933f,0x9342,0x9386,0x93be,0x93bc,0x93bd,0x93f1,0x93f2,0x93ef,0x9422,
+ 0x9423,0x9424,0x9467,0x9466,0x9597,0x95ce,0x95e7,0x973b,0x974d,0x98e4,
+ 0x9942,0x9b1d,0x9b98,UBOGON,0x9d49,0x6449,0x5e71,0x5e85,0x61d3,0x990e,
+ 0x8002,0x781e,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ 0x5528,0x5572,0x55ba,0x55f0,0x55ee,0x56b8,0x56b9,0x56c4,0x8053,0x92b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+#endif
+
+#if CNS_EXTENSION
+/* CNS 11643 plane 15 conversion table */
+
+static const unsigned short
+ cns11643_15tab[MAX_CNS11643_KU_15][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5301,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3436,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9f99,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c49,
+ UBOGON,UBOGON,UBOGON,0x8fb7,UBOGON,0x3406,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4f29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x534e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5c81,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5f10,UBOGON,UBOGON,0x6268,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6742,0x6740,0x51ea,UBOGON,UBOGON,UBOGON,
+ 0x6c62,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7391,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8fbb,0x8fbc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3575,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x56e8,UBOGON,0x575b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5c97,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,0x6762,UBOGON,UBOGON,0x383c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x62a4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6766,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6ca3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x707f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,0x77f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8fc8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4fab,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3453,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5c2d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x549c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5788,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34ac,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x62c3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6619,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3b49,UBOGON,0x67a1,UBOGON,0x67a6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c91,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cd3,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x77fe,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7f57,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43d5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82c5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8fdf,UBOGON,UBOGON,
+ 0x8fdc,0x488c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4fe4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x551b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3588,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x57aa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x57ab,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36c9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5ba9,UBOGON,UBOGON,UBOGON,UBOGON,0x3917,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6811,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7551,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ebd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7553,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7818,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4133,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ad7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ 0x7c7e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x867e,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4844,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5266,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5520,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5521,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x57d7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x36e1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36e2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5bbe
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x387c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38e3,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3aec,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,0x6857,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7f3c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8273,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4627,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96be,UBOGON,
+ UBOGON,UBOGON,0x66fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35ab,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x364b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5a72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x37e2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x68bd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6e15,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dc1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7413,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x74f8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7b3d
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,0x76d8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x79fc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7b39,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7d4b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83b9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x86cf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8eae,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96eb,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x55b0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5840,0x5842
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3701,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x692b,UBOGON,0x6916,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ 0x691b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6927,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6bf5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3d20,UBOGON,UBOGON,UBOGON,0x6e82,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d21,UBOGON,UBOGON,
+ 0x6e7a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7129
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3eda,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ 0x7cab,UBOGON,0x7cac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83f7,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x44ea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4989,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9596
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4ab2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x55f1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x5f41,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x698a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x698c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6980,UBOGON,
+ 0x697f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dda,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ddb,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ee2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3ee3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x789c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7b7b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47f3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x90d2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x95a0,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x51a9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bbc,
+ UBOGON,UBOGON,0x3ba4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7195,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3de8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7198,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7478,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x78b9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a33,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7cc0,UBOGON,UBOGON,UBOGON,
+ 0x7cc1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8744,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4886,UBOGON,UBOGON,UBOGON,0x9064,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,0x9277,UBOGON,UBOGON,UBOGON,UBOGON,0x92af,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x366d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x366e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5e64,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6a2b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6f46,UBOGON,
+ UBOGON,UBOGON,0x6f9a,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f53,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4526,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4527,UBOGON,UBOGON,0x4528,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x92f2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b79,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x567a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x372c,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5f5c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x65d9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6a72,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6a78,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6b5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3efb,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x3efc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8ebe,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x933b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9340,UBOGON,UBOGON,UBOGON,0x933a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9b96,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39a0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x71f5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3f01,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a50,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x481a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9387,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9385,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x493c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9bb1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,0x9d47,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3a6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x455c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4787,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x93b9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x93bf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9bcf,UBOGON,UBOGON,UBOGON,UBOGON,0x9d64,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9ebf,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3c07,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f05,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45f9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x89b8,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4953,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9bf3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c69,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c12,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7c4f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 46 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9425,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4878,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x95e6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c2f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6b0c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c47,
+ 0x4c88,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7936,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6b15,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4584,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x53b5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+#endif
+#endif
diff --git a/imap/src/charset/decomtab.c b/imap/src/charset/decomtab.c
new file mode 100644
index 00000000..8a02e322
--- /dev/null
+++ b/imap/src/charset/decomtab.c
@@ -0,0 +1,2909 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode decomposition tables (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 April 2006
+ * Last Edited: 6 December 2006
+ */
+
+/* BMP decompositions */
+
+#define UCS4_BMPLOMIN 0x00a0
+#define UCS4_BMPLOMAX 0x33ff
+#define UCS4_BMPLOIXMASK 0x1fff
+#define UCS4_BMPLOSIZEMASK 0xe000
+#define UCS4_BMPLOSIZESHIFT 13
+
+ /* BMP low decomposition indices - sssi iiii iiii iiii */
+static unsigned short ucs4_dbmploixtab[13152] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2001,0x0000,0x0003,0x0000,0x0000,0x0000,0x0000,0x2004,
+ 0x0000,0x0000,0x0006,0x0007,0x2008,0x000a,0x0000,0x0000,
+ 0x200b,0x000d,0x000e,0x0000,0x400f,0x4012,0x4015,0x0000,
+ 0x2018,0x201a,0x201c,0x201e,0x2020,0x2022,0x0000,0x2024,
+ 0x2026,0x2028,0x202a,0x202c,0x202e,0x2030,0x2032,0x2034,
+ 0x0000,0x2036,0x2038,0x203a,0x203c,0x203e,0x2040,0x0000,
+ 0x0000,0x2042,0x2044,0x2046,0x2048,0x204a,0x0000,0x0000,
+ 0x204c,0x204e,0x2050,0x2052,0x2054,0x2056,0x0000,0x2058,
+ 0x205a,0x205c,0x205e,0x2060,0x2062,0x2064,0x2066,0x2068,
+ 0x0000,0x206a,0x206c,0x206e,0x2070,0x2072,0x2074,0x0000,
+ 0x0000,0x2076,0x2078,0x207a,0x207c,0x207e,0x0000,0x2080,
+ 0x2082,0x2084,0x2086,0x2088,0x208a,0x208c,0x208e,0x2090,
+ 0x2092,0x2094,0x2096,0x2098,0x209a,0x209c,0x209e,0x20a0,
+ 0x0000,0x0000,0x20a2,0x20a4,0x20a6,0x20a8,0x20aa,0x20ac,
+ 0x20ae,0x20b0,0x20b2,0x20b4,0x20b6,0x20b8,0x20ba,0x20bc,
+ 0x20be,0x20c0,0x20c2,0x20c4,0x20c6,0x20c8,0x0000,0x0000,
+ 0x20ca,0x20cc,0x20ce,0x20d0,0x20d2,0x20d4,0x20d6,0x20d8,
+ 0x20da,0x0000,0x20dc,0x20de,0x20e0,0x20e2,0x20e4,0x20e6,
+ 0x0000,0x20e8,0x20ea,0x20ec,0x20ee,0x20f0,0x20f2,0x20f4,
+ 0x20f6,0x0000,0x0000,0x20f8,0x20fa,0x20fc,0x20fe,0x2100,
+ 0x2102,0x2104,0x0000,0x0000,0x2106,0x2108,0x210a,0x210c,
+ 0x210e,0x2110,0x0000,0x0000,0x2112,0x2114,0x2116,0x2118,
+ 0x211a,0x211c,0x211e,0x2120,0x2122,0x2124,0x2126,0x2128,
+ 0x212a,0x212c,0x212e,0x2130,0x2132,0x2134,0x0000,0x0000,
+ 0x2136,0x2138,0x213a,0x213c,0x213e,0x2140,0x2142,0x2144,
+ 0x2146,0x2148,0x214a,0x214c,0x214e,0x2150,0x2152,0x2154,
+ 0x2156,0x2158,0x215a,0x215c,0x215e,0x2160,0x2162,0x0164,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2165,0x2167,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2169,
+ 0x216b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x216d,0x216f,0x2171,0x2173,
+ 0x2175,0x2177,0x2179,0x217b,0x217d,0x217f,0x2181,0x2183,
+ 0x2185,0x2187,0x2189,0x218b,0x218d,0x218f,0x2191,0x2193,
+ 0x2195,0x2197,0x2199,0x219b,0x219d,0x0000,0x219f,0x21a1,
+ 0x21a3,0x21a5,0x21a7,0x21a9,0x0000,0x0000,0x21ab,0x21ad,
+ 0x21af,0x21b1,0x21b3,0x21b5,0x21b7,0x21b9,0x21bb,0x21bd,
+ 0x21bf,0x21c1,0x21c3,0x21c5,0x21c7,0x21c9,0x0000,0x0000,
+ 0x21cb,0x21cd,0x21cf,0x21d1,0x21d3,0x21d5,0x21d7,0x21d9,
+ 0x21db,0x21dd,0x21df,0x21e1,0x21e3,0x21e5,0x21e7,0x21e9,
+ 0x21eb,0x21ed,0x21ef,0x21f1,0x21f3,0x21f5,0x21f7,0x21f9,
+ 0x21fb,0x21fd,0x21ff,0x2201,0x2203,0x2205,0x2207,0x2209,
+ 0x220b,0x220d,0x220f,0x2211,0x0000,0x0000,0x2213,0x2215,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2217,0x2219,
+ 0x221b,0x221d,0x221f,0x2221,0x2223,0x2225,0x2227,0x2229,
+ 0x222b,0x222d,0x222f,0x2231,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0233,0x0234,0x0235,0x0236,0x0237,0x0238,0x0239,0x023a,
+ 0x023b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x223c,0x223e,0x2240,0x2242,0x2244,0x2246,0x0000,0x0000,
+ 0x0248,0x0249,0x024a,0x024b,0x024c,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x024d,0x024e,0x0000,0x024f,0x2250,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0252,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2253,0x0000,0x0000,0x0000,0x0255,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2256,0x2258,0x225a,0x025c,
+ 0x225d,0x225f,0x2261,0x0000,0x2263,0x0000,0x2265,0x2267,
+ 0x2269,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x226b,0x226d,0x226f,0x2271,0x2273,0x2275,
+ 0x2277,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2279,0x227b,0x227d,0x227f,0x2281,0x0000,
+ 0x0283,0x0284,0x0285,0x2286,0x2288,0x028a,0x028b,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x028c,0x028d,0x028e,0x0000,0x028f,0x0290,0x0000,0x0000,
+ 0x0000,0x0291,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2292,0x2294,0x0000,0x2296,0x0000,0x0000,0x0000,0x2298,
+ 0x0000,0x0000,0x0000,0x0000,0x229a,0x229c,0x229e,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22a0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22a2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x22a4,0x22a6,0x0000,0x22a8,0x0000,0x0000,0x0000,0x22aa,
+ 0x0000,0x0000,0x0000,0x0000,0x22ac,0x22ae,0x22b0,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x22b2,0x22b4,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22b6,0x22b8,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x22ba,0x22bc,0x22be,0x22c0,0x0000,0x0000,0x22c2,0x22c4,
+ 0x0000,0x0000,0x22c6,0x22c8,0x22ca,0x22cc,0x22ce,0x22d0,
+ 0x0000,0x0000,0x22d2,0x22d4,0x22d6,0x22d8,0x22da,0x22dc,
+ 0x0000,0x0000,0x22de,0x22e0,0x22e2,0x22e4,0x22e6,0x22e8,
+ 0x22ea,0x22ec,0x22ee,0x22f0,0x22f2,0x22f4,0x0000,0x0000,
+ 0x22f6,0x22f8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x22fa,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x22fc,0x22fe,0x2300,0x2302,0x2304,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2306,0x2308,0x230a,
+ 0x230c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x230e,0x0000,0x2310,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2312,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2314,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2316,0x0000,0x0000,0x2318,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x231a,0x231c,0x231e,0x2320,0x2322,0x2324,0x2326,0x2328,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x232a,0x232c,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x232e,0x2330,0x0000,0x2332,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2334,0x0000,0x0000,0x2336,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2338,0x233a,0x233c,0x0000,0x0000,0x233e,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2340,0x0000,0x0000,0x2342,0x2344,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2346,0x2348,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x234a,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x234c,0x234e,0x2350,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2352,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2354,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2356,
+ 0x2358,0x0000,0x235a,0x235c,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x235e,0x2360,0x2362,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2364,0x0000,0x2366,0x2368,0x236a,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x236c,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x236e,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2370,0x2372,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0374,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2375,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2377,0x0000,0x0000,
+ 0x0000,0x0000,0x2379,0x0000,0x0000,0x0000,0x0000,0x237b,
+ 0x0000,0x0000,0x0000,0x0000,0x237d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x237f,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2381,0x0000,0x2383,0x2385,0x2387,
+ 0x2389,0x238b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x238d,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x238f,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2391,0x0000,0x0000,
+ 0x0000,0x0000,0x2393,0x0000,0x0000,0x0000,0x0000,0x2395,
+ 0x0000,0x0000,0x0000,0x0000,0x2397,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2399,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x239b,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x039d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x239e,0x0000,
+ 0x23a0,0x0000,0x23a2,0x0000,0x23a4,0x0000,0x23a6,0x0000,
+ 0x0000,0x0000,0x23a8,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x23aa,0x0000,0x23ac,0x0000,0x0000,
+ 0x23ae,0x23b0,0x0000,0x23b2,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x03b4,0x03b5,0x03b6,0x0000,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x0000,0x03c2,0x03c3,0x03c4,0x03c5,
+ 0x03c6,0x03c7,0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,
+ 0x03ce,0x03cf,0x03d0,0x03d1,0x03d2,0x03d3,0x0000,0x03d4,
+ 0x03d5,0x03d6,0x03d7,0x03d8,0x03d9,0x03da,0x03db,0x03dc,
+ 0x03dd,0x03de,0x03df,0x03e0,0x03e1,0x03e2,0x03e3,0x03e4,
+ 0x03e5,0x03e6,0x03e7,0x03e8,0x03e9,0x03ea,0x03eb,0x03ec,
+ 0x03ed,0x03ee,0x03ef,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x03f0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x03f1,0x03f2,0x03f3,0x03f4,0x03f5,
+ 0x03f6,0x03f7,0x03f8,0x03f9,0x03fa,0x03fb,0x03fc,0x03fd,
+ 0x03fe,0x03ff,0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,
+ 0x0406,0x0407,0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,
+ 0x040e,0x040f,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2416,0x2418,0x241a,0x241c,0x241e,0x2420,0x2422,0x2424,
+ 0x2426,0x2428,0x242a,0x242c,0x242e,0x2430,0x2432,0x2434,
+ 0x2436,0x2438,0x243a,0x243c,0x243e,0x2440,0x2442,0x2444,
+ 0x2446,0x2448,0x244a,0x244c,0x244e,0x2450,0x2452,0x2454,
+ 0x2456,0x2458,0x245a,0x245c,0x245e,0x2460,0x2462,0x2464,
+ 0x2466,0x2468,0x246a,0x246c,0x246e,0x2470,0x2472,0x2474,
+ 0x2476,0x2478,0x247a,0x247c,0x247e,0x2480,0x2482,0x2484,
+ 0x2486,0x2488,0x248a,0x248c,0x248e,0x2490,0x2492,0x2494,
+ 0x2496,0x2498,0x249a,0x249c,0x249e,0x24a0,0x24a2,0x24a4,
+ 0x24a6,0x24a8,0x24aa,0x24ac,0x24ae,0x24b0,0x24b2,0x24b4,
+ 0x24b6,0x24b8,0x24ba,0x24bc,0x24be,0x24c0,0x24c2,0x24c4,
+ 0x24c6,0x24c8,0x24ca,0x24cc,0x24ce,0x24d0,0x24d2,0x24d4,
+ 0x24d6,0x24d8,0x24da,0x24dc,0x24de,0x24e0,0x24e2,0x24e4,
+ 0x24e6,0x24e8,0x24ea,0x24ec,0x24ee,0x24f0,0x24f2,0x24f4,
+ 0x24f6,0x24f8,0x24fa,0x24fc,0x24fe,0x2500,0x2502,0x2504,
+ 0x2506,0x2508,0x250a,0x250c,0x250e,0x2510,0x2512,0x2514,
+ 0x2516,0x2518,0x251a,0x251c,0x251e,0x2520,0x2522,0x2524,
+ 0x2526,0x2528,0x252a,0x252c,0x252e,0x2530,0x2532,0x2534,
+ 0x2536,0x2538,0x253a,0x253c,0x253e,0x2540,0x2542,0x2544,
+ 0x2546,0x2548,0x254a,0x254c,0x0000,0x0000,0x0000,0x0000,
+ 0x254e,0x2550,0x2552,0x2554,0x2556,0x2558,0x255a,0x255c,
+ 0x255e,0x2560,0x2562,0x2564,0x2566,0x2568,0x256a,0x256c,
+ 0x256e,0x2570,0x2572,0x2574,0x2576,0x2578,0x257a,0x257c,
+ 0x257e,0x2580,0x2582,0x2584,0x2586,0x2588,0x258a,0x258c,
+ 0x258e,0x2590,0x2592,0x2594,0x2596,0x2598,0x259a,0x259c,
+ 0x259e,0x25a0,0x25a2,0x25a4,0x25a6,0x25a8,0x25aa,0x25ac,
+ 0x25ae,0x25b0,0x25b2,0x25b4,0x25b6,0x25b8,0x25ba,0x25bc,
+ 0x25be,0x25c0,0x25c2,0x25c4,0x25c6,0x25c8,0x25ca,0x25cc,
+ 0x25ce,0x25d0,0x25d2,0x25d4,0x25d6,0x25d8,0x25da,0x25dc,
+ 0x25de,0x25e0,0x25e2,0x25e4,0x25e6,0x25e8,0x25ea,0x25ec,
+ 0x25ee,0x25f0,0x25f2,0x25f4,0x25f6,0x25f8,0x25fa,0x25fc,
+ 0x25fe,0x2600,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2602,0x2604,0x2606,0x2608,0x260a,0x260c,0x260e,0x2610,
+ 0x2612,0x2614,0x2616,0x2618,0x261a,0x261c,0x261e,0x2620,
+ 0x2622,0x2624,0x2626,0x2628,0x262a,0x262c,0x0000,0x0000,
+ 0x262e,0x2630,0x2632,0x2634,0x2636,0x2638,0x0000,0x0000,
+ 0x263a,0x263c,0x263e,0x2640,0x2642,0x2644,0x2646,0x2648,
+ 0x264a,0x264c,0x264e,0x2650,0x2652,0x2654,0x2656,0x2658,
+ 0x265a,0x265c,0x265e,0x2660,0x2662,0x2664,0x2666,0x2668,
+ 0x266a,0x266c,0x266e,0x2670,0x2672,0x2674,0x2676,0x2678,
+ 0x267a,0x267c,0x267e,0x2680,0x2682,0x2684,0x0000,0x0000,
+ 0x2686,0x2688,0x268a,0x268c,0x268e,0x2690,0x0000,0x0000,
+ 0x2692,0x2694,0x2696,0x2698,0x269a,0x269c,0x269e,0x26a0,
+ 0x0000,0x26a2,0x0000,0x26a4,0x0000,0x26a6,0x0000,0x26a8,
+ 0x26aa,0x26ac,0x26ae,0x26b0,0x26b2,0x26b4,0x26b6,0x26b8,
+ 0x26ba,0x26bc,0x26be,0x26c0,0x26c2,0x26c4,0x26c6,0x26c8,
+ 0x26ca,0x06cc,0x26cd,0x06cf,0x26d0,0x06d2,0x26d3,0x06d5,
+ 0x26d6,0x06d8,0x26d9,0x06db,0x26dc,0x06de,0x0000,0x0000,
+ 0x26df,0x26e1,0x26e3,0x26e5,0x26e7,0x26e9,0x26eb,0x26ed,
+ 0x26ef,0x26f1,0x26f3,0x26f5,0x26f7,0x26f9,0x26fb,0x26fd,
+ 0x26ff,0x2701,0x2703,0x2705,0x2707,0x2709,0x270b,0x270d,
+ 0x270f,0x2711,0x2713,0x2715,0x2717,0x2719,0x271b,0x271d,
+ 0x271f,0x2721,0x2723,0x2725,0x2727,0x2729,0x272b,0x272d,
+ 0x272f,0x2731,0x2733,0x2735,0x2737,0x2739,0x273b,0x273d,
+ 0x273f,0x2741,0x2743,0x2745,0x2747,0x0000,0x2749,0x274b,
+ 0x274d,0x274f,0x2751,0x0753,0x2754,0x2756,0x0758,0x2759,
+ 0x275b,0x275d,0x275f,0x2761,0x2763,0x0000,0x2765,0x2767,
+ 0x2769,0x076b,0x276c,0x076e,0x276f,0x2771,0x2773,0x2775,
+ 0x2777,0x2779,0x277b,0x077d,0x0000,0x0000,0x277e,0x2780,
+ 0x2782,0x2784,0x2786,0x0788,0x0000,0x2789,0x278b,0x278d,
+ 0x278f,0x2791,0x2793,0x0795,0x2796,0x2798,0x279a,0x279c,
+ 0x279e,0x27a0,0x27a2,0x07a4,0x27a5,0x27a7,0x07a9,0x07aa,
+ 0x0000,0x0000,0x27ab,0x27ad,0x27af,0x0000,0x27b1,0x27b3,
+ 0x27b5,0x07b7,0x27b8,0x07ba,0x27bb,0x07bd,0x27be,0x0000,
+ 0x07c0,0x07c1,0x07c2,0x07c3,0x07c4,0x07c5,0x07c6,0x07c7,
+ 0x07c8,0x07c9,0x07ca,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x07cb,0x0000,0x0000,0x0000,0x0000,0x0000,0x27cc,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x07ce,0x27cf,0x47d1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07d4,
+ 0x0000,0x0000,0x0000,0x27d5,0x47d7,0x0000,0x27da,0x47dc,
+ 0x0000,0x0000,0x0000,0x0000,0x27df,0x0000,0x27e1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x27e3,
+ 0x27e5,0x27e7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x67e9,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07ed,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x07ee,0x07ef,0x0000,0x0000,0x07f0,0x07f1,0x07f2,0x07f3,
+ 0x07f4,0x07f5,0x07f6,0x07f7,0x07f8,0x07f9,0x07fa,0x07fb,
+ 0x07fc,0x07fd,0x07fe,0x07ff,0x0800,0x0801,0x0802,0x0803,
+ 0x0804,0x0805,0x0806,0x0807,0x0808,0x0809,0x080a,0x0000,
+ 0x080b,0x080c,0x080d,0x080e,0x080f,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2810,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4812,0x4815,0x0818,0x2819,0x0000,0x481b,0x481e,0x0821,
+ 0x0000,0x2822,0x0824,0x0825,0x0826,0x0827,0x0828,0x0829,
+ 0x082a,0x082b,0x082c,0x082d,0x0000,0x082e,0x282f,0x0000,
+ 0x0000,0x0831,0x0832,0x0833,0x0834,0x0835,0x0000,0x0000,
+ 0x2836,0x4838,0x283b,0x0000,0x083d,0x0000,0x083e,0x0000,
+ 0x083f,0x0000,0x0840,0x0841,0x0842,0x0843,0x0000,0x0844,
+ 0x0845,0x0846,0x0000,0x0847,0x0848,0x0849,0x084a,0x084b,
+ 0x084c,0x084d,0x0000,0x484e,0x0851,0x0852,0x0853,0x0854,
+ 0x0855,0x0000,0x0000,0x0000,0x0000,0x0856,0x0857,0x0858,
+ 0x0859,0x085a,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x485b,0x485e,0x4861,0x4864,0x4867,
+ 0x486a,0x486d,0x4870,0x4873,0x4876,0x4879,0x487c,0x287f,
+ 0x0881,0x2882,0x4884,0x2887,0x0889,0x288a,0x488c,0x688f,
+ 0x2893,0x0895,0x2896,0x4898,0x089b,0x089c,0x089d,0x089e,
+ 0x089f,0x28a0,0x48a2,0x28a5,0x08a7,0x28a8,0x48aa,0x68ad,
+ 0x28b1,0x08b3,0x28b4,0x48b6,0x08b9,0x08ba,0x08bb,0x08bc,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x28bd,0x28bf,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x28c1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x28c3,0x28c5,0x28c7,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28c9,0x0000,0x0000,0x0000,
+ 0x0000,0x28cb,0x0000,0x0000,0x28cd,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28cf,0x0000,0x28d1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28d3,0x48d5,0x0000,0x28d8,
+ 0x48da,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x28dd,0x0000,0x0000,0x28df,0x0000,0x0000,0x28e1,
+ 0x0000,0x28e3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x28e5,0x0000,0x28e7,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x28e9,0x28eb,0x28ed,
+ 0x28ef,0x28f1,0x0000,0x0000,0x28f3,0x28f5,0x0000,0x0000,
+ 0x28f7,0x28f9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x28fb,0x28fd,0x0000,0x0000,0x28ff,0x2901,0x0000,0x0000,
+ 0x2903,0x2905,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2907,0x2909,0x290b,0x290d,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x290f,0x2911,0x2913,0x2915,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2917,0x2919,0x291b,0x291d,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x091f,0x0920,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0921,0x0922,0x0923,0x0924,0x0925,0x0926,0x0927,0x0928,
+ 0x0929,0x292a,0x292c,0x292e,0x2930,0x2932,0x2934,0x2936,
+ 0x2938,0x293a,0x293c,0x293e,0x4940,0x4943,0x4946,0x4949,
+ 0x494c,0x494f,0x4952,0x4955,0x4958,0x695b,0x695f,0x6963,
+ 0x6967,0x696b,0x696f,0x6973,0x6977,0x697b,0x697f,0x6983,
+ 0x2987,0x2989,0x298b,0x298d,0x298f,0x2991,0x2993,0x2995,
+ 0x2997,0x4999,0x499c,0x499f,0x49a2,0x49a5,0x49a8,0x49ab,
+ 0x49ae,0x49b1,0x49b4,0x49b7,0x49ba,0x49bd,0x49c0,0x49c3,
+ 0x49c6,0x49c9,0x49cc,0x49cf,0x49d2,0x49d5,0x49d8,0x49db,
+ 0x49de,0x49e1,0x49e4,0x49e7,0x49ea,0x49ed,0x49f0,0x49f3,
+ 0x49f6,0x49f9,0x49fc,0x49ff,0x4a02,0x4a05,0x0a08,0x0a09,
+ 0x0a0a,0x0a0b,0x0a0c,0x0a0d,0x0a0e,0x0a0f,0x0a10,0x0a11,
+ 0x0a12,0x0a13,0x0a14,0x0a15,0x0a16,0x0a17,0x0a18,0x0a19,
+ 0x0a1a,0x0a1b,0x0a1c,0x0a1d,0x0a1e,0x0a1f,0x0a20,0x0a21,
+ 0x0a22,0x0a23,0x0a24,0x0a25,0x0a26,0x0a27,0x0a28,0x0a29,
+ 0x0a2a,0x0a2b,0x0a2c,0x0a2d,0x0a2e,0x0a2f,0x0a30,0x0a31,
+ 0x0a32,0x0a33,0x0a34,0x0a35,0x0a36,0x0a37,0x0a38,0x0a39,
+ 0x0a3a,0x0a3b,0x0a3c,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x6a3d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x4a41,0x2a44,0x4a46,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2a49,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0a4b,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0a4c,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0a4d,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0a4e,0x0a4f,0x0a50,0x0a51,0x0a52,0x0a53,0x0a54,0x0a55,
+ 0x0a56,0x0a57,0x0a58,0x0a59,0x0a5a,0x0a5b,0x0a5c,0x0a5d,
+ 0x0a5e,0x0a5f,0x0a60,0x0a61,0x0a62,0x0a63,0x0a64,0x0a65,
+ 0x0a66,0x0a67,0x0a68,0x0a69,0x0a6a,0x0a6b,0x0a6c,0x0a6d,
+ 0x0a6e,0x0a6f,0x0a70,0x0a71,0x0a72,0x0a73,0x0a74,0x0a75,
+ 0x0a76,0x0a77,0x0a78,0x0a79,0x0a7a,0x0a7b,0x0a7c,0x0a7d,
+ 0x0a7e,0x0a7f,0x0a80,0x0a81,0x0a82,0x0a83,0x0a84,0x0a85,
+ 0x0a86,0x0a87,0x0a88,0x0a89,0x0a8a,0x0a8b,0x0a8c,0x0a8d,
+ 0x0a8e,0x0a8f,0x0a90,0x0a91,0x0a92,0x0a93,0x0a94,0x0a95,
+ 0x0a96,0x0a97,0x0a98,0x0a99,0x0a9a,0x0a9b,0x0a9c,0x0a9d,
+ 0x0a9e,0x0a9f,0x0aa0,0x0aa1,0x0aa2,0x0aa3,0x0aa4,0x0aa5,
+ 0x0aa6,0x0aa7,0x0aa8,0x0aa9,0x0aaa,0x0aab,0x0aac,0x0aad,
+ 0x0aae,0x0aaf,0x0ab0,0x0ab1,0x0ab2,0x0ab3,0x0ab4,0x0ab5,
+ 0x0ab6,0x0ab7,0x0ab8,0x0ab9,0x0aba,0x0abb,0x0abc,0x0abd,
+ 0x0abe,0x0abf,0x0ac0,0x0ac1,0x0ac2,0x0ac3,0x0ac4,0x0ac5,
+ 0x0ac6,0x0ac7,0x0ac8,0x0ac9,0x0aca,0x0acb,0x0acc,0x0acd,
+ 0x0ace,0x0acf,0x0ad0,0x0ad1,0x0ad2,0x0ad3,0x0ad4,0x0ad5,
+ 0x0ad6,0x0ad7,0x0ad8,0x0ad9,0x0ada,0x0adb,0x0adc,0x0add,
+ 0x0ade,0x0adf,0x0ae0,0x0ae1,0x0ae2,0x0ae3,0x0ae4,0x0ae5,
+ 0x0ae6,0x0ae7,0x0ae8,0x0ae9,0x0aea,0x0aeb,0x0aec,0x0aed,
+ 0x0aee,0x0aef,0x0af0,0x0af1,0x0af2,0x0af3,0x0af4,0x0af5,
+ 0x0af6,0x0af7,0x0af8,0x0af9,0x0afa,0x0afb,0x0afc,0x0afd,
+ 0x0afe,0x0aff,0x0b00,0x0b01,0x0b02,0x0b03,0x0b04,0x0b05,
+ 0x0b06,0x0b07,0x0b08,0x0b09,0x0b0a,0x0b0b,0x0b0c,0x0b0d,
+ 0x0b0e,0x0b0f,0x0b10,0x0b11,0x0b12,0x0b13,0x0b14,0x0b15,
+ 0x0b16,0x0b17,0x0b18,0x0b19,0x0b1a,0x0b1b,0x0b1c,0x0b1d,
+ 0x0b1e,0x0b1f,0x0b20,0x0b21,0x0b22,0x0b23,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0b24,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0b25,0x0000,
+ 0x0b26,0x0b27,0x0b28,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b29,0x0000,0x2b2b,0x0000,
+ 0x2b2d,0x0000,0x2b2f,0x0000,0x2b31,0x0000,0x2b33,0x0000,
+ 0x2b35,0x0000,0x2b37,0x0000,0x2b39,0x0000,0x2b3b,0x0000,
+ 0x2b3d,0x0000,0x2b3f,0x0000,0x0000,0x2b41,0x0000,0x2b43,
+ 0x0000,0x2b45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2b47,0x2b49,0x0000,0x2b4b,0x2b4d,0x0000,0x2b4f,0x2b51,
+ 0x0000,0x2b53,0x2b55,0x0000,0x2b57,0x2b59,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b5b,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2b5d,0x2b5f,0x0000,0x2b61,0x2b63,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b65,0x0000,0x2b67,0x0000,
+ 0x2b69,0x0000,0x2b6b,0x0000,0x2b6d,0x0000,0x2b6f,0x0000,
+ 0x2b71,0x0000,0x2b73,0x0000,0x2b75,0x0000,0x2b77,0x0000,
+ 0x2b79,0x0000,0x2b7b,0x0000,0x0000,0x2b7d,0x0000,0x2b7f,
+ 0x0000,0x2b81,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2b83,0x2b85,0x0000,0x2b87,0x2b89,0x0000,0x2b8b,0x2b8d,
+ 0x0000,0x2b8f,0x2b91,0x0000,0x2b93,0x2b95,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b97,0x0000,0x0000,0x2b99,
+ 0x2b9b,0x2b9d,0x2b9f,0x0000,0x0000,0x0000,0x2ba1,0x2ba3,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0ba5,0x0ba6,0x0ba7,0x0ba8,0x0ba9,0x0baa,0x0bab,
+ 0x0bac,0x0bad,0x0bae,0x0baf,0x0bb0,0x0bb1,0x0bb2,0x0bb3,
+ 0x0bb4,0x0bb5,0x0bb6,0x0bb7,0x0bb8,0x0bb9,0x0bba,0x0bbb,
+ 0x0bbc,0x0bbd,0x0bbe,0x0bbf,0x0bc0,0x0bc1,0x0bc2,0x0bc3,
+ 0x0bc4,0x0bc5,0x0bc6,0x0bc7,0x0bc8,0x0bc9,0x0bca,0x0bcb,
+ 0x0bcc,0x0bcd,0x0bce,0x0bcf,0x0bd0,0x0bd1,0x0bd2,0x0bd3,
+ 0x0bd4,0x0bd5,0x0bd6,0x0bd7,0x0bd8,0x0bd9,0x0bda,0x0bdb,
+ 0x0bdc,0x0bdd,0x0bde,0x0bdf,0x0be0,0x0be1,0x0be2,0x0be3,
+ 0x0be4,0x0be5,0x0be6,0x0be7,0x0be8,0x0be9,0x0bea,0x0beb,
+ 0x0bec,0x0bed,0x0bee,0x0bef,0x0bf0,0x0bf1,0x0bf2,0x0bf3,
+ 0x0bf4,0x0bf5,0x0bf6,0x0bf7,0x0bf8,0x0bf9,0x0bfa,0x0bfb,
+ 0x0bfc,0x0bfd,0x0bfe,0x0bff,0x0c00,0x0c01,0x0c02,0x0000,
+ 0x0000,0x0000,0x0c03,0x0c04,0x0c05,0x0c06,0x0c07,0x0c08,
+ 0x0c09,0x0c0a,0x0c0b,0x0c0c,0x0c0d,0x0c0e,0x0c0f,0x0c10,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4c11,0x4c14,0x4c17,0x4c1a,0x4c1d,0x4c20,0x4c23,0x4c26,
+ 0x4c29,0x4c2c,0x4c2f,0x4c32,0x4c35,0x4c38,0x6c3b,0x6c3f,
+ 0x6c43,0x6c47,0x6c4b,0x6c4f,0x6c53,0x6c57,0x6c5b,0x6c5f,
+ 0x6c63,0x6c67,0x6c6b,0x6c6f,0x6c73,0xcc77,0xac7e,0x0000,
+ 0x4c84,0x4c87,0x4c8a,0x4c8d,0x4c90,0x4c93,0x4c96,0x4c99,
+ 0x4c9c,0x4c9f,0x4ca2,0x4ca5,0x4ca8,0x4cab,0x4cae,0x4cb1,
+ 0x4cb4,0x4cb7,0x4cba,0x4cbd,0x4cc0,0x4cc3,0x4cc6,0x4cc9,
+ 0x4ccc,0x4ccf,0x4cd2,0x4cd5,0x4cd8,0x4cdb,0x4cde,0x4ce1,
+ 0x4ce4,0x4ce7,0x4cea,0x4ced,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4cf0,0x2cf3,0x2cf5,0x2cf7,0x2cf9,0x2cfb,0x2cfd,0x2cff,
+ 0x2d01,0x2d03,0x2d05,0x2d07,0x2d09,0x2d0b,0x2d0d,0x2d0f,
+ 0x0d11,0x0d12,0x0d13,0x0d14,0x0d15,0x0d16,0x0d17,0x0d18,
+ 0x0d19,0x0d1a,0x0d1b,0x0d1c,0x0d1d,0x0d1e,0x2d1f,0x2d21,
+ 0x2d23,0x2d25,0x2d27,0x2d29,0x2d2b,0x2d2d,0x2d2f,0x2d31,
+ 0x2d33,0x2d35,0x2d37,0x2d39,0x8d3b,0x6d40,0x2d44,0x0000,
+ 0x0d46,0x0d47,0x0d48,0x0d49,0x0d4a,0x0d4b,0x0d4c,0x0d4d,
+ 0x0d4e,0x0d4f,0x0d50,0x0d51,0x0d52,0x0d53,0x0d54,0x0d55,
+ 0x0d56,0x0d57,0x0d58,0x0d59,0x0d5a,0x0d5b,0x0d5c,0x0d5d,
+ 0x0d5e,0x0d5f,0x0d60,0x0d61,0x0d62,0x0d63,0x0d64,0x0d65,
+ 0x0d66,0x0d67,0x0d68,0x0d69,0x0d6a,0x0d6b,0x0d6c,0x0d6d,
+ 0x0d6e,0x0d6f,0x0d70,0x0d71,0x0d72,0x0d73,0x0d74,0x0d75,
+ 0x0d76,0x2d77,0x2d79,0x2d7b,0x2d7d,0x2d7f,0x2d81,0x2d83,
+ 0x2d85,0x2d87,0x2d89,0x2d8b,0x2d8d,0x2d8f,0x2d91,0x2d93,
+ 0x2d95,0x2d97,0x2d99,0x2d9b,0x2d9d,0x2d9f,0x2da1,0x2da3,
+ 0x2da5,0x4da7,0x4daa,0x4dad,0x2db0,0x4db2,0x2db5,0x4db7,
+ 0x0dba,0x0dbb,0x0dbc,0x0dbd,0x0dbe,0x0dbf,0x0dc0,0x0dc1,
+ 0x0dc2,0x0dc3,0x0dc4,0x0dc5,0x0dc6,0x0dc7,0x0dc8,0x0dc9,
+ 0x0dca,0x0dcb,0x0dcc,0x0dcd,0x0dce,0x0dcf,0x0dd0,0x0dd1,
+ 0x0dd2,0x0dd3,0x0dd4,0x0dd5,0x0dd6,0x0dd7,0x0dd8,0x0dd9,
+ 0x0dda,0x0ddb,0x0ddc,0x0ddd,0x0dde,0x0ddf,0x0de0,0x0de1,
+ 0x0de2,0x0de3,0x0de4,0x0de5,0x0de6,0x0de7,0x0de8,0x0000,
+ 0x6de9,0x6ded,0x6df1,0x4df5,0x6df8,0x4dfc,0x4dff,0x8e02,
+ 0x6e07,0x4e0b,0x4e0e,0x4e11,0x6e14,0x6e18,0x4e1c,0x4e1f,
+ 0x2e22,0x4e24,0x6e27,0x6e2b,0x2e2f,0x8e31,0xae36,0x8e3c,
+ 0x4e41,0x8e44,0x8e49,0x6e4e,0x4e52,0x4e55,0x4e58,0x6e5b,
+ 0x8e5f,0x6e64,0x4e68,0x4e6b,0x4e6e,0x2e71,0x2e73,0x2e75,
+ 0x2e77,0x4e79,0x4e7c,0x8e7f,0x4e84,0x6e87,0x8e8b,0x4e90,
+ 0x2e93,0x2e95,0x8e97,0x6e9c,0x8ea0,0x4ea5,0x8ea8,0x2ead,
+ 0x4eaf,0x4eb2,0x4eb5,0x4eb8,0x4ebb,0x6ebe,0x4ec2,0x2ec5,
+ 0x4ec7,0x4eca,0x4ecd,0x6ed0,0x4ed4,0x4ed7,0x4eda,0x8edd,
+ 0x6ee2,0x2ee6,0x8ee8,0x2eed,0x6eef,0x6ef3,0x4ef7,0x4efa,
+ 0x4efd,0x6f00,0x2f04,0x4f06,0x6f09,0x2f0d,0x8f0f,0x4f14,
+ 0x2f17,0x2f19,0x2f1b,0x2f1d,0x2f1f,0x2f21,0x2f23,0x2f25,
+ 0x2f27,0x2f29,0x4f2b,0x4f2e,0x4f31,0x4f34,0x4f37,0x4f3a,
+ 0x4f3d,0x4f40,0x4f43,0x4f46,0x4f49,0x4f4c,0x4f4f,0x4f52,
+ 0x4f55,0x4f58,0x2f5b,0x2f5d,0x4f5f,0x2f62,0x2f64,0x2f66,
+ 0x4f68,0x4f6b,0x2f6e,0x2f70,0x2f72,0x2f74,0x2f76,0x6f78,
+ 0x2f7c,0x2f7e,0x2f80,0x2f82,0x2f84,0x2f86,0x2f88,0x2f8a,
+ 0x4f8c,0x6f8f,0x2f93,0x2f95,0x2f97,0x2f99,0x2f9b,0x2f9d,
+ 0x2f9f,0x4fa1,0x4fa4,0x4fa7,0x4faa,0x2fad,0x2faf,0x2fb1,
+ 0x2fb3,0x2fb5,0x2fb7,0x2fb9,0x2fbb,0x2fbd,0x2fbf,0x4fc1,
+ 0x4fc4,0x2fc7,0x4fc9,0x4fcc,0x4fcf,0x2fd2,0x4fd4,0x4fd7,
+ 0x6fda,0x2fde,0x4fe0,0x4fe3,0x4fe6,0x4fe9,0x8fec,0xaff1,
+ 0x2ff7,0x2ff9,0x2ffb,0x2ffd,0x2fff,0x3001,0x3003,0x3005,
+ 0x3007,0x3009,0x300b,0x300d,0x300f,0x3011,0x3013,0x3015,
+ 0x3017,0x3019,0x701b,0x301f,0x3021,0x3023,0x7025,0x5029,
+ 0x302c,0x302e,0x3030,0x3032,0x3034,0x3036,0x3038,0x303a,
+ 0x303c,0x303e,0x5040,0x3043,0x3045,0x5047,0x504a,0x304d,
+ 0x704f,0x5053,0x3056,0x3058,0x305a,0x305c,0x505e,0x5061,
+ 0x3064,0x3066,0x3068,0x306a,0x306c,0x306e,0x3070,0x3072,
+ 0x3074,0x5076,0x5079,0x507c,0x507f,0x5082,0x5085,0x5088,
+ 0x508b,0x508e,0x5091,0x5094,0x5097,0x509a,0x509d,0x50a0,
+ 0x50a3,0x50a6,0x50a9,0x50ac,0x50af,0x50b2,0x50b5,0x50b8,
+};
+
+ /* BMP low decompositions */
+static unsigned short ucs4_dbmplotab[4283] = {
+ 0x0020,0x0020,0x0308,0x0061,0x0020,0x0304,0x0032,0x0033,
+ 0x0020,0x0301,0x03bc,0x0020,0x0327,0x0031,0x006f,0x0031,
+ 0x2044,0x0034,0x0031,0x2044,0x0032,0x0033,0x2044,0x0034,
+ 0x0041,0x0300,0x0041,0x0301,0x0041,0x0302,0x0041,0x0303,
+ 0x0041,0x0308,0x0041,0x030a,0x0043,0x0327,0x0045,0x0300,
+ 0x0045,0x0301,0x0045,0x0302,0x0045,0x0308,0x0049,0x0300,
+ 0x0049,0x0301,0x0049,0x0302,0x0049,0x0308,0x004e,0x0303,
+ 0x004f,0x0300,0x004f,0x0301,0x004f,0x0302,0x004f,0x0303,
+ 0x004f,0x0308,0x0055,0x0300,0x0055,0x0301,0x0055,0x0302,
+ 0x0055,0x0308,0x0059,0x0301,0x0061,0x0300,0x0061,0x0301,
+ 0x0061,0x0302,0x0061,0x0303,0x0061,0x0308,0x0061,0x030a,
+ 0x0063,0x0327,0x0065,0x0300,0x0065,0x0301,0x0065,0x0302,
+ 0x0065,0x0308,0x0069,0x0300,0x0069,0x0301,0x0069,0x0302,
+ 0x0069,0x0308,0x006e,0x0303,0x006f,0x0300,0x006f,0x0301,
+ 0x006f,0x0302,0x006f,0x0303,0x006f,0x0308,0x0075,0x0300,
+ 0x0075,0x0301,0x0075,0x0302,0x0075,0x0308,0x0079,0x0301,
+ 0x0079,0x0308,0x0041,0x0304,0x0061,0x0304,0x0041,0x0306,
+ 0x0061,0x0306,0x0041,0x0328,0x0061,0x0328,0x0043,0x0301,
+ 0x0063,0x0301,0x0043,0x0302,0x0063,0x0302,0x0043,0x0307,
+ 0x0063,0x0307,0x0043,0x030c,0x0063,0x030c,0x0044,0x030c,
+ 0x0064,0x030c,0x0045,0x0304,0x0065,0x0304,0x0045,0x0306,
+ 0x0065,0x0306,0x0045,0x0307,0x0065,0x0307,0x0045,0x0328,
+ 0x0065,0x0328,0x0045,0x030c,0x0065,0x030c,0x0047,0x0302,
+ 0x0067,0x0302,0x0047,0x0306,0x0067,0x0306,0x0047,0x0307,
+ 0x0067,0x0307,0x0047,0x0327,0x0067,0x0327,0x0048,0x0302,
+ 0x0068,0x0302,0x0049,0x0303,0x0069,0x0303,0x0049,0x0304,
+ 0x0069,0x0304,0x0049,0x0306,0x0069,0x0306,0x0049,0x0328,
+ 0x0069,0x0328,0x0049,0x0307,0x0049,0x004a,0x0069,0x006a,
+ 0x004a,0x0302,0x006a,0x0302,0x004b,0x0327,0x006b,0x0327,
+ 0x004c,0x0301,0x006c,0x0301,0x004c,0x0327,0x006c,0x0327,
+ 0x004c,0x030c,0x006c,0x030c,0x004c,0x00b7,0x006c,0x00b7,
+ 0x004e,0x0301,0x006e,0x0301,0x004e,0x0327,0x006e,0x0327,
+ 0x004e,0x030c,0x006e,0x030c,0x02bc,0x006e,0x004f,0x0304,
+ 0x006f,0x0304,0x004f,0x0306,0x006f,0x0306,0x004f,0x030b,
+ 0x006f,0x030b,0x0052,0x0301,0x0072,0x0301,0x0052,0x0327,
+ 0x0072,0x0327,0x0052,0x030c,0x0072,0x030c,0x0053,0x0301,
+ 0x0073,0x0301,0x0053,0x0302,0x0073,0x0302,0x0053,0x0327,
+ 0x0073,0x0327,0x0053,0x030c,0x0073,0x030c,0x0054,0x0327,
+ 0x0074,0x0327,0x0054,0x030c,0x0074,0x030c,0x0055,0x0303,
+ 0x0075,0x0303,0x0055,0x0304,0x0075,0x0304,0x0055,0x0306,
+ 0x0075,0x0306,0x0055,0x030a,0x0075,0x030a,0x0055,0x030b,
+ 0x0075,0x030b,0x0055,0x0328,0x0075,0x0328,0x0057,0x0302,
+ 0x0077,0x0302,0x0059,0x0302,0x0079,0x0302,0x0059,0x0308,
+ 0x005a,0x0301,0x007a,0x0301,0x005a,0x0307,0x007a,0x0307,
+ 0x005a,0x030c,0x007a,0x030c,0x0073,0x004f,0x031b,0x006f,
+ 0x031b,0x0055,0x031b,0x0075,0x031b,0x0044,0x017d,0x0044,
+ 0x017e,0x0064,0x017e,0x004c,0x004a,0x004c,0x006a,0x006c,
+ 0x006a,0x004e,0x004a,0x004e,0x006a,0x006e,0x006a,0x0041,
+ 0x030c,0x0061,0x030c,0x0049,0x030c,0x0069,0x030c,0x004f,
+ 0x030c,0x006f,0x030c,0x0055,0x030c,0x0075,0x030c,0x00dc,
+ 0x0304,0x00fc,0x0304,0x00dc,0x0301,0x00fc,0x0301,0x00dc,
+ 0x030c,0x00fc,0x030c,0x00dc,0x0300,0x00fc,0x0300,0x00c4,
+ 0x0304,0x00e4,0x0304,0x0226,0x0304,0x0227,0x0304,0x00c6,
+ 0x0304,0x00e6,0x0304,0x0047,0x030c,0x0067,0x030c,0x004b,
+ 0x030c,0x006b,0x030c,0x004f,0x0328,0x006f,0x0328,0x01ea,
+ 0x0304,0x01eb,0x0304,0x01b7,0x030c,0x0292,0x030c,0x006a,
+ 0x030c,0x0044,0x005a,0x0044,0x007a,0x0064,0x007a,0x0047,
+ 0x0301,0x0067,0x0301,0x004e,0x0300,0x006e,0x0300,0x00c5,
+ 0x0301,0x00e5,0x0301,0x00c6,0x0301,0x00e6,0x0301,0x00d8,
+ 0x0301,0x00f8,0x0301,0x0041,0x030f,0x0061,0x030f,0x0041,
+ 0x0311,0x0061,0x0311,0x0045,0x030f,0x0065,0x030f,0x0045,
+ 0x0311,0x0065,0x0311,0x0049,0x030f,0x0069,0x030f,0x0049,
+ 0x0311,0x0069,0x0311,0x004f,0x030f,0x006f,0x030f,0x004f,
+ 0x0311,0x006f,0x0311,0x0052,0x030f,0x0072,0x030f,0x0052,
+ 0x0311,0x0072,0x0311,0x0055,0x030f,0x0075,0x030f,0x0055,
+ 0x0311,0x0075,0x0311,0x0053,0x0326,0x0073,0x0326,0x0054,
+ 0x0326,0x0074,0x0326,0x0048,0x030c,0x0068,0x030c,0x0041,
+ 0x0307,0x0061,0x0307,0x0045,0x0327,0x0065,0x0327,0x00d6,
+ 0x0304,0x00f6,0x0304,0x00d5,0x0304,0x00f5,0x0304,0x004f,
+ 0x0307,0x006f,0x0307,0x022e,0x0304,0x022f,0x0304,0x0059,
+ 0x0304,0x0079,0x0304,0x0068,0x0266,0x006a,0x0072,0x0279,
+ 0x027b,0x0281,0x0077,0x0079,0x0020,0x0306,0x0020,0x0307,
+ 0x0020,0x030a,0x0020,0x0328,0x0020,0x0303,0x0020,0x030b,
+ 0x0263,0x006c,0x0073,0x0078,0x0295,0x0300,0x0301,0x0313,
+ 0x0308,0x0301,0x02b9,0x0020,0x0345,0x003b,0x0020,0x0301,
+ 0x00a8,0x0301,0x0391,0x0301,0x00b7,0x0395,0x0301,0x0397,
+ 0x0301,0x0399,0x0301,0x039f,0x0301,0x03a5,0x0301,0x03a9,
+ 0x0301,0x03ca,0x0301,0x0399,0x0308,0x03a5,0x0308,0x03b1,
+ 0x0301,0x03b5,0x0301,0x03b7,0x0301,0x03b9,0x0301,0x03cb,
+ 0x0301,0x03b9,0x0308,0x03c5,0x0308,0x03bf,0x0301,0x03c5,
+ 0x0301,0x03c9,0x0301,0x03b2,0x03b8,0x03a5,0x03d2,0x0301,
+ 0x03d2,0x0308,0x03c6,0x03c0,0x03ba,0x03c1,0x03c2,0x0398,
+ 0x03b5,0x03a3,0x0415,0x0300,0x0415,0x0308,0x0413,0x0301,
+ 0x0406,0x0308,0x041a,0x0301,0x0418,0x0300,0x0423,0x0306,
+ 0x0418,0x0306,0x0438,0x0306,0x0435,0x0300,0x0435,0x0308,
+ 0x0433,0x0301,0x0456,0x0308,0x043a,0x0301,0x0438,0x0300,
+ 0x0443,0x0306,0x0474,0x030f,0x0475,0x030f,0x0416,0x0306,
+ 0x0436,0x0306,0x0410,0x0306,0x0430,0x0306,0x0410,0x0308,
+ 0x0430,0x0308,0x0415,0x0306,0x0435,0x0306,0x04d8,0x0308,
+ 0x04d9,0x0308,0x0416,0x0308,0x0436,0x0308,0x0417,0x0308,
+ 0x0437,0x0308,0x0418,0x0304,0x0438,0x0304,0x0418,0x0308,
+ 0x0438,0x0308,0x041e,0x0308,0x043e,0x0308,0x04e8,0x0308,
+ 0x04e9,0x0308,0x042d,0x0308,0x044d,0x0308,0x0423,0x0304,
+ 0x0443,0x0304,0x0423,0x0308,0x0443,0x0308,0x0423,0x030b,
+ 0x0443,0x030b,0x0427,0x0308,0x0447,0x0308,0x042b,0x0308,
+ 0x044b,0x0308,0x0565,0x0582,0x0627,0x0653,0x0627,0x0654,
+ 0x0648,0x0654,0x0627,0x0655,0x064a,0x0654,0x0627,0x0674,
+ 0x0648,0x0674,0x06c7,0x0674,0x064a,0x0674,0x06d5,0x0654,
+ 0x06c1,0x0654,0x06d2,0x0654,0x0928,0x093c,0x0930,0x093c,
+ 0x0933,0x093c,0x0915,0x093c,0x0916,0x093c,0x0917,0x093c,
+ 0x091c,0x093c,0x0921,0x093c,0x0922,0x093c,0x092b,0x093c,
+ 0x092f,0x093c,0x09c7,0x09be,0x09c7,0x09d7,0x09a1,0x09bc,
+ 0x09a2,0x09bc,0x09af,0x09bc,0x0a32,0x0a3c,0x0a38,0x0a3c,
+ 0x0a16,0x0a3c,0x0a17,0x0a3c,0x0a1c,0x0a3c,0x0a2b,0x0a3c,
+ 0x0b47,0x0b56,0x0b47,0x0b3e,0x0b47,0x0b57,0x0b21,0x0b3c,
+ 0x0b22,0x0b3c,0x0b92,0x0bd7,0x0bc6,0x0bbe,0x0bc7,0x0bbe,
+ 0x0bc6,0x0bd7,0x0c46,0x0c56,0x0cbf,0x0cd5,0x0cc6,0x0cd5,
+ 0x0cc6,0x0cd6,0x0cc6,0x0cc2,0x0cca,0x0cd5,0x0d46,0x0d3e,
+ 0x0d47,0x0d3e,0x0d46,0x0d57,0x0dd9,0x0dca,0x0dd9,0x0dcf,
+ 0x0ddc,0x0dca,0x0dd9,0x0ddf,0x0e4d,0x0e32,0x0ecd,0x0eb2,
+ 0x0eab,0x0e99,0x0eab,0x0ea1,0x0f0b,0x0f42,0x0fb7,0x0f4c,
+ 0x0fb7,0x0f51,0x0fb7,0x0f56,0x0fb7,0x0f5b,0x0fb7,0x0f40,
+ 0x0fb5,0x0f71,0x0f72,0x0f71,0x0f74,0x0fb2,0x0f80,0x0fb2,
+ 0x0f81,0x0fb3,0x0f80,0x0fb3,0x0f81,0x0f71,0x0f80,0x0f92,
+ 0x0fb7,0x0f9c,0x0fb7,0x0fa1,0x0fb7,0x0fa6,0x0fb7,0x0fab,
+ 0x0fb7,0x0f90,0x0fb5,0x1025,0x102e,0x10dc,0x1b05,0x1b35,
+ 0x1b07,0x1b35,0x1b09,0x1b35,0x1b0b,0x1b35,0x1b0d,0x1b35,
+ 0x1b11,0x1b35,0x1b3a,0x1b35,0x1b3c,0x1b35,0x1b3e,0x1b35,
+ 0x1b3f,0x1b35,0x1b42,0x1b35,0x0041,0x00c6,0x0042,0x0044,
+ 0x0045,0x018e,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0222,0x0050,0x0052,0x0054,0x0055,
+ 0x0057,0x0061,0x0250,0x0251,0x1d02,0x0062,0x0064,0x0065,
+ 0x0259,0x025b,0x025c,0x0067,0x006b,0x006d,0x014b,0x006f,
+ 0x0254,0x1d16,0x1d17,0x0070,0x0074,0x0075,0x1d1d,0x026f,
+ 0x0076,0x1d25,0x03b2,0x03b3,0x03b4,0x03c6,0x03c7,0x0069,
+ 0x0072,0x0075,0x0076,0x03b2,0x03b3,0x03c1,0x03c6,0x03c7,
+ 0x043d,0x0252,0x0063,0x0255,0x00f0,0x025c,0x0066,0x025f,
+ 0x0261,0x0265,0x0268,0x0269,0x026a,0x1d7b,0x029d,0x026d,
+ 0x1d85,0x029f,0x0271,0x0270,0x0272,0x0273,0x0274,0x0275,
+ 0x0278,0x0282,0x0283,0x01ab,0x0289,0x028a,0x1d1c,0x028b,
+ 0x028c,0x007a,0x0290,0x0291,0x0292,0x03b8,0x0041,0x0325,
+ 0x0061,0x0325,0x0042,0x0307,0x0062,0x0307,0x0042,0x0323,
+ 0x0062,0x0323,0x0042,0x0331,0x0062,0x0331,0x00c7,0x0301,
+ 0x00e7,0x0301,0x0044,0x0307,0x0064,0x0307,0x0044,0x0323,
+ 0x0064,0x0323,0x0044,0x0331,0x0064,0x0331,0x0044,0x0327,
+ 0x0064,0x0327,0x0044,0x032d,0x0064,0x032d,0x0112,0x0300,
+ 0x0113,0x0300,0x0112,0x0301,0x0113,0x0301,0x0045,0x032d,
+ 0x0065,0x032d,0x0045,0x0330,0x0065,0x0330,0x0228,0x0306,
+ 0x0229,0x0306,0x0046,0x0307,0x0066,0x0307,0x0047,0x0304,
+ 0x0067,0x0304,0x0048,0x0307,0x0068,0x0307,0x0048,0x0323,
+ 0x0068,0x0323,0x0048,0x0308,0x0068,0x0308,0x0048,0x0327,
+ 0x0068,0x0327,0x0048,0x032e,0x0068,0x032e,0x0049,0x0330,
+ 0x0069,0x0330,0x00cf,0x0301,0x00ef,0x0301,0x004b,0x0301,
+ 0x006b,0x0301,0x004b,0x0323,0x006b,0x0323,0x004b,0x0331,
+ 0x006b,0x0331,0x004c,0x0323,0x006c,0x0323,0x1e36,0x0304,
+ 0x1e37,0x0304,0x004c,0x0331,0x006c,0x0331,0x004c,0x032d,
+ 0x006c,0x032d,0x004d,0x0301,0x006d,0x0301,0x004d,0x0307,
+ 0x006d,0x0307,0x004d,0x0323,0x006d,0x0323,0x004e,0x0307,
+ 0x006e,0x0307,0x004e,0x0323,0x006e,0x0323,0x004e,0x0331,
+ 0x006e,0x0331,0x004e,0x032d,0x006e,0x032d,0x00d5,0x0301,
+ 0x00f5,0x0301,0x00d5,0x0308,0x00f5,0x0308,0x014c,0x0300,
+ 0x014d,0x0300,0x014c,0x0301,0x014d,0x0301,0x0050,0x0301,
+ 0x0070,0x0301,0x0050,0x0307,0x0070,0x0307,0x0052,0x0307,
+ 0x0072,0x0307,0x0052,0x0323,0x0072,0x0323,0x1e5a,0x0304,
+ 0x1e5b,0x0304,0x0052,0x0331,0x0072,0x0331,0x0053,0x0307,
+ 0x0073,0x0307,0x0053,0x0323,0x0073,0x0323,0x015a,0x0307,
+ 0x015b,0x0307,0x0160,0x0307,0x0161,0x0307,0x1e62,0x0307,
+ 0x1e63,0x0307,0x0054,0x0307,0x0074,0x0307,0x0054,0x0323,
+ 0x0074,0x0323,0x0054,0x0331,0x0074,0x0331,0x0054,0x032d,
+ 0x0074,0x032d,0x0055,0x0324,0x0075,0x0324,0x0055,0x0330,
+ 0x0075,0x0330,0x0055,0x032d,0x0075,0x032d,0x0168,0x0301,
+ 0x0169,0x0301,0x016a,0x0308,0x016b,0x0308,0x0056,0x0303,
+ 0x0076,0x0303,0x0056,0x0323,0x0076,0x0323,0x0057,0x0300,
+ 0x0077,0x0300,0x0057,0x0301,0x0077,0x0301,0x0057,0x0308,
+ 0x0077,0x0308,0x0057,0x0307,0x0077,0x0307,0x0057,0x0323,
+ 0x0077,0x0323,0x0058,0x0307,0x0078,0x0307,0x0058,0x0308,
+ 0x0078,0x0308,0x0059,0x0307,0x0079,0x0307,0x005a,0x0302,
+ 0x007a,0x0302,0x005a,0x0323,0x007a,0x0323,0x005a,0x0331,
+ 0x007a,0x0331,0x0068,0x0331,0x0074,0x0308,0x0077,0x030a,
+ 0x0079,0x030a,0x0061,0x02be,0x017f,0x0307,0x0041,0x0323,
+ 0x0061,0x0323,0x0041,0x0309,0x0061,0x0309,0x00c2,0x0301,
+ 0x00e2,0x0301,0x00c2,0x0300,0x00e2,0x0300,0x00c2,0x0309,
+ 0x00e2,0x0309,0x00c2,0x0303,0x00e2,0x0303,0x1ea0,0x0302,
+ 0x1ea1,0x0302,0x0102,0x0301,0x0103,0x0301,0x0102,0x0300,
+ 0x0103,0x0300,0x0102,0x0309,0x0103,0x0309,0x0102,0x0303,
+ 0x0103,0x0303,0x1ea0,0x0306,0x1ea1,0x0306,0x0045,0x0323,
+ 0x0065,0x0323,0x0045,0x0309,0x0065,0x0309,0x0045,0x0303,
+ 0x0065,0x0303,0x00ca,0x0301,0x00ea,0x0301,0x00ca,0x0300,
+ 0x00ea,0x0300,0x00ca,0x0309,0x00ea,0x0309,0x00ca,0x0303,
+ 0x00ea,0x0303,0x1eb8,0x0302,0x1eb9,0x0302,0x0049,0x0309,
+ 0x0069,0x0309,0x0049,0x0323,0x0069,0x0323,0x004f,0x0323,
+ 0x006f,0x0323,0x004f,0x0309,0x006f,0x0309,0x00d4,0x0301,
+ 0x00f4,0x0301,0x00d4,0x0300,0x00f4,0x0300,0x00d4,0x0309,
+ 0x00f4,0x0309,0x00d4,0x0303,0x00f4,0x0303,0x1ecc,0x0302,
+ 0x1ecd,0x0302,0x01a0,0x0301,0x01a1,0x0301,0x01a0,0x0300,
+ 0x01a1,0x0300,0x01a0,0x0309,0x01a1,0x0309,0x01a0,0x0303,
+ 0x01a1,0x0303,0x01a0,0x0323,0x01a1,0x0323,0x0055,0x0323,
+ 0x0075,0x0323,0x0055,0x0309,0x0075,0x0309,0x01af,0x0301,
+ 0x01b0,0x0301,0x01af,0x0300,0x01b0,0x0300,0x01af,0x0309,
+ 0x01b0,0x0309,0x01af,0x0303,0x01b0,0x0303,0x01af,0x0323,
+ 0x01b0,0x0323,0x0059,0x0300,0x0079,0x0300,0x0059,0x0323,
+ 0x0079,0x0323,0x0059,0x0309,0x0079,0x0309,0x0059,0x0303,
+ 0x0079,0x0303,0x03b1,0x0313,0x03b1,0x0314,0x1f00,0x0300,
+ 0x1f01,0x0300,0x1f00,0x0301,0x1f01,0x0301,0x1f00,0x0342,
+ 0x1f01,0x0342,0x0391,0x0313,0x0391,0x0314,0x1f08,0x0300,
+ 0x1f09,0x0300,0x1f08,0x0301,0x1f09,0x0301,0x1f08,0x0342,
+ 0x1f09,0x0342,0x03b5,0x0313,0x03b5,0x0314,0x1f10,0x0300,
+ 0x1f11,0x0300,0x1f10,0x0301,0x1f11,0x0301,0x0395,0x0313,
+ 0x0395,0x0314,0x1f18,0x0300,0x1f19,0x0300,0x1f18,0x0301,
+ 0x1f19,0x0301,0x03b7,0x0313,0x03b7,0x0314,0x1f20,0x0300,
+ 0x1f21,0x0300,0x1f20,0x0301,0x1f21,0x0301,0x1f20,0x0342,
+ 0x1f21,0x0342,0x0397,0x0313,0x0397,0x0314,0x1f28,0x0300,
+ 0x1f29,0x0300,0x1f28,0x0301,0x1f29,0x0301,0x1f28,0x0342,
+ 0x1f29,0x0342,0x03b9,0x0313,0x03b9,0x0314,0x1f30,0x0300,
+ 0x1f31,0x0300,0x1f30,0x0301,0x1f31,0x0301,0x1f30,0x0342,
+ 0x1f31,0x0342,0x0399,0x0313,0x0399,0x0314,0x1f38,0x0300,
+ 0x1f39,0x0300,0x1f38,0x0301,0x1f39,0x0301,0x1f38,0x0342,
+ 0x1f39,0x0342,0x03bf,0x0313,0x03bf,0x0314,0x1f40,0x0300,
+ 0x1f41,0x0300,0x1f40,0x0301,0x1f41,0x0301,0x039f,0x0313,
+ 0x039f,0x0314,0x1f48,0x0300,0x1f49,0x0300,0x1f48,0x0301,
+ 0x1f49,0x0301,0x03c5,0x0313,0x03c5,0x0314,0x1f50,0x0300,
+ 0x1f51,0x0300,0x1f50,0x0301,0x1f51,0x0301,0x1f50,0x0342,
+ 0x1f51,0x0342,0x03a5,0x0314,0x1f59,0x0300,0x1f59,0x0301,
+ 0x1f59,0x0342,0x03c9,0x0313,0x03c9,0x0314,0x1f60,0x0300,
+ 0x1f61,0x0300,0x1f60,0x0301,0x1f61,0x0301,0x1f60,0x0342,
+ 0x1f61,0x0342,0x03a9,0x0313,0x03a9,0x0314,0x1f68,0x0300,
+ 0x1f69,0x0300,0x1f68,0x0301,0x1f69,0x0301,0x1f68,0x0342,
+ 0x1f69,0x0342,0x03b1,0x0300,0x03ac,0x03b5,0x0300,0x03ad,
+ 0x03b7,0x0300,0x03ae,0x03b9,0x0300,0x03af,0x03bf,0x0300,
+ 0x03cc,0x03c5,0x0300,0x03cd,0x03c9,0x0300,0x03ce,0x1f00,
+ 0x0345,0x1f01,0x0345,0x1f02,0x0345,0x1f03,0x0345,0x1f04,
+ 0x0345,0x1f05,0x0345,0x1f06,0x0345,0x1f07,0x0345,0x1f08,
+ 0x0345,0x1f09,0x0345,0x1f0a,0x0345,0x1f0b,0x0345,0x1f0c,
+ 0x0345,0x1f0d,0x0345,0x1f0e,0x0345,0x1f0f,0x0345,0x1f20,
+ 0x0345,0x1f21,0x0345,0x1f22,0x0345,0x1f23,0x0345,0x1f24,
+ 0x0345,0x1f25,0x0345,0x1f26,0x0345,0x1f27,0x0345,0x1f28,
+ 0x0345,0x1f29,0x0345,0x1f2a,0x0345,0x1f2b,0x0345,0x1f2c,
+ 0x0345,0x1f2d,0x0345,0x1f2e,0x0345,0x1f2f,0x0345,0x1f60,
+ 0x0345,0x1f61,0x0345,0x1f62,0x0345,0x1f63,0x0345,0x1f64,
+ 0x0345,0x1f65,0x0345,0x1f66,0x0345,0x1f67,0x0345,0x1f68,
+ 0x0345,0x1f69,0x0345,0x1f6a,0x0345,0x1f6b,0x0345,0x1f6c,
+ 0x0345,0x1f6d,0x0345,0x1f6e,0x0345,0x1f6f,0x0345,0x03b1,
+ 0x0306,0x03b1,0x0304,0x1f70,0x0345,0x03b1,0x0345,0x03ac,
+ 0x0345,0x03b1,0x0342,0x1fb6,0x0345,0x0391,0x0306,0x0391,
+ 0x0304,0x0391,0x0300,0x0386,0x0391,0x0345,0x0020,0x0313,
+ 0x03b9,0x0020,0x0313,0x0020,0x0342,0x00a8,0x0342,0x1f74,
+ 0x0345,0x03b7,0x0345,0x03ae,0x0345,0x03b7,0x0342,0x1fc6,
+ 0x0345,0x0395,0x0300,0x0388,0x0397,0x0300,0x0389,0x0397,
+ 0x0345,0x1fbf,0x0300,0x1fbf,0x0301,0x1fbf,0x0342,0x03b9,
+ 0x0306,0x03b9,0x0304,0x03ca,0x0300,0x0390,0x03b9,0x0342,
+ 0x03ca,0x0342,0x0399,0x0306,0x0399,0x0304,0x0399,0x0300,
+ 0x038a,0x1ffe,0x0300,0x1ffe,0x0301,0x1ffe,0x0342,0x03c5,
+ 0x0306,0x03c5,0x0304,0x03cb,0x0300,0x03b0,0x03c1,0x0313,
+ 0x03c1,0x0314,0x03c5,0x0342,0x03cb,0x0342,0x03a5,0x0306,
+ 0x03a5,0x0304,0x03a5,0x0300,0x038e,0x03a1,0x0314,0x00a8,
+ 0x0300,0x0385,0x0060,0x1f7c,0x0345,0x03c9,0x0345,0x03ce,
+ 0x0345,0x03c9,0x0342,0x1ff6,0x0345,0x039f,0x0300,0x038c,
+ 0x03a9,0x0300,0x038f,0x03a9,0x0345,0x00b4,0x0020,0x0314,
+ 0x2002,0x2003,0x0020,0x0020,0x0020,0x0020,0x0020,0x0020,
+ 0x0020,0x0020,0x0020,0x2010,0x0020,0x0333,0x002e,0x002e,
+ 0x002e,0x002e,0x002e,0x002e,0x0020,0x2032,0x2032,0x2032,
+ 0x2032,0x2032,0x2035,0x2035,0x2035,0x2035,0x2035,0x0021,
+ 0x0021,0x0020,0x0305,0x003f,0x003f,0x003f,0x0021,0x0021,
+ 0x003f,0x2032,0x2032,0x2032,0x2032,0x0020,0x0030,0x0069,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x002b,0x2212,
+ 0x003d,0x0028,0x0029,0x006e,0x0030,0x0031,0x0032,0x0033,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x002b,0x2212,
+ 0x003d,0x0028,0x0029,0x0061,0x0065,0x006f,0x0078,0x0259,
+ 0x0052,0x0073,0x0061,0x002f,0x0063,0x0061,0x002f,0x0073,
+ 0x0043,0x00b0,0x0043,0x0063,0x002f,0x006f,0x0063,0x002f,
+ 0x0075,0x0190,0x00b0,0x0046,0x0067,0x0048,0x0048,0x0048,
+ 0x0068,0x0127,0x0049,0x0049,0x004c,0x006c,0x004e,0x004e,
+ 0x006f,0x0050,0x0051,0x0052,0x0052,0x0052,0x0053,0x004d,
+ 0x0054,0x0045,0x004c,0x0054,0x004d,0x005a,0x03a9,0x005a,
+ 0x004b,0x00c5,0x0042,0x0043,0x0065,0x0045,0x0046,0x004d,
+ 0x006f,0x05d0,0x05d1,0x05d2,0x05d3,0x0069,0x0046,0x0041,
+ 0x0058,0x03c0,0x03b3,0x0393,0x03a0,0x2211,0x0044,0x0064,
+ 0x0065,0x0069,0x006a,0x0031,0x2044,0x0033,0x0032,0x2044,
+ 0x0033,0x0031,0x2044,0x0035,0x0032,0x2044,0x0035,0x0033,
+ 0x2044,0x0035,0x0034,0x2044,0x0035,0x0031,0x2044,0x0036,
+ 0x0035,0x2044,0x0036,0x0031,0x2044,0x0038,0x0033,0x2044,
+ 0x0038,0x0035,0x2044,0x0038,0x0037,0x2044,0x0038,0x0031,
+ 0x2044,0x0049,0x0049,0x0049,0x0049,0x0049,0x0049,0x0049,
+ 0x0056,0x0056,0x0056,0x0049,0x0056,0x0049,0x0049,0x0056,
+ 0x0049,0x0049,0x0049,0x0049,0x0058,0x0058,0x0058,0x0049,
+ 0x0058,0x0049,0x0049,0x004c,0x0043,0x0044,0x004d,0x0069,
+ 0x0069,0x0069,0x0069,0x0069,0x0069,0x0069,0x0076,0x0076,
+ 0x0076,0x0069,0x0076,0x0069,0x0069,0x0076,0x0069,0x0069,
+ 0x0069,0x0069,0x0078,0x0078,0x0078,0x0069,0x0078,0x0069,
+ 0x0069,0x006c,0x0063,0x0064,0x006d,0x2190,0x0338,0x2192,
+ 0x0338,0x2194,0x0338,0x21d0,0x0338,0x21d4,0x0338,0x21d2,
+ 0x0338,0x2203,0x0338,0x2208,0x0338,0x220b,0x0338,0x2223,
+ 0x0338,0x2225,0x0338,0x222b,0x222b,0x222b,0x222b,0x222b,
+ 0x222e,0x222e,0x222e,0x222e,0x222e,0x223c,0x0338,0x2243,
+ 0x0338,0x2245,0x0338,0x2248,0x0338,0x003d,0x0338,0x2261,
+ 0x0338,0x224d,0x0338,0x003c,0x0338,0x003e,0x0338,0x2264,
+ 0x0338,0x2265,0x0338,0x2272,0x0338,0x2273,0x0338,0x2276,
+ 0x0338,0x2277,0x0338,0x227a,0x0338,0x227b,0x0338,0x2282,
+ 0x0338,0x2283,0x0338,0x2286,0x0338,0x2287,0x0338,0x22a2,
+ 0x0338,0x22a8,0x0338,0x22a9,0x0338,0x22ab,0x0338,0x227c,
+ 0x0338,0x227d,0x0338,0x2291,0x0338,0x2292,0x0338,0x22b2,
+ 0x0338,0x22b3,0x0338,0x22b4,0x0338,0x22b5,0x0338,0x3008,
+ 0x3009,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x0031,0x0030,0x0031,0x0031,0x0031,0x0032,
+ 0x0031,0x0033,0x0031,0x0034,0x0031,0x0035,0x0031,0x0036,
+ 0x0031,0x0037,0x0031,0x0038,0x0031,0x0039,0x0032,0x0030,
+ 0x0028,0x0031,0x0029,0x0028,0x0032,0x0029,0x0028,0x0033,
+ 0x0029,0x0028,0x0034,0x0029,0x0028,0x0035,0x0029,0x0028,
+ 0x0036,0x0029,0x0028,0x0037,0x0029,0x0028,0x0038,0x0029,
+ 0x0028,0x0039,0x0029,0x0028,0x0031,0x0030,0x0029,0x0028,
+ 0x0031,0x0031,0x0029,0x0028,0x0031,0x0032,0x0029,0x0028,
+ 0x0031,0x0033,0x0029,0x0028,0x0031,0x0034,0x0029,0x0028,
+ 0x0031,0x0035,0x0029,0x0028,0x0031,0x0036,0x0029,0x0028,
+ 0x0031,0x0037,0x0029,0x0028,0x0031,0x0038,0x0029,0x0028,
+ 0x0031,0x0039,0x0029,0x0028,0x0032,0x0030,0x0029,0x0031,
+ 0x002e,0x0032,0x002e,0x0033,0x002e,0x0034,0x002e,0x0035,
+ 0x002e,0x0036,0x002e,0x0037,0x002e,0x0038,0x002e,0x0039,
+ 0x002e,0x0031,0x0030,0x002e,0x0031,0x0031,0x002e,0x0031,
+ 0x0032,0x002e,0x0031,0x0033,0x002e,0x0031,0x0034,0x002e,
+ 0x0031,0x0035,0x002e,0x0031,0x0036,0x002e,0x0031,0x0037,
+ 0x002e,0x0031,0x0038,0x002e,0x0031,0x0039,0x002e,0x0032,
+ 0x0030,0x002e,0x0028,0x0061,0x0029,0x0028,0x0062,0x0029,
+ 0x0028,0x0063,0x0029,0x0028,0x0064,0x0029,0x0028,0x0065,
+ 0x0029,0x0028,0x0066,0x0029,0x0028,0x0067,0x0029,0x0028,
+ 0x0068,0x0029,0x0028,0x0069,0x0029,0x0028,0x006a,0x0029,
+ 0x0028,0x006b,0x0029,0x0028,0x006c,0x0029,0x0028,0x006d,
+ 0x0029,0x0028,0x006e,0x0029,0x0028,0x006f,0x0029,0x0028,
+ 0x0070,0x0029,0x0028,0x0071,0x0029,0x0028,0x0072,0x0029,
+ 0x0028,0x0073,0x0029,0x0028,0x0074,0x0029,0x0028,0x0075,
+ 0x0029,0x0028,0x0076,0x0029,0x0028,0x0077,0x0029,0x0028,
+ 0x0078,0x0029,0x0028,0x0079,0x0029,0x0028,0x007a,0x0029,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0030,0x222b,0x222b,0x222b,
+ 0x222b,0x003a,0x003a,0x003d,0x003d,0x003d,0x003d,0x003d,
+ 0x003d,0x2add,0x0338,0x2d61,0x6bcd,0x9f9f,0x4e00,0x4e28,
+ 0x4e36,0x4e3f,0x4e59,0x4e85,0x4e8c,0x4ea0,0x4eba,0x513f,
+ 0x5165,0x516b,0x5182,0x5196,0x51ab,0x51e0,0x51f5,0x5200,
+ 0x529b,0x52f9,0x5315,0x531a,0x5338,0x5341,0x535c,0x5369,
+ 0x5382,0x53b6,0x53c8,0x53e3,0x56d7,0x571f,0x58eb,0x5902,
+ 0x590a,0x5915,0x5927,0x5973,0x5b50,0x5b80,0x5bf8,0x5c0f,
+ 0x5c22,0x5c38,0x5c6e,0x5c71,0x5ddb,0x5de5,0x5df1,0x5dfe,
+ 0x5e72,0x5e7a,0x5e7f,0x5ef4,0x5efe,0x5f0b,0x5f13,0x5f50,
+ 0x5f61,0x5f73,0x5fc3,0x6208,0x6236,0x624b,0x652f,0x6534,
+ 0x6587,0x6597,0x65a4,0x65b9,0x65e0,0x65e5,0x66f0,0x6708,
+ 0x6728,0x6b20,0x6b62,0x6b79,0x6bb3,0x6bcb,0x6bd4,0x6bdb,
+ 0x6c0f,0x6c14,0x6c34,0x706b,0x722a,0x7236,0x723b,0x723f,
+ 0x7247,0x7259,0x725b,0x72ac,0x7384,0x7389,0x74dc,0x74e6,
+ 0x7518,0x751f,0x7528,0x7530,0x758b,0x7592,0x7676,0x767d,
+ 0x76ae,0x76bf,0x76ee,0x77db,0x77e2,0x77f3,0x793a,0x79b8,
+ 0x79be,0x7a74,0x7acb,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f51,
+ 0x7f8a,0x7fbd,0x8001,0x800c,0x8012,0x8033,0x807f,0x8089,
+ 0x81e3,0x81ea,0x81f3,0x81fc,0x820c,0x821b,0x821f,0x826e,
+ 0x8272,0x8278,0x864d,0x866b,0x8840,0x884c,0x8863,0x897e,
+ 0x898b,0x89d2,0x8a00,0x8c37,0x8c46,0x8c55,0x8c78,0x8c9d,
+ 0x8d64,0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fb5,
+ 0x9091,0x9149,0x91c6,0x91cc,0x91d1,0x9577,0x9580,0x961c,
+ 0x96b6,0x96b9,0x96e8,0x9751,0x975e,0x9762,0x9769,0x97cb,
+ 0x97ed,0x97f3,0x9801,0x98a8,0x98db,0x98df,0x9996,0x9999,
+ 0x99ac,0x9aa8,0x9ad8,0x9adf,0x9b25,0x9b2f,0x9b32,0x9b3c,
+ 0x9b5a,0x9ce5,0x9e75,0x9e7f,0x9ea5,0x9ebb,0x9ec3,0x9ecd,
+ 0x9ed1,0x9ef9,0x9efd,0x9f0e,0x9f13,0x9f20,0x9f3b,0x9f4a,
+ 0x9f52,0x9f8d,0x9f9c,0x9fa0,0x0020,0x3012,0x5341,0x5344,
+ 0x5345,0x304b,0x3099,0x304d,0x3099,0x304f,0x3099,0x3051,
+ 0x3099,0x3053,0x3099,0x3055,0x3099,0x3057,0x3099,0x3059,
+ 0x3099,0x305b,0x3099,0x305d,0x3099,0x305f,0x3099,0x3061,
+ 0x3099,0x3064,0x3099,0x3066,0x3099,0x3068,0x3099,0x306f,
+ 0x3099,0x306f,0x309a,0x3072,0x3099,0x3072,0x309a,0x3075,
+ 0x3099,0x3075,0x309a,0x3078,0x3099,0x3078,0x309a,0x307b,
+ 0x3099,0x307b,0x309a,0x3046,0x3099,0x0020,0x3099,0x0020,
+ 0x309a,0x309d,0x3099,0x3088,0x308a,0x30ab,0x3099,0x30ad,
+ 0x3099,0x30af,0x3099,0x30b1,0x3099,0x30b3,0x3099,0x30b5,
+ 0x3099,0x30b7,0x3099,0x30b9,0x3099,0x30bb,0x3099,0x30bd,
+ 0x3099,0x30bf,0x3099,0x30c1,0x3099,0x30c4,0x3099,0x30c6,
+ 0x3099,0x30c8,0x3099,0x30cf,0x3099,0x30cf,0x309a,0x30d2,
+ 0x3099,0x30d2,0x309a,0x30d5,0x3099,0x30d5,0x309a,0x30d8,
+ 0x3099,0x30d8,0x309a,0x30db,0x3099,0x30db,0x309a,0x30a6,
+ 0x3099,0x30ef,0x3099,0x30f0,0x3099,0x30f1,0x3099,0x30f2,
+ 0x3099,0x30fd,0x3099,0x30b3,0x30c8,0x1100,0x1101,0x11aa,
+ 0x1102,0x11ac,0x11ad,0x1103,0x1104,0x1105,0x11b0,0x11b1,
+ 0x11b2,0x11b3,0x11b4,0x11b5,0x111a,0x1106,0x1107,0x1108,
+ 0x1121,0x1109,0x110a,0x110b,0x110c,0x110d,0x110e,0x110f,
+ 0x1110,0x1111,0x1112,0x1161,0x1162,0x1163,0x1164,0x1165,
+ 0x1166,0x1167,0x1168,0x1169,0x116a,0x116b,0x116c,0x116d,
+ 0x116e,0x116f,0x1170,0x1171,0x1172,0x1173,0x1174,0x1175,
+ 0x1160,0x1114,0x1115,0x11c7,0x11c8,0x11cc,0x11ce,0x11d3,
+ 0x11d7,0x11d9,0x111c,0x11dd,0x11df,0x111d,0x111e,0x1120,
+ 0x1122,0x1123,0x1127,0x1129,0x112b,0x112c,0x112d,0x112e,
+ 0x112f,0x1132,0x1136,0x1140,0x1147,0x114c,0x11f1,0x11f2,
+ 0x1157,0x1158,0x1159,0x1184,0x1185,0x1188,0x1191,0x1192,
+ 0x1194,0x119e,0x11a1,0x4e00,0x4e8c,0x4e09,0x56db,0x4e0a,
+ 0x4e2d,0x4e0b,0x7532,0x4e59,0x4e19,0x4e01,0x5929,0x5730,
+ 0x4eba,0x0028,0x1100,0x0029,0x0028,0x1102,0x0029,0x0028,
+ 0x1103,0x0029,0x0028,0x1105,0x0029,0x0028,0x1106,0x0029,
+ 0x0028,0x1107,0x0029,0x0028,0x1109,0x0029,0x0028,0x110b,
+ 0x0029,0x0028,0x110c,0x0029,0x0028,0x110e,0x0029,0x0028,
+ 0x110f,0x0029,0x0028,0x1110,0x0029,0x0028,0x1111,0x0029,
+ 0x0028,0x1112,0x0029,0x0028,0x1100,0x1161,0x0029,0x0028,
+ 0x1102,0x1161,0x0029,0x0028,0x1103,0x1161,0x0029,0x0028,
+ 0x1105,0x1161,0x0029,0x0028,0x1106,0x1161,0x0029,0x0028,
+ 0x1107,0x1161,0x0029,0x0028,0x1109,0x1161,0x0029,0x0028,
+ 0x110b,0x1161,0x0029,0x0028,0x110c,0x1161,0x0029,0x0028,
+ 0x110e,0x1161,0x0029,0x0028,0x110f,0x1161,0x0029,0x0028,
+ 0x1110,0x1161,0x0029,0x0028,0x1111,0x1161,0x0029,0x0028,
+ 0x1112,0x1161,0x0029,0x0028,0x110c,0x116e,0x0029,0x0028,
+ 0x110b,0x1169,0x110c,0x1165,0x11ab,0x0029,0x0028,0x110b,
+ 0x1169,0x1112,0x116e,0x0029,0x0028,0x4e00,0x0029,0x0028,
+ 0x4e8c,0x0029,0x0028,0x4e09,0x0029,0x0028,0x56db,0x0029,
+ 0x0028,0x4e94,0x0029,0x0028,0x516d,0x0029,0x0028,0x4e03,
+ 0x0029,0x0028,0x516b,0x0029,0x0028,0x4e5d,0x0029,0x0028,
+ 0x5341,0x0029,0x0028,0x6708,0x0029,0x0028,0x706b,0x0029,
+ 0x0028,0x6c34,0x0029,0x0028,0x6728,0x0029,0x0028,0x91d1,
+ 0x0029,0x0028,0x571f,0x0029,0x0028,0x65e5,0x0029,0x0028,
+ 0x682a,0x0029,0x0028,0x6709,0x0029,0x0028,0x793e,0x0029,
+ 0x0028,0x540d,0x0029,0x0028,0x7279,0x0029,0x0028,0x8ca1,
+ 0x0029,0x0028,0x795d,0x0029,0x0028,0x52b4,0x0029,0x0028,
+ 0x4ee3,0x0029,0x0028,0x547c,0x0029,0x0028,0x5b66,0x0029,
+ 0x0028,0x76e3,0x0029,0x0028,0x4f01,0x0029,0x0028,0x8cc7,
+ 0x0029,0x0028,0x5354,0x0029,0x0028,0x796d,0x0029,0x0028,
+ 0x4f11,0x0029,0x0028,0x81ea,0x0029,0x0028,0x81f3,0x0029,
+ 0x0050,0x0054,0x0045,0x0032,0x0031,0x0032,0x0032,0x0032,
+ 0x0033,0x0032,0x0034,0x0032,0x0035,0x0032,0x0036,0x0032,
+ 0x0037,0x0032,0x0038,0x0032,0x0039,0x0033,0x0030,0x0033,
+ 0x0031,0x0033,0x0032,0x0033,0x0033,0x0033,0x0034,0x0033,
+ 0x0035,0x1100,0x1102,0x1103,0x1105,0x1106,0x1107,0x1109,
+ 0x110b,0x110c,0x110e,0x110f,0x1110,0x1111,0x1112,0x1100,
+ 0x1161,0x1102,0x1161,0x1103,0x1161,0x1105,0x1161,0x1106,
+ 0x1161,0x1107,0x1161,0x1109,0x1161,0x110b,0x1161,0x110c,
+ 0x1161,0x110e,0x1161,0x110f,0x1161,0x1110,0x1161,0x1111,
+ 0x1161,0x1112,0x1161,0x110e,0x1161,0x11b7,0x1100,0x1169,
+ 0x110c,0x116e,0x110b,0x1174,0x110b,0x116e,0x4e00,0x4e8c,
+ 0x4e09,0x56db,0x4e94,0x516d,0x4e03,0x516b,0x4e5d,0x5341,
+ 0x6708,0x706b,0x6c34,0x6728,0x91d1,0x571f,0x65e5,0x682a,
+ 0x6709,0x793e,0x540d,0x7279,0x8ca1,0x795d,0x52b4,0x79d8,
+ 0x7537,0x5973,0x9069,0x512a,0x5370,0x6ce8,0x9805,0x4f11,
+ 0x5199,0x6b63,0x4e0a,0x4e2d,0x4e0b,0x5de6,0x53f3,0x533b,
+ 0x5b97,0x5b66,0x76e3,0x4f01,0x8cc7,0x5354,0x591c,0x0033,
+ 0x0036,0x0033,0x0037,0x0033,0x0038,0x0033,0x0039,0x0034,
+ 0x0030,0x0034,0x0031,0x0034,0x0032,0x0034,0x0033,0x0034,
+ 0x0034,0x0034,0x0035,0x0034,0x0036,0x0034,0x0037,0x0034,
+ 0x0038,0x0034,0x0039,0x0035,0x0030,0x0031,0x6708,0x0032,
+ 0x6708,0x0033,0x6708,0x0034,0x6708,0x0035,0x6708,0x0036,
+ 0x6708,0x0037,0x6708,0x0038,0x6708,0x0039,0x6708,0x0031,
+ 0x0030,0x6708,0x0031,0x0031,0x6708,0x0031,0x0032,0x6708,
+ 0x0048,0x0067,0x0065,0x0072,0x0067,0x0065,0x0056,0x004c,
+ 0x0054,0x0044,0x30a2,0x30a4,0x30a6,0x30a8,0x30aa,0x30ab,
+ 0x30ad,0x30af,0x30b1,0x30b3,0x30b5,0x30b7,0x30b9,0x30bb,
+ 0x30bd,0x30bf,0x30c1,0x30c4,0x30c6,0x30c8,0x30ca,0x30cb,
+ 0x30cc,0x30cd,0x30ce,0x30cf,0x30d2,0x30d5,0x30d8,0x30db,
+ 0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e4,0x30e6,0x30e8,
+ 0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ef,0x30f0,0x30f1,
+ 0x30f2,0x30a2,0x30d1,0x30fc,0x30c8,0x30a2,0x30eb,0x30d5,
+ 0x30a1,0x30a2,0x30f3,0x30da,0x30a2,0x30a2,0x30fc,0x30eb,
+ 0x30a4,0x30cb,0x30f3,0x30b0,0x30a4,0x30f3,0x30c1,0x30a6,
+ 0x30a9,0x30f3,0x30a8,0x30b9,0x30af,0x30fc,0x30c9,0x30a8,
+ 0x30fc,0x30ab,0x30fc,0x30aa,0x30f3,0x30b9,0x30aa,0x30fc,
+ 0x30e0,0x30ab,0x30a4,0x30ea,0x30ab,0x30e9,0x30c3,0x30c8,
+ 0x30ab,0x30ed,0x30ea,0x30fc,0x30ac,0x30ed,0x30f3,0x30ac,
+ 0x30f3,0x30de,0x30ae,0x30ac,0x30ae,0x30cb,0x30fc,0x30ad,
+ 0x30e5,0x30ea,0x30fc,0x30ae,0x30eb,0x30c0,0x30fc,0x30ad,
+ 0x30ed,0x30ad,0x30ed,0x30b0,0x30e9,0x30e0,0x30ad,0x30ed,
+ 0x30e1,0x30fc,0x30c8,0x30eb,0x30ad,0x30ed,0x30ef,0x30c3,
+ 0x30c8,0x30b0,0x30e9,0x30e0,0x30b0,0x30e9,0x30e0,0x30c8,
+ 0x30f3,0x30af,0x30eb,0x30bc,0x30a4,0x30ed,0x30af,0x30ed,
+ 0x30fc,0x30cd,0x30b1,0x30fc,0x30b9,0x30b3,0x30eb,0x30ca,
+ 0x30b3,0x30fc,0x30dd,0x30b5,0x30a4,0x30af,0x30eb,0x30b5,
+ 0x30f3,0x30c1,0x30fc,0x30e0,0x30b7,0x30ea,0x30f3,0x30b0,
+ 0x30bb,0x30f3,0x30c1,0x30bb,0x30f3,0x30c8,0x30c0,0x30fc,
+ 0x30b9,0x30c7,0x30b7,0x30c9,0x30eb,0x30c8,0x30f3,0x30ca,
+ 0x30ce,0x30ce,0x30c3,0x30c8,0x30cf,0x30a4,0x30c4,0x30d1,
+ 0x30fc,0x30bb,0x30f3,0x30c8,0x30d1,0x30fc,0x30c4,0x30d0,
+ 0x30fc,0x30ec,0x30eb,0x30d4,0x30a2,0x30b9,0x30c8,0x30eb,
+ 0x30d4,0x30af,0x30eb,0x30d4,0x30b3,0x30d3,0x30eb,0x30d5,
+ 0x30a1,0x30e9,0x30c3,0x30c9,0x30d5,0x30a3,0x30fc,0x30c8,
+ 0x30d6,0x30c3,0x30b7,0x30a7,0x30eb,0x30d5,0x30e9,0x30f3,
+ 0x30d8,0x30af,0x30bf,0x30fc,0x30eb,0x30da,0x30bd,0x30da,
+ 0x30cb,0x30d2,0x30d8,0x30eb,0x30c4,0x30da,0x30f3,0x30b9,
+ 0x30da,0x30fc,0x30b8,0x30d9,0x30fc,0x30bf,0x30dd,0x30a4,
+ 0x30f3,0x30c8,0x30dc,0x30eb,0x30c8,0x30db,0x30f3,0x30dd,
+ 0x30f3,0x30c9,0x30db,0x30fc,0x30eb,0x30db,0x30fc,0x30f3,
+ 0x30de,0x30a4,0x30af,0x30ed,0x30de,0x30a4,0x30eb,0x30de,
+ 0x30c3,0x30cf,0x30de,0x30eb,0x30af,0x30de,0x30f3,0x30b7,
+ 0x30e7,0x30f3,0x30df,0x30af,0x30ed,0x30f3,0x30df,0x30ea,
+ 0x30df,0x30ea,0x30d0,0x30fc,0x30eb,0x30e1,0x30ac,0x30e1,
+ 0x30ac,0x30c8,0x30f3,0x30e1,0x30fc,0x30c8,0x30eb,0x30e4,
+ 0x30fc,0x30c9,0x30e4,0x30fc,0x30eb,0x30e6,0x30a2,0x30f3,
+ 0x30ea,0x30c3,0x30c8,0x30eb,0x30ea,0x30e9,0x30eb,0x30d4,
+ 0x30fc,0x30eb,0x30fc,0x30d6,0x30eb,0x30ec,0x30e0,0x30ec,
+ 0x30f3,0x30c8,0x30b2,0x30f3,0x30ef,0x30c3,0x30c8,0x0030,
+ 0x70b9,0x0031,0x70b9,0x0032,0x70b9,0x0033,0x70b9,0x0034,
+ 0x70b9,0x0035,0x70b9,0x0036,0x70b9,0x0037,0x70b9,0x0038,
+ 0x70b9,0x0039,0x70b9,0x0031,0x0030,0x70b9,0x0031,0x0031,
+ 0x70b9,0x0031,0x0032,0x70b9,0x0031,0x0033,0x70b9,0x0031,
+ 0x0034,0x70b9,0x0031,0x0035,0x70b9,0x0031,0x0036,0x70b9,
+ 0x0031,0x0037,0x70b9,0x0031,0x0038,0x70b9,0x0031,0x0039,
+ 0x70b9,0x0032,0x0030,0x70b9,0x0032,0x0031,0x70b9,0x0032,
+ 0x0032,0x70b9,0x0032,0x0033,0x70b9,0x0032,0x0034,0x70b9,
+ 0x0068,0x0050,0x0061,0x0064,0x0061,0x0041,0x0055,0x0062,
+ 0x0061,0x0072,0x006f,0x0056,0x0070,0x0063,0x0064,0x006d,
+ 0x0064,0x006d,0x00b2,0x0064,0x006d,0x00b3,0x0049,0x0055,
+ 0x5e73,0x6210,0x662d,0x548c,0x5927,0x6b63,0x660e,0x6cbb,
+ 0x682a,0x5f0f,0x4f1a,0x793e,0x0070,0x0041,0x006e,0x0041,
+ 0x03bc,0x0041,0x006d,0x0041,0x006b,0x0041,0x004b,0x0042,
+ 0x004d,0x0042,0x0047,0x0042,0x0063,0x0061,0x006c,0x006b,
+ 0x0063,0x0061,0x006c,0x0070,0x0046,0x006e,0x0046,0x03bc,
+ 0x0046,0x03bc,0x0067,0x006d,0x0067,0x006b,0x0067,0x0048,
+ 0x007a,0x006b,0x0048,0x007a,0x004d,0x0048,0x007a,0x0047,
+ 0x0048,0x007a,0x0054,0x0048,0x007a,0x03bc,0x2113,0x006d,
+ 0x2113,0x0064,0x2113,0x006b,0x2113,0x0066,0x006d,0x006e,
+ 0x006d,0x03bc,0x006d,0x006d,0x006d,0x0063,0x006d,0x006b,
+ 0x006d,0x006d,0x006d,0x00b2,0x0063,0x006d,0x00b2,0x006d,
+ 0x00b2,0x006b,0x006d,0x00b2,0x006d,0x006d,0x00b3,0x0063,
+ 0x006d,0x00b3,0x006d,0x00b3,0x006b,0x006d,0x00b3,0x006d,
+ 0x2215,0x0073,0x006d,0x2215,0x0073,0x00b2,0x0050,0x0061,
+ 0x006b,0x0050,0x0061,0x004d,0x0050,0x0061,0x0047,0x0050,
+ 0x0061,0x0072,0x0061,0x0064,0x0072,0x0061,0x0064,0x2215,
+ 0x0073,0x0072,0x0061,0x0064,0x2215,0x0073,0x00b2,0x0070,
+ 0x0073,0x006e,0x0073,0x03bc,0x0073,0x006d,0x0073,0x0070,
+ 0x0056,0x006e,0x0056,0x03bc,0x0056,0x006d,0x0056,0x006b,
+ 0x0056,0x004d,0x0056,0x0070,0x0057,0x006e,0x0057,0x03bc,
+ 0x0057,0x006d,0x0057,0x006b,0x0057,0x004d,0x0057,0x006b,
+ 0x03a9,0x004d,0x03a9,0x0061,0x002e,0x006d,0x002e,0x0042,
+ 0x0071,0x0063,0x0063,0x0063,0x0064,0x0043,0x2215,0x006b,
+ 0x0067,0x0043,0x006f,0x002e,0x0064,0x0042,0x0047,0x0079,
+ 0x0068,0x0061,0x0048,0x0050,0x0069,0x006e,0x004b,0x004b,
+ 0x004b,0x004d,0x006b,0x0074,0x006c,0x006d,0x006c,0x006e,
+ 0x006c,0x006f,0x0067,0x006c,0x0078,0x006d,0x0062,0x006d,
+ 0x0069,0x006c,0x006d,0x006f,0x006c,0x0050,0x0048,0x0070,
+ 0x002e,0x006d,0x002e,0x0050,0x0050,0x004d,0x0050,0x0052,
+ 0x0073,0x0072,0x0053,0x0076,0x0057,0x0062,0x0056,0x2215,
+ 0x006d,0x0041,0x2215,0x006d,0x0031,0x65e5,0x0032,0x65e5,
+ 0x0033,0x65e5,0x0034,0x65e5,0x0035,0x65e5,0x0036,0x65e5,
+ 0x0037,0x65e5,0x0038,0x65e5,0x0039,0x65e5,0x0031,0x0030,
+ 0x65e5,0x0031,0x0031,0x65e5,0x0031,0x0032,0x65e5,0x0031,
+ 0x0033,0x65e5,0x0031,0x0034,0x65e5,0x0031,0x0035,0x65e5,
+ 0x0031,0x0036,0x65e5,0x0031,0x0037,0x65e5,0x0031,0x0038,
+ 0x65e5,0x0031,0x0039,0x65e5,0x0032,0x0030,0x65e5,0x0032,
+ 0x0031,0x65e5,0x0032,0x0032,0x65e5,0x0032,0x0033,0x65e5,
+ 0x0032,0x0034,0x65e5,0x0032,0x0035,0x65e5,0x0032,0x0036,
+ 0x65e5,0x0032,0x0037,0x65e5,0x0032,0x0038,0x65e5,0x0032,
+ 0x0039,0x65e5,0x0033,0x0030,0x65e5,0x0033,0x0031,0x65e5,
+ 0x0067,0x0061,0x006c
+};
+
+
+#define UCS4_BMPCJKMIN 0xf900
+#define UCS4_BMPCJKMAX 0xface
+
+ /* CJK Compatibility - 0 means hole (no decomposition) */
+static const unsigned short ucs4_bmpcjk1decomptab[463] = {
+ 0x8c48,0x66f4,0x8eca,0x8cc8,0x6ed1,0x4e32,0x53e5,0x9f9c,
+ 0x9f9c,0x5951,0x91d1,0x5587,0x5948,0x61f6,0x7669,0x7f85,
+ 0x863f,0x87ba,0x88f8,0x908f,0x6a02,0x6d1b,0x70d9,0x73de,
+ 0x843d,0x916a,0x99f1,0x4e82,0x5375,0x6b04,0x721b,0x862d,
+ 0x9e1e,0x5d50,0x6feb,0x85cd,0x8964,0x62c9,0x81d8,0x881f,
+ 0x5eca,0x6717,0x6d6a,0x72fc,0x90ce,0x4f86,0x51b7,0x52de,
+ 0x64c4,0x6ad3,0x7210,0x76e7,0x8001,0x8606,0x865c,0x8def,
+ 0x9732,0x9b6f,0x9dfa,0x788c,0x797f,0x7da0,0x83c9,0x9304,
+ 0x9e7f,0x8ad6,0x58df,0x5f04,0x7c60,0x807e,0x7262,0x78ca,
+ 0x8cc2,0x96f7,0x58d8,0x5c62,0x6a13,0x6dda,0x6f0f,0x7d2f,
+ 0x7e37,0x964b,0x52d2,0x808b,0x51dc,0x51cc,0x7a1c,0x7dbe,
+ 0x83f1,0x9675,0x8b80,0x62cf,0x6a02,0x8afe,0x4e39,0x5be7,
+ 0x6012,0x7387,0x7570,0x5317,0x78fb,0x4fbf,0x5fa9,0x4e0d,
+ 0x6ccc,0x6578,0x7d22,0x53c3,0x585e,0x7701,0x8449,0x8aaa,
+ 0x6bba,0x8fb0,0x6c88,0x62fe,0x82e5,0x63a0,0x7565,0x4eae,
+ 0x5169,0x51c9,0x6881,0x7ce7,0x826f,0x8ad2,0x91cf,0x52f5,
+ 0x5442,0x5973,0x5eec,0x65c5,0x6ffe,0x792a,0x95ad,0x9a6a,
+ 0x9e97,0x9ece,0x529b,0x66c6,0x6b77,0x8f62,0x5e74,0x6190,
+ 0x6200,0x649a,0x6f23,0x7149,0x7489,0x79ca,0x7df4,0x806f,
+ 0x8f26,0x84ee,0x9023,0x934a,0x5217,0x52a3,0x54bd,0x70c8,
+ 0x88c2,0x8aaa,0x5ec9,0x5ff5,0x637b,0x6bae,0x7c3e,0x7375,
+ 0x4ee4,0x56f9,0x5be7,0x5dba,0x601c,0x73b2,0x7469,0x7f9a,
+ 0x8046,0x9234,0x96f6,0x9748,0x9818,0x4f8b,0x79ae,0x91b4,
+ 0x96b8,0x60e1,0x4e86,0x50da,0x5bee,0x5c3f,0x6599,0x6a02,
+ 0x71ce,0x7642,0x84fc,0x907c,0x9f8d,0x6688,0x962e,0x5289,
+ 0x677b,0x67f3,0x6d41,0x6e9c,0x7409,0x7559,0x786b,0x7d10,
+ 0x985e,0x516d,0x622e,0x9678,0x502b,0x5d19,0x6dea,0x8f2a,
+ 0x5f8b,0x6144,0x6817,0x7387,0x9686,0x5229,0x540f,0x5c65,
+ 0x6613,0x674e,0x68a8,0x6ce5,0x7406,0x75e2,0x7f79,0x88cf,
+ 0x88e1,0x91cc,0x96e2,0x533f,0x6eba,0x541d,0x71d0,0x7498,
+ 0x85fa,0x96a3,0x9c57,0x9e9f,0x6797,0x6dcb,0x81e8,0x7acb,
+ 0x7b20,0x7c92,0x72c0,0x7099,0x8b58,0x4ec0,0x8336,0x523a,
+ 0x5207,0x5ea6,0x62d3,0x7cd6,0x5b85,0x6d1e,0x66b4,0x8f3b,
+ 0x884c,0x964d,0x898b,0x5ed3,0x5140,0x55c0,0x0000,0x0000,
+ 0x585a,0x0000,0x6674,0x0000,0x0000,0x51de,0x732a,0x76ca,
+ 0x793c,0x795e,0x7965,0x798f,0x9756,0x7cbe,0x7fbd,0x0000,
+ 0x8612,0x0000,0x8af8,0x0000,0x0000,0x9038,0x90fd,0x0000,
+ 0x0000,0x0000,0x98ef,0x98fc,0x9928,0x9db4,0x0000,0x0000,
+ 0x4fae,0x50e7,0x514d,0x52c9,0x52e4,0x5351,0x559d,0x5606,
+ 0x5668,0x5840,0x58a8,0x5c64,0x5c6e,0x6094,0x6168,0x618e,
+ 0x61f2,0x654f,0x65e2,0x6691,0x6885,0x6d77,0x6e1a,0x6f22,
+ 0x716e,0x722b,0x7422,0x7891,0x793e,0x7949,0x7948,0x7950,
+ 0x7956,0x795d,0x798d,0x798e,0x7a40,0x7a81,0x7bc0,0x7df4,
+ 0x7e09,0x7e41,0x7f72,0x8005,0x81ed,0x8279,0x8279,0x8457,
+ 0x8910,0x8996,0x8b01,0x8b39,0x8cd3,0x8d08,0x8fb6,0x9038,
+ 0x96e3,0x97ff,0x983b,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4e26,0x51b5,0x5168,0x4f80,0x5145,0x5180,0x52c7,0x52fa,
+ 0x559d,0x5555,0x5599,0x55e2,0x585a,0x58b3,0x5944,0x5954,
+ 0x5a62,0x5b28,0x5ed2,0x5ed9,0x5f69,0x5fad,0x60d8,0x614e,
+ 0x6108,0x618e,0x6160,0x61f2,0x6234,0x63c4,0x641c,0x6452,
+ 0x6556,0x6674,0x6717,0x671b,0x6756,0x6b79,0x6bba,0x6d41,
+ 0x6edb,0x6ecb,0x6f22,0x701e,0x716e,0x77a7,0x7235,0x72af,
+ 0x732a,0x7471,0x7506,0x753b,0x761d,0x761f,0x76ca,0x76db,
+ 0x76f4,0x774a,0x7740,0x78cc,0x7ab1,0x7bc0,0x7c7b,0x7d5b,
+ 0x7df4,0x7f3e,0x8005,0x8352,0x83ef,0x8779,0x8941,0x8986,
+ 0x8996,0x8abf,0x8af8,0x8acb,0x8b01,0x8afe,0x8aed,0x8b39,
+ 0x8b8a,0x8d08,0x8f38,0x9072,0x9199,0x9276,0x967c,0x96e3,
+ 0x9756,0x97db,0x97ff,0x980b,0x983b,0x9b12,0x9f9c
+};
+
+
+#define UCS4_BMPCJK2MIN 0xfacf
+#define UCS4_BMPCJK2MAX 0xfad9
+
+
+ /* CJK Compatibility - some values not in BMP */
+static const unsigned long ucs4_bmpcjk2decomptab[11] = {
+ 0x2284a,0x22844,0x233d5, 0x3b9d, 0x4018, 0x4039,
+ 0x25249,0x25cd0,0x27ed3, 0x9f43, 0x9f8e
+};
+
+
+#define UCS4_BMPHIMIN 0xfb00
+#define UCS4_BMPHIMAX 0xfefc
+#define UCS4_BMPHIIXMASK 0x7ff
+#define UCS4_BMPHISIZEMASK 0xf800
+#define UCS4_BMPHISIZESHIFT 11
+
+ /* BMP hi decomposition indices - ssss siii iiii iiii */
+static unsigned short ucs4_dbmphiixtab[1021] = {
+0x0800,0x0802,0x0804,0x1006,0x1009,0x080c,0x080e,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0810,0x0812,0x0814,0x0816,0x0818,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x081a,0x0000,0x081c,
+0x001e,0x001f,0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,
+0x0026,0x0027,0x0828,0x082a,0x082c,0x082e,0x0830,0x0832,
+0x0834,0x0836,0x0838,0x083a,0x083c,0x083e,0x0840,0x0000,
+0x0842,0x0844,0x0846,0x0848,0x084a,0x0000,0x084c,0x0000,
+0x084e,0x0850,0x0000,0x0852,0x0854,0x0000,0x0856,0x0858,
+0x085a,0x085c,0x085e,0x0860,0x0862,0x0864,0x0866,0x0868,
+0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,
+0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,
+0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,0x0080,0x0081,
+0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,
+0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,0x0090,0x0091,
+0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,
+0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,0x00a0,0x00a1,
+0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,
+0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,0x00b0,0x00b1,
+0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,
+0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,0x00c0,0x00c1,
+0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,
+0x00ca,0x00cb,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x00cc,0x00cd,0x00ce,0x00cf,0x00d0,
+0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,
+0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,0x00e0,
+0x00e1,0x00e2,0x08e3,0x08e5,0x08e7,0x08e9,0x08eb,0x08ed,
+0x08ef,0x08f1,0x08f3,0x08f5,0x08f7,0x08f9,0x08fb,0x08fd,
+0x08ff,0x0901,0x0903,0x0905,0x0107,0x0108,0x0109,0x010a,
+0x090b,0x090d,0x090f,0x0911,0x0913,0x0915,0x0917,0x0919,
+0x091b,0x091d,0x091f,0x0921,0x0923,0x0925,0x0927,0x0929,
+0x092b,0x092d,0x092f,0x0931,0x0933,0x0935,0x0937,0x0939,
+0x093b,0x093d,0x093f,0x0941,0x0943,0x0945,0x0947,0x0949,
+0x094b,0x094d,0x094f,0x0951,0x0953,0x0955,0x0957,0x0959,
+0x095b,0x095d,0x095f,0x0961,0x0963,0x0965,0x0967,0x0969,
+0x096b,0x096d,0x096f,0x0971,0x0973,0x0975,0x0977,0x0979,
+0x097b,0x097d,0x097f,0x0981,0x0983,0x0985,0x0987,0x0989,
+0x098b,0x098d,0x098f,0x0991,0x0993,0x0995,0x0997,0x0999,
+0x099b,0x099d,0x099f,0x09a1,0x09a3,0x09a5,0x09a7,0x09a9,
+0x09ab,0x09ad,0x09af,0x09b1,0x09b3,0x09b5,0x09b7,0x09b9,
+0x09bb,0x09bd,0x09bf,0x09c1,0x09c3,0x09c5,0x11c7,0x11ca,
+0x11cd,0x11d0,0x11d3,0x11d6,0x09d9,0x09db,0x09dd,0x09df,
+0x09e1,0x09e3,0x09e5,0x09e7,0x09e9,0x09eb,0x09ed,0x09ef,
+0x09f1,0x09f3,0x09f5,0x09f7,0x09f9,0x09fb,0x09fd,0x09ff,
+0x0a01,0x0a03,0x0a05,0x0a07,0x0a09,0x0a0b,0x0a0d,0x0a0f,
+0x0a11,0x0a13,0x0a15,0x0a17,0x0a19,0x0a1b,0x0a1d,0x0a1f,
+0x0a21,0x0a23,0x0a25,0x0a27,0x0a29,0x0a2b,0x0a2d,0x0a2f,
+0x0a31,0x0a33,0x0a35,0x0a37,0x0a39,0x0a3b,0x0a3d,0x0a3f,
+0x0a41,0x0a43,0x0a45,0x0a47,0x0a49,0x0a4b,0x0a4d,0x0a4f,
+0x0a51,0x0a53,0x0a55,0x0a57,0x0a59,0x0a5b,0x0a5d,0x0a5f,
+0x0a61,0x0a63,0x0a65,0x0a67,0x0a69,0x0a6b,0x0a6d,0x0a6f,
+0x0a71,0x0a73,0x0a75,0x0a77,0x0a79,0x0a7b,0x0a7d,0x0a7f,
+0x0a81,0x0a83,0x0a85,0x0a87,0x0a89,0x0a8b,0x0a8d,0x0a8f,
+0x0a91,0x0a93,0x0a95,0x0a97,0x0a99,0x0a9b,0x0a9d,0x0a9f,
+0x0aa1,0x0aa3,0x0aa5,0x0aa7,0x0aa9,0x0aab,0x0aad,0x0aaf,
+0x0ab1,0x0ab3,0x0ab5,0x0ab7,0x0ab9,0x0abb,0x0abd,0x0abf,
+0x0ac1,0x0ac3,0x0ac5,0x0ac7,0x0ac9,0x0acb,0x0acd,0x0acf,
+0x0ad1,0x0ad3,0x0ad5,0x0ad7,0x0ad9,0x0adb,0x0add,0x0adf,
+0x0ae1,0x0ae3,0x0ae5,0x0ae7,0x0ae9,0x0aeb,0x0aed,0x0aef,
+0x0af1,0x0af3,0x12f5,0x12f8,0x12fb,0x0afe,0x0b00,0x0b02,
+0x0b04,0x0b06,0x0b08,0x0b0a,0x0b0c,0x0b0e,0x0b10,0x0b12,
+0x0b14,0x0b16,0x0b18,0x0b1a,0x0b1c,0x0b1e,0x0b20,0x0b22,
+0x0b24,0x0b26,0x0b28,0x0b2a,0x0b2c,0x0b2e,0x0b30,0x0b32,
+0x0b34,0x0b36,0x0b38,0x0b3a,0x0b3c,0x0b3e,0x0b40,0x0b42,
+0x0b44,0x0b46,0x0b48,0x0b4a,0x0b4c,0x0b4e,0x0b50,0x0b52,
+0x0b54,0x0b56,0x0b58,0x0b5a,0x0b5c,0x0b5e,0x0b60,0x0b62,
+0x0b64,0x0b66,0x0b68,0x0b6a,0x0b6c,0x0b6e,0x0b70,0x0b72,
+0x0b74,0x0b76,0x0b78,0x0b7a,0x0b7c,0x0b7e,0x0b80,0x0b82,
+0x0b84,0x0b86,0x0b88,0x0b8a,0x0b8c,0x0b8e,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x1390,0x1393,0x1396,0x1399,0x139c,0x139f,0x13a2,0x13a5,
+0x13a8,0x13ab,0x13ae,0x13b1,0x13b4,0x13b7,0x13ba,0x13bd,
+0x13c0,0x13c3,0x13c6,0x13c9,0x13cc,0x13cf,0x13d2,0x13d5,
+0x13d8,0x13db,0x13de,0x13e1,0x13e4,0x13e7,0x13ea,0x13ed,
+0x13f0,0x13f3,0x13f6,0x13f9,0x13fc,0x13ff,0x1402,0x1405,
+0x1408,0x140b,0x140e,0x1411,0x1414,0x1417,0x141a,0x141d,
+0x1420,0x1423,0x1426,0x1429,0x142c,0x142f,0x1432,0x1435,
+0x1438,0x143b,0x143e,0x1441,0x1444,0x1447,0x144a,0x144d,
+0x0000,0x0000,0x1450,0x1453,0x1456,0x1459,0x145c,0x145f,
+0x1462,0x1465,0x1468,0x146b,0x146e,0x1471,0x1474,0x1477,
+0x147a,0x147d,0x1480,0x1483,0x1486,0x1489,0x148c,0x148f,
+0x1492,0x1495,0x1498,0x149b,0x149e,0x14a1,0x14a4,0x14a7,
+0x14aa,0x14ad,0x14b0,0x14b3,0x14b6,0x14b9,0x14bc,0x14bf,
+0x14c2,0x14c5,0x14c8,0x14cb,0x14ce,0x14d1,0x14d4,0x14d7,
+0x14da,0x14dd,0x14e0,0x14e3,0x14e6,0x14e9,0x14ec,0x14ef,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x14f2,0x14f5,0x1cf8,0x1cfc,0x1d00,0x1d04,0x1d08,0x1d0c,
+0x1d10,0x1514,0x8d17,0x3d29,0x1d31,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0535,0x0536,0x0537,0x0538,0x0539,0x053a,0x053b,0x053c,
+0x053d,0x053e,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x053f,0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,
+0x0547,0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,
+0x054f,0x0550,0x0551,0x0552,0x0553,0x0000,0x0000,0x0554,
+0x0555,0x0556,0x0557,0x0558,0x0559,0x055a,0x055b,0x055c,
+0x055d,0x055e,0x055f,0x0000,0x0560,0x0561,0x0562,0x0563,
+0x0564,0x0565,0x0566,0x0567,0x0568,0x0569,0x056a,0x056b,
+0x056c,0x056d,0x056e,0x056f,0x0570,0x0571,0x0572,0x0000,
+0x0573,0x0574,0x0575,0x0576,0x0000,0x0000,0x0000,0x0000,
+0x0d77,0x0d79,0x0d7b,0x0000,0x0d7d,0x0000,0x0d7f,0x0d81,
+0x0d83,0x0d85,0x0d87,0x0d89,0x0d8b,0x0d8d,0x0d8f,0x0d91,
+0x0593,0x0594,0x0595,0x0596,0x0597,0x0598,0x0599,0x059a,
+0x059b,0x059c,0x059d,0x059e,0x059f,0x05a0,0x05a1,0x05a2,
+0x05a3,0x05a4,0x05a5,0x05a6,0x05a7,0x05a8,0x05a9,0x05aa,
+0x05ab,0x05ac,0x05ad,0x05ae,0x05af,0x05b0,0x05b1,0x05b2,
+0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,0x05b8,0x05b9,0x05ba,
+0x05bb,0x05bc,0x05bd,0x05be,0x05bf,0x05c0,0x05c1,0x05c2,
+0x05c3,0x05c4,0x05c5,0x05c6,0x05c7,0x05c8,0x05c9,0x05ca,
+0x05cb,0x05cc,0x05cd,0x05ce,0x05cf,0x05d0,0x05d1,0x05d2,
+0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,0x05d8,0x05d9,0x05da,
+0x05db,0x05dc,0x05dd,0x05de,0x05df,0x05e0,0x05e1,0x05e2,
+0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,0x05e8,0x05e9,0x05ea,
+0x05eb,0x05ec,0x05ed,0x05ee,0x05ef,0x05f0,0x05f1,0x05f2,
+0x05f3,0x05f4,0x05f5,0x05f6,0x05f7,0x05f8,0x05f9,0x05fa,
+0x05fb,0x05fc,0x05fd,0x05fe,0x05ff,0x0600,0x0601,0x0602,
+0x0603,0x0604,0x0605,0x0606,0x0607,0x0e08,0x0e0a,0x0e0c,
+0x0e0e,0x0e10,0x0e12,0x0e14,0x0e16
+};
+
+ /* BMP high decompositions */
+static unsigned short ucs4_dbmphitab[1560] = {
+0x0066,0x0066,0x0066,0x0069,0x0066,0x006c,0x0066,0x0066,
+0x0069,0x0066,0x0066,0x006c,0x017f,0x0074,0x0073,0x0074,
+0x0574,0x0576,0x0574,0x0565,0x0574,0x056b,0x057e,0x0576,
+0x0574,0x056d,0x05d9,0x05b4,0x05f2,0x05b7,0x05e2,0x05d0,
+0x05d3,0x05d4,0x05db,0x05dc,0x05dd,0x05e8,0x05ea,0x002b,
+0x05e9,0x05c1,0x05e9,0x05c2,0xfb49,0x05c1,0xfb49,0x05c2,
+0x05d0,0x05b7,0x05d0,0x05b8,0x05d0,0x05bc,0x05d1,0x05bc,
+0x05d2,0x05bc,0x05d3,0x05bc,0x05d4,0x05bc,0x05d5,0x05bc,
+0x05d6,0x05bc,0x05d8,0x05bc,0x05d9,0x05bc,0x05da,0x05bc,
+0x05db,0x05bc,0x05dc,0x05bc,0x05de,0x05bc,0x05e0,0x05bc,
+0x05e1,0x05bc,0x05e3,0x05bc,0x05e4,0x05bc,0x05e6,0x05bc,
+0x05e7,0x05bc,0x05e8,0x05bc,0x05e9,0x05bc,0x05ea,0x05bc,
+0x05d5,0x05b9,0x05d1,0x05bf,0x05db,0x05bf,0x05e4,0x05bf,
+0x05d0,0x05dc,0x0671,0x0671,0x067b,0x067b,0x067b,0x067b,
+0x067e,0x067e,0x067e,0x067e,0x0680,0x0680,0x0680,0x0680,
+0x067a,0x067a,0x067a,0x067a,0x067f,0x067f,0x067f,0x067f,
+0x0679,0x0679,0x0679,0x0679,0x06a4,0x06a4,0x06a4,0x06a4,
+0x06a6,0x06a6,0x06a6,0x06a6,0x0684,0x0684,0x0684,0x0684,
+0x0683,0x0683,0x0683,0x0683,0x0686,0x0686,0x0686,0x0686,
+0x0687,0x0687,0x0687,0x0687,0x068d,0x068d,0x068c,0x068c,
+0x068e,0x068e,0x0688,0x0688,0x0698,0x0698,0x0691,0x0691,
+0x06a9,0x06a9,0x06a9,0x06a9,0x06af,0x06af,0x06af,0x06af,
+0x06b3,0x06b3,0x06b3,0x06b3,0x06b1,0x06b1,0x06b1,0x06b1,
+0x06ba,0x06ba,0x06bb,0x06bb,0x06bb,0x06bb,0x06c0,0x06c0,
+0x06c1,0x06c1,0x06c1,0x06c1,0x06be,0x06be,0x06be,0x06be,
+0x06d2,0x06d2,0x06d3,0x06d3,0x06ad,0x06ad,0x06ad,0x06ad,
+0x06c7,0x06c7,0x06c6,0x06c6,0x06c8,0x06c8,0x0677,0x06cb,
+0x06cb,0x06c5,0x06c5,0x06c9,0x06c9,0x06d0,0x06d0,0x06d0,
+0x06d0,0x0649,0x0649,0x0626,0x0627,0x0626,0x0627,0x0626,
+0x06d5,0x0626,0x06d5,0x0626,0x0648,0x0626,0x0648,0x0626,
+0x06c7,0x0626,0x06c7,0x0626,0x06c6,0x0626,0x06c6,0x0626,
+0x06c8,0x0626,0x06c8,0x0626,0x06d0,0x0626,0x06d0,0x0626,
+0x06d0,0x0626,0x0649,0x0626,0x0649,0x0626,0x0649,0x06cc,
+0x06cc,0x06cc,0x06cc,0x0626,0x062c,0x0626,0x062d,0x0626,
+0x0645,0x0626,0x0649,0x0626,0x064a,0x0628,0x062c,0x0628,
+0x062d,0x0628,0x062e,0x0628,0x0645,0x0628,0x0649,0x0628,
+0x064a,0x062a,0x062c,0x062a,0x062d,0x062a,0x062e,0x062a,
+0x0645,0x062a,0x0649,0x062a,0x064a,0x062b,0x062c,0x062b,
+0x0645,0x062b,0x0649,0x062b,0x064a,0x062c,0x062d,0x062c,
+0x0645,0x062d,0x062c,0x062d,0x0645,0x062e,0x062c,0x062e,
+0x062d,0x062e,0x0645,0x0633,0x062c,0x0633,0x062d,0x0633,
+0x062e,0x0633,0x0645,0x0635,0x062d,0x0635,0x0645,0x0636,
+0x062c,0x0636,0x062d,0x0636,0x062e,0x0636,0x0645,0x0637,
+0x062d,0x0637,0x0645,0x0638,0x0645,0x0639,0x062c,0x0639,
+0x0645,0x063a,0x062c,0x063a,0x0645,0x0641,0x062c,0x0641,
+0x062d,0x0641,0x062e,0x0641,0x0645,0x0641,0x0649,0x0641,
+0x064a,0x0642,0x062d,0x0642,0x0645,0x0642,0x0649,0x0642,
+0x064a,0x0643,0x0627,0x0643,0x062c,0x0643,0x062d,0x0643,
+0x062e,0x0643,0x0644,0x0643,0x0645,0x0643,0x0649,0x0643,
+0x064a,0x0644,0x062c,0x0644,0x062d,0x0644,0x062e,0x0644,
+0x0645,0x0644,0x0649,0x0644,0x064a,0x0645,0x062c,0x0645,
+0x062d,0x0645,0x062e,0x0645,0x0645,0x0645,0x0649,0x0645,
+0x064a,0x0646,0x062c,0x0646,0x062d,0x0646,0x062e,0x0646,
+0x0645,0x0646,0x0649,0x0646,0x064a,0x0647,0x062c,0x0647,
+0x0645,0x0647,0x0649,0x0647,0x064a,0x064a,0x062c,0x064a,
+0x062d,0x064a,0x062e,0x064a,0x0645,0x064a,0x0649,0x064a,
+0x064a,0x0630,0x0670,0x0631,0x0670,0x0649,0x0670,0x0020,
+0x064c,0x0651,0x0020,0x064d,0x0651,0x0020,0x064e,0x0651,
+0x0020,0x064f,0x0651,0x0020,0x0650,0x0651,0x0020,0x0651,
+0x0670,0x0626,0x0631,0x0626,0x0632,0x0626,0x0645,0x0626,
+0x0646,0x0626,0x0649,0x0626,0x064a,0x0628,0x0631,0x0628,
+0x0632,0x0628,0x0645,0x0628,0x0646,0x0628,0x0649,0x0628,
+0x064a,0x062a,0x0631,0x062a,0x0632,0x062a,0x0645,0x062a,
+0x0646,0x062a,0x0649,0x062a,0x064a,0x062b,0x0631,0x062b,
+0x0632,0x062b,0x0645,0x062b,0x0646,0x062b,0x0649,0x062b,
+0x064a,0x0641,0x0649,0x0641,0x064a,0x0642,0x0649,0x0642,
+0x064a,0x0643,0x0627,0x0643,0x0644,0x0643,0x0645,0x0643,
+0x0649,0x0643,0x064a,0x0644,0x0645,0x0644,0x0649,0x0644,
+0x064a,0x0645,0x0627,0x0645,0x0645,0x0646,0x0631,0x0646,
+0x0632,0x0646,0x0645,0x0646,0x0646,0x0646,0x0649,0x0646,
+0x064a,0x0649,0x0670,0x064a,0x0631,0x064a,0x0632,0x064a,
+0x0645,0x064a,0x0646,0x064a,0x0649,0x064a,0x064a,0x0626,
+0x062c,0x0626,0x062d,0x0626,0x062e,0x0626,0x0645,0x0626,
+0x0647,0x0628,0x062c,0x0628,0x062d,0x0628,0x062e,0x0628,
+0x0645,0x0628,0x0647,0x062a,0x062c,0x062a,0x062d,0x062a,
+0x062e,0x062a,0x0645,0x062a,0x0647,0x062b,0x0645,0x062c,
+0x062d,0x062c,0x0645,0x062d,0x062c,0x062d,0x0645,0x062e,
+0x062c,0x062e,0x0645,0x0633,0x062c,0x0633,0x062d,0x0633,
+0x062e,0x0633,0x0645,0x0635,0x062d,0x0635,0x062e,0x0635,
+0x0645,0x0636,0x062c,0x0636,0x062d,0x0636,0x062e,0x0636,
+0x0645,0x0637,0x062d,0x0638,0x0645,0x0639,0x062c,0x0639,
+0x0645,0x063a,0x062c,0x063a,0x0645,0x0641,0x062c,0x0641,
+0x062d,0x0641,0x062e,0x0641,0x0645,0x0642,0x062d,0x0642,
+0x0645,0x0643,0x062c,0x0643,0x062d,0x0643,0x062e,0x0643,
+0x0644,0x0643,0x0645,0x0644,0x062c,0x0644,0x062d,0x0644,
+0x062e,0x0644,0x0645,0x0644,0x0647,0x0645,0x062c,0x0645,
+0x062d,0x0645,0x062e,0x0645,0x0645,0x0646,0x062c,0x0646,
+0x062d,0x0646,0x062e,0x0646,0x0645,0x0646,0x0647,0x0647,
+0x062c,0x0647,0x0645,0x0647,0x0670,0x064a,0x062c,0x064a,
+0x062d,0x064a,0x062e,0x064a,0x0645,0x064a,0x0647,0x0626,
+0x0645,0x0626,0x0647,0x0628,0x0645,0x0628,0x0647,0x062a,
+0x0645,0x062a,0x0647,0x062b,0x0645,0x062b,0x0647,0x0633,
+0x0645,0x0633,0x0647,0x0634,0x0645,0x0634,0x0647,0x0643,
+0x0644,0x0643,0x0645,0x0644,0x0645,0x0646,0x0645,0x0646,
+0x0647,0x064a,0x0645,0x064a,0x0647,0x0640,0x064e,0x0651,
+0x0640,0x064f,0x0651,0x0640,0x0650,0x0651,0x0637,0x0649,
+0x0637,0x064a,0x0639,0x0649,0x0639,0x064a,0x063a,0x0649,
+0x063a,0x064a,0x0633,0x0649,0x0633,0x064a,0x0634,0x0649,
+0x0634,0x064a,0x062d,0x0649,0x062d,0x064a,0x062c,0x0649,
+0x062c,0x064a,0x062e,0x0649,0x062e,0x064a,0x0635,0x0649,
+0x0635,0x064a,0x0636,0x0649,0x0636,0x064a,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0634,0x0631,
+0x0633,0x0631,0x0635,0x0631,0x0636,0x0631,0x0637,0x0649,
+0x0637,0x064a,0x0639,0x0649,0x0639,0x064a,0x063a,0x0649,
+0x063a,0x064a,0x0633,0x0649,0x0633,0x064a,0x0634,0x0649,
+0x0634,0x064a,0x062d,0x0649,0x062d,0x064a,0x062c,0x0649,
+0x062c,0x064a,0x062e,0x0649,0x062e,0x064a,0x0635,0x0649,
+0x0635,0x064a,0x0636,0x0649,0x0636,0x064a,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0634,0x0631,
+0x0633,0x0631,0x0635,0x0631,0x0636,0x0631,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0633,0x0647,
+0x0634,0x0647,0x0637,0x0645,0x0633,0x062c,0x0633,0x062d,
+0x0633,0x062e,0x0634,0x062c,0x0634,0x062d,0x0634,0x062e,
+0x0637,0x0645,0x0638,0x0645,0x0627,0x064b,0x0627,0x064b,
+0x062a,0x062c,0x0645,0x062a,0x062d,0x062c,0x062a,0x062d,
+0x062c,0x062a,0x062d,0x0645,0x062a,0x062e,0x0645,0x062a,
+0x0645,0x062c,0x062a,0x0645,0x062d,0x062a,0x0645,0x062e,
+0x062c,0x0645,0x062d,0x062c,0x0645,0x062d,0x062d,0x0645,
+0x064a,0x062d,0x0645,0x0649,0x0633,0x062d,0x062c,0x0633,
+0x062c,0x062d,0x0633,0x062c,0x0649,0x0633,0x0645,0x062d,
+0x0633,0x0645,0x062d,0x0633,0x0645,0x062c,0x0633,0x0645,
+0x0645,0x0633,0x0645,0x0645,0x0635,0x062d,0x062d,0x0635,
+0x062d,0x062d,0x0635,0x0645,0x0645,0x0634,0x062d,0x0645,
+0x0634,0x062d,0x0645,0x0634,0x062c,0x064a,0x0634,0x0645,
+0x062e,0x0634,0x0645,0x062e,0x0634,0x0645,0x0645,0x0634,
+0x0645,0x0645,0x0636,0x062d,0x0649,0x0636,0x062e,0x0645,
+0x0636,0x062e,0x0645,0x0637,0x0645,0x062d,0x0637,0x0645,
+0x062d,0x0637,0x0645,0x0645,0x0637,0x0645,0x064a,0x0639,
+0x062c,0x0645,0x0639,0x0645,0x0645,0x0639,0x0645,0x0645,
+0x0639,0x0645,0x0649,0x063a,0x0645,0x0645,0x063a,0x0645,
+0x064a,0x063a,0x0645,0x0649,0x0641,0x062e,0x0645,0x0641,
+0x062e,0x0645,0x0642,0x0645,0x062d,0x0642,0x0645,0x0645,
+0x0644,0x062d,0x0645,0x0644,0x062d,0x064a,0x0644,0x062d,
+0x0649,0x0644,0x062c,0x062c,0x0644,0x062c,0x062c,0x0644,
+0x062e,0x0645,0x0644,0x062e,0x0645,0x0644,0x0645,0x062d,
+0x0644,0x0645,0x062d,0x0645,0x062d,0x062c,0x0645,0x062d,
+0x0645,0x0645,0x062d,0x064a,0x0645,0x062c,0x062d,0x0645,
+0x062c,0x0645,0x0645,0x062e,0x062c,0x0645,0x062e,0x0645,
+0x0645,0x062c,0x062e,0x0647,0x0645,0x062c,0x0647,0x0645,
+0x0645,0x0646,0x062d,0x0645,0x0646,0x062d,0x0649,0x0646,
+0x062c,0x0645,0x0646,0x062c,0x0645,0x0646,0x062c,0x0649,
+0x0646,0x0645,0x064a,0x0646,0x0645,0x0649,0x064a,0x0645,
+0x0645,0x064a,0x0645,0x0645,0x0628,0x062e,0x064a,0x062a,
+0x062c,0x064a,0x062a,0x062c,0x0649,0x062a,0x062e,0x064a,
+0x062a,0x062e,0x0649,0x062a,0x0645,0x064a,0x062a,0x0645,
+0x0649,0x062c,0x0645,0x064a,0x062c,0x062d,0x0649,0x062c,
+0x0645,0x0649,0x0633,0x062e,0x0649,0x0635,0x062d,0x064a,
+0x0634,0x062d,0x064a,0x0636,0x062d,0x064a,0x0644,0x062c,
+0x064a,0x0644,0x0645,0x064a,0x064a,0x062d,0x064a,0x064a,
+0x062c,0x064a,0x064a,0x0645,0x064a,0x0645,0x0645,0x064a,
+0x0642,0x0645,0x064a,0x0646,0x062d,0x064a,0x0642,0x0645,
+0x062d,0x0644,0x062d,0x0645,0x0639,0x0645,0x064a,0x0643,
+0x0645,0x064a,0x0646,0x062c,0x062d,0x0645,0x062e,0x064a,
+0x0644,0x062c,0x0645,0x0643,0x0645,0x0645,0x0644,0x062c,
+0x0645,0x0646,0x062c,0x062d,0x062c,0x062d,0x064a,0x062d,
+0x062c,0x064a,0x0645,0x062c,0x064a,0x0641,0x0645,0x064a,
+0x0628,0x062d,0x064a,0x0643,0x0645,0x0645,0x0639,0x062c,
+0x0645,0x0635,0x0645,0x0645,0x0633,0x062e,0x064a,0x0646,
+0x062c,0x064a,0x0635,0x0644,0x06d2,0x0642,0x0644,0x06d2,
+0x0627,0x0644,0x0644,0x0647,0x0627,0x0643,0x0628,0x0631,
+0x0645,0x062d,0x0645,0x062f,0x0635,0x0644,0x0639,0x0645,
+0x0631,0x0633,0x0648,0x0644,0x0639,0x0644,0x064a,0x0647,
+0x0648,0x0633,0x0644,0x0645,0x0635,0x0644,0x0649,0x0635,
+0x0644,0x0649,0x0020,0x0627,0x0644,0x0644,0x0647,0x0020,
+0x0639,0x0644,0x064a,0x0647,0x0020,0x0648,0x0633,0x0644,
+0x0645,0x062c,0x0644,0x0020,0x062c,0x0644,0x0627,0x0644,
+0x0647,0x0631,0x06cc,0x0627,0x0644,0x002c,0x3001,0x3002,
+0x003a,0x003b,0x0021,0x003f,0x3016,0x3017,0x2026,0x2025,
+0x2014,0x2013,0x005f,0x005f,0x0028,0x0029,0x007b,0x007d,
+0x3014,0x3015,0x3010,0x3011,0x300a,0x300b,0x3008,0x3009,
+0x300c,0x300d,0x300e,0x300f,0x005b,0x005d,0x203e,0x203e,
+0x203e,0x203e,0x005f,0x005f,0x005f,0x002c,0x3001,0x002e,
+0x003b,0x003a,0x003f,0x0021,0x2014,0x0028,0x0029,0x007b,
+0x007d,0x3014,0x3015,0x0023,0x0026,0x002a,0x002b,0x002d,
+0x003c,0x003e,0x003d,0x005c,0x0024,0x0025,0x0040,0x0020,
+0x064b,0x0640,0x064b,0x0020,0x064c,0x0020,0x064d,0x0020,
+0x064e,0x0640,0x064e,0x0020,0x064f,0x0640,0x064f,0x0020,
+0x0650,0x0640,0x0650,0x0020,0x0651,0x0640,0x0651,0x0020,
+0x0652,0x0640,0x0652,0x0621,0x0622,0x0622,0x0623,0x0623,
+0x0624,0x0624,0x0625,0x0625,0x0626,0x0626,0x0626,0x0626,
+0x0627,0x0627,0x0628,0x0628,0x0628,0x0628,0x0629,0x0629,
+0x062a,0x062a,0x062a,0x062a,0x062b,0x062b,0x062b,0x062b,
+0x062c,0x062c,0x062c,0x062c,0x062d,0x062d,0x062d,0x062d,
+0x062e,0x062e,0x062e,0x062e,0x062f,0x062f,0x0630,0x0630,
+0x0631,0x0631,0x0632,0x0632,0x0633,0x0633,0x0633,0x0633,
+0x0634,0x0634,0x0634,0x0634,0x0635,0x0635,0x0635,0x0635,
+0x0636,0x0636,0x0636,0x0636,0x0637,0x0637,0x0637,0x0637,
+0x0638,0x0638,0x0638,0x0638,0x0639,0x0639,0x0639,0x0639,
+0x063a,0x063a,0x063a,0x063a,0x0641,0x0641,0x0641,0x0641,
+0x0642,0x0642,0x0642,0x0642,0x0643,0x0643,0x0643,0x0643,
+0x0644,0x0644,0x0644,0x0644,0x0645,0x0645,0x0645,0x0645,
+0x0646,0x0646,0x0646,0x0646,0x0647,0x0647,0x0647,0x0647,
+0x0648,0x0648,0x0649,0x0649,0x064a,0x064a,0x064a,0x064a,
+0x0644,0x0622,0x0644,0x0622,0x0644,0x0623,0x0644,0x0623,
+0x0644,0x0625,0x0644,0x0625,0x0644,0x0627,0x0644,0x0627,
+};
+
+
+#define UCS4_BMPHALFFULLMIN 0xff00
+#define UCS4_BMPHALFFULLMAX 0xffef
+
+static const unsigned short ucs4_bmphalffulldecomptab[240] = {
+ 0x0000,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,
+ 0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
+ 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,
+ 0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2985,
+ 0x2986,0x3002,0x300c,0x300d,0x3001,0x30fb,0x30f2,0x30a1,
+ 0x30a3,0x30a5,0x30a7,0x30a9,0x30e3,0x30e5,0x30e7,0x30c3,
+ 0x30fc,0x30a2,0x30a4,0x30a6,0x30a8,0x30aa,0x30ab,0x30ad,
+ 0x30af,0x30b1,0x30b3,0x30b5,0x30b7,0x30b9,0x30bb,0x30bd,
+ 0x30bf,0x30c1,0x30c4,0x30c6,0x30c8,0x30ca,0x30cb,0x30cc,
+ 0x30cd,0x30ce,0x30cf,0x30d2,0x30d5,0x30d8,0x30db,0x30de,
+ 0x30df,0x30e0,0x30e1,0x30e2,0x30e4,0x30e6,0x30e8,0x30e9,
+ 0x30ea,0x30eb,0x30ec,0x30ed,0x30ef,0x30f3,0x3099,0x309a,
+ 0x3164,0x3131,0x3132,0x3133,0x3134,0x3135,0x3136,0x3137,
+ 0x3138,0x3139,0x313a,0x313b,0x313c,0x313d,0x313e,0x313f,
+ 0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,
+ 0x3148,0x3149,0x314a,0x314b,0x314c,0x314d,0x314e,0x0000,
+ 0x0000,0x0000,0x314f,0x3150,0x3151,0x3152,0x3153,0x3154,
+ 0x0000,0x0000,0x3155,0x3156,0x3157,0x3158,0x3159,0x315a,
+ 0x0000,0x0000,0x315b,0x315c,0x315d,0x315e,0x315f,0x3160,
+ 0x0000,0x0000,0x3161,0x3162,0x3163,0x0000,0x0000,0x0000,
+ 0x00a2,0x00a3,0x00ac,0x00af,0x00a6,0x00a5,0x20a9,0x0000,
+ 0x2502,0x2190,0x2191,0x2192,0x2193,0x25a0,0x25cb,0x0000
+};
+
+/* SMP decompositions */
+
+ /* Musical */
+
+#define UCS4_SMPMUSIC1MIN 0x1d15e
+#define UCS4_SMPMUSIC1MAX 0x1d164
+
+static const unsigned long ucs4_smpmusic1decomptab[7][2] = {
+ {0x1d157,0x1d165},{0x1d158,0x1d165},{0x1d15f,0x1d16e},{0x1d15f,0x1d16f},
+ {0x1d15f,0x1d170},{0x1d15f,0x1d171},{0x1d15f,0x1d172}
+};
+
+
+#define UCS4_SMPMUSIC2MIN 0x1d1bb
+#define UCS4_SMPMUSIC2MAX 0x1d1c0
+
+static const unsigned long ucs4_smpmusic2decomptab[6][2] = {
+ {0x1d1b9,0x1d165},{0x1d1ba,0x1d165},{0x1d1bb,0x1d16e},
+ {0x1d1bc,0x1d16e},{0x1d1bb,0x1d16f},{0x1d1bc,0x1d16f}
+};
+
+
+#define UCS4_SMPMATHMIN 0x1d400
+#define UCS4_SMPMATHMAX 0x1d7ff
+
+ /* Mathematical - 0 means hole (no decomposition) */
+static const unsigned short ucs4_smpmathdecomptab[1024] = {
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0000,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0000,0x0043,0x0044,
+ 0x0000,0x0000,0x0047,0x0000,0x004a,0x004b,0x0000,0x0000,
+ 0x004e,0x004f,0x0050,0x0051,0x0000,0x0053,0x0054,0x0055,
+ 0x0000,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0000,0x0066,0x0000,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x0000,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0000,0x0044,
+ 0x0045,0x0046,0x0047,0x0000,0x004a,0x004b,0x004c,0x004d,
+ 0x0000,0x004e,0x004f,0x0050,0x0051,0x0000,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x0000,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0000,0x0044,0x0045,0x0046,0x0047,0x0000,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x0000,0x004f,0x0000,
+ 0x0000,0x0000,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x0000,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0131,0x0237,0x0000,0x0000,
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,
+ 0x03f1,0x03d6,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,
+ 0x039f,0x03a0,0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,
+ 0x03a7,0x03a8,0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,
+ 0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,
+ 0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,
+ 0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,
+ 0x03f0,0x03d5,0x03f1,0x03d6,0x0391,0x0392,0x0393,0x0394,
+ 0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,
+ 0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03f4,0x03a3,0x03a4,
+ 0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x2207,0x03b1,0x03b2,
+ 0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,
+ 0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c2,
+ 0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x2202,
+ 0x03f5,0x03d1,0x03f0,0x03d5,0x03f1,0x03d6,0x0391,0x0392,
+ 0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03f4,
+ 0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x2207,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,
+ 0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,0x03f1,0x03d6,
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,
+ 0x03f1,0x03d6,0x03dc,0x03dd,0x0000,0x0000,0x0030,0x0031,
+ 0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,
+ 0x0036,0x0037,0x0038,0x0039,0x0030,0x0031,0x0032,0x0033,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x0030,0x0031,
+ 0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039
+};
+
+/* SIP decompositions */
+
+#define UCS4_SIPMIN 0x2f800
+#define UCS4_SIPMAX 0x2fa1d
+
+ /* CJK compatibility ideographs - no holes */
+static const unsigned long ucs4_sipdecomptab[542] = {
+ 0x4e3d, 0x4e38, 0x4e41,0x20122, 0x4f60, 0x4fae, 0x4fbb, 0x5002,
+ 0x507a, 0x5099, 0x50e7, 0x50cf, 0x349e,0x2063a, 0x514d, 0x5154,
+ 0x5164, 0x5177,0x2051c, 0x34b9, 0x5167, 0x518d,0x2054b, 0x5197,
+ 0x51a4, 0x4ecc, 0x51ac, 0x51b5,0x291df, 0x51f5, 0x5203, 0x34df,
+ 0x523b, 0x5246, 0x5272, 0x5277, 0x3515, 0x52c7, 0x52c9, 0x52e4,
+ 0x52fa, 0x5305, 0x5306, 0x5317, 0x5349, 0x5351, 0x535a, 0x5373,
+ 0x537d, 0x537f, 0x537f, 0x537f,0x20a2c, 0x7070, 0x53ca, 0x53df,
+ 0x20b63, 0x53eb, 0x53f1, 0x5406, 0x549e, 0x5438, 0x5448, 0x5468,
+ 0x54a2, 0x54f6, 0x5510, 0x5553, 0x5563, 0x5584, 0x5584, 0x5599,
+ 0x55ab, 0x55b3, 0x55c2, 0x5716, 0x5606, 0x5717, 0x5651, 0x5674,
+ 0x5207, 0x58ee, 0x57ce, 0x57f4, 0x580d, 0x578b, 0x5832, 0x5831,
+ 0x58ac,0x214e4, 0x58f2, 0x58f7, 0x5906, 0x591a, 0x5922, 0x5962,
+ 0x216a8,0x216ea, 0x59ec, 0x5a1b, 0x5a27, 0x59d8, 0x5a66, 0x36ee,
+ 0x36fc, 0x5b08, 0x5b3e, 0x5b3e,0x219c8, 0x5bc3, 0x5bd8, 0x5be7,
+ 0x5bf3,0x21b18, 0x5bff, 0x5c06, 0x5f53, 0x5c22, 0x3781, 0x5c60,
+ 0x5c6e, 0x5cc0, 0x5c8d,0x21de4, 0x5d43,0x21de6, 0x5d6e, 0x5d6b,
+ 0x5d7c, 0x5de1, 0x5de2, 0x382f, 0x5dfd, 0x5e28, 0x5e3d, 0x5e69,
+ 0x3862,0x22183, 0x387c, 0x5eb0, 0x5eb3, 0x5eb6, 0x5eca,0x2a392,
+ 0x5efe,0x22331,0x22331, 0x8201, 0x5f22, 0x5f22, 0x38c7,0x232b8,
+ 0x261da, 0x5f62, 0x5f6b, 0x38e3, 0x5f9a, 0x5fcd, 0x5fd7, 0x5ff9,
+ 0x6081, 0x393a, 0x391c, 0x6094,0x226d4, 0x60c7, 0x6148, 0x614c,
+ 0x614e, 0x614c, 0x617a, 0x618e, 0x61b2, 0x61a4, 0x61af, 0x61de,
+ 0x61f2, 0x61f6, 0x6210, 0x621b, 0x625d, 0x62b1, 0x62d4, 0x6350,
+ 0x22b0c, 0x633d, 0x62fc, 0x6368, 0x6383, 0x63e4,0x22bf1, 0x6422,
+ 0x63c5, 0x63a9, 0x3a2e, 0x6469, 0x647e, 0x649d, 0x6477, 0x3a6c,
+ 0x654f, 0x656c,0x2300a, 0x65e3, 0x66f8, 0x6649, 0x3b19, 0x6691,
+ 0x3b08, 0x3ae4, 0x5192, 0x5195, 0x6700, 0x669c, 0x80ad, 0x43d9,
+ 0x6717, 0x671b, 0x6721, 0x675e, 0x6753,0x233c3, 0x3b49, 0x67fa,
+ 0x6785, 0x6852, 0x6885,0x2346d, 0x688e, 0x681f, 0x6914, 0x3b9d,
+ 0x6942, 0x69a3, 0x69ea, 0x6aa8,0x236a3, 0x6adb, 0x3c18, 0x6b21,
+ 0x238a7, 0x6b54, 0x3c4e, 0x6b72, 0x6b9f, 0x6bba, 0x6bbb,0x23a8d,
+ 0x21d0b,0x23afa, 0x6c4e,0x23cbc, 0x6cbf, 0x6ccd, 0x6c67, 0x6d16,
+ 0x6d3e, 0x6d77, 0x6d41, 0x6d69, 0x6d78, 0x6d85,0x23d1e, 0x6d34,
+ 0x6e2f, 0x6e6e, 0x3d33, 0x6ecb, 0x6ec7,0x23ed1, 0x6df9, 0x6f6e,
+ 0x23f5e,0x23f8e, 0x6fc6, 0x7039, 0x701e, 0x701b, 0x3d96, 0x704a,
+ 0x707d, 0x7077, 0x70ad,0x20525, 0x7145,0x24263, 0x719c,0x243ab,
+ 0x7228, 0x7235, 0x7250,0x24608, 0x7280, 0x7295,0x24735,0x24814,
+ 0x737a, 0x738b, 0x3eac, 0x73a5, 0x3eb8, 0x3eb8, 0x7447, 0x745c,
+ 0x7471, 0x7485, 0x74ca, 0x3f1b, 0x7524,0x24c36, 0x753e,0x24c92,
+ 0x7570,0x2219f, 0x7610,0x24fa1,0x24fb8,0x25044, 0x3ffc, 0x4008,
+ 0x76f4,0x250f3,0x250f2,0x25119,0x25133, 0x771e, 0x771f, 0x771f,
+ 0x774a, 0x4039, 0x778b, 0x4046, 0x4096,0x2541d, 0x784e, 0x788c,
+ 0x78cc, 0x40e3,0x25626, 0x7956,0x2569a,0x256c5, 0x798f, 0x79eb,
+ 0x412f, 0x7a40, 0x7a4a, 0x7a4f,0x2597c,0x25aa7,0x25aa7, 0x7aee,
+ 0x4202,0x25bab, 0x7bc6, 0x7bc9, 0x4227,0x25c80, 0x7cd2, 0x42a0,
+ 0x7ce8, 0x7ce3, 0x7d00,0x25f86, 0x7d63, 0x4301, 0x7dc7, 0x7e02,
+ 0x7e45, 0x4334,0x26228,0x26247, 0x4359,0x262d9, 0x7f7a,0x2633e,
+ 0x7f95, 0x7ffa, 0x8005,0x264da,0x26523, 0x8060,0x265a8, 0x8070,
+ 0x2335f, 0x43d5, 0x80b2, 0x8103, 0x440b, 0x813e, 0x5ab5,0x267a7,
+ 0x267b5,0x23393,0x2339c, 0x8201, 0x8204, 0x8f9e, 0x446b, 0x8291,
+ 0x828b, 0x829d, 0x52b3, 0x82b1, 0x82b3, 0x82bd, 0x82e6,0x26b3c,
+ 0x82e5, 0x831d, 0x8363, 0x83ad, 0x8323, 0x83bd, 0x83e7, 0x8457,
+ 0x8353, 0x83ca, 0x83cc, 0x83dc,0x26c36,0x26d6b,0x26cd5, 0x452b,
+ 0x84f1, 0x84f3, 0x8516,0x273ca, 0x8564,0x26f2c, 0x455d, 0x4561,
+ 0x26fb1,0x270d2, 0x456b, 0x8650, 0x865c, 0x8667, 0x8669, 0x86a9,
+ 0x8688, 0x870e, 0x86e2, 0x8779, 0x8728, 0x876b, 0x8786, 0x45d7,
+ 0x87e1, 0x8801, 0x45f9, 0x8860, 0x8863,0x27667, 0x88d7, 0x88de,
+ 0x4635, 0x88fa, 0x34bb,0x278ae,0x27966, 0x46be, 0x46c7, 0x8aa0,
+ 0x8aed, 0x8b8a, 0x8c55,0x27ca8, 0x8cab, 0x8cc1, 0x8d1b, 0x8d77,
+ 0x27f2f,0x20804, 0x8dcb, 0x8dbc, 0x8df0,0x208de, 0x8ed4, 0x8f38,
+ 0x285d2,0x285ed, 0x9094, 0x90f1, 0x9111,0x2872e, 0x911b, 0x9238,
+ 0x92d7, 0x92d8, 0x927c, 0x93f9, 0x9415,0x28bfa, 0x958b, 0x4995,
+ 0x95b7,0x28d77, 0x49e6, 0x96c3, 0x5db2, 0x9723,0x29145,0x2921a,
+ 0x4a6e, 0x4a76, 0x97e0,0x2940a, 0x4ab2,0x29496, 0x980b, 0x980b,
+ 0x9829,0x295b6, 0x98e2, 0x4b33, 0x9929, 0x99a7, 0x99c2, 0x99fe,
+ 0x4bce,0x29b30, 0x9b12, 0x9c40, 0x9cfd, 0x4cce, 0x4ced, 0x9d67,
+ 0x2a0ce, 0x4cf8,0x2a105,0x2a20e,0x2a291, 0x9ebb, 0x4d56, 0x9ef9,
+ 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b,0x2a600
+};
diff --git a/imap/src/charset/gb_12345.c b/imap/src/charset/gb_12345.c
new file mode 100644
index 00000000..88eaccb3
--- /dev/null
+++ b/imap/src/charset/gb_12345.c
@@ -0,0 +1,1114 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GB 12345 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 May 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* GB 12345 is the national standard of the People's Republic of China
+ * (mainland China) for traditional Chinese characters.
+ */
+
+#define BASE_GB12345_KU 0xa1
+#define BASE_GB12345_TEN 0xa1
+#define MAX_GB12345_KU 89
+#define MAX_GB12345_TEN 94
+
+
+#define GB12345TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_GB12345_KU) < MAX_GB12345_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_GB12345_TEN) < MAX_GB12345_TEN)) ? \
+ gb12345tab[ku][ten] : UBOGON)
+
+
+static const unsigned short gb12345tab[MAX_GB12345_KU][MAX_GB12345_TEN] = {
+ { /* ku 01 */
+ 0x3000,0x3001,0x3002,0x30fb,0x02c9,0x02c7,0x00a8,0x3003,0x3005,0x2015,
+ 0xff5e,0x2225,0x2026,0x2018,0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,
+ 0x3009,0x300a,0x300b,0x300c,0x300d,0x300e,0x300f,0x3016,0x3017,0x3010,
+ 0x3011,0x00b1,0x00d7,0x00f7,0x2236,0x2227,0x2228,0x2211,0x220f,0x222a,
+ 0x2229,0x2208,0x2237,0x221a,0x22a5,0x2225,0x2220,0x2312,0x2299,0x222b,
+ 0x222e,0x2261,0x224c,0x2248,0x223d,0x221d,0x2260,0x226e,0x226f,0x2264,
+ 0x2265,0x221e,0x2235,0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,
+ 0xff04,0x00a4,0xffe0,0xffe1,0x2030,0x00a7,0x2116,0x2606,0x2605,0x25cb,
+ 0x25cf,0x25ce,0x25c7,0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x203b,0x2192,
+ 0x2190,0x2191,0x2193,0x3013
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2488,0x2489,0x248a,0x248b,
+ 0x248c,0x248d,0x248e,0x248f,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,
+ 0x2496,0x2497,0x2498,0x2499,0x249a,0x249b,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,
+ 0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,0x2460,0x2461,0x2462,0x2463,
+ 0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,UBOGON,UBOGON,0x3220,0x3221,
+ 0x3222,0x3223,0x3224,0x3225,0x3226,0x3227,0x3228,0x3229,UBOGON,UBOGON,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ 0x216a,0x216b,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ 0xff01,0xff02,0xff03,0xffe5,0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,
+ 0xff0b,0xff0c,0xff0d,0xff0e,0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,
+ 0xff15,0xff16,0xff17,0xff18,0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,
+ 0xff1f,0xff20,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,0xff3b,0xff3c,
+ 0xff3d,0xff3e,0xff3f,0xff40,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,
+ 0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,
+ 0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,
+ 0xff5b,0xff5c,0xff5d,0xffe3
+ },
+ { /* ku 04 */
+ 0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,
+ 0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,
+ 0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,
+ 0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,
+ 0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,
+ 0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,
+ 0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,
+ 0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,
+ 0x3091,0x3092,0x3093,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ 0x30a1,0x30a2,0x30a3,0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,
+ 0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,
+ 0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,
+ 0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,
+ 0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,
+ 0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,
+ 0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,
+ 0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,
+ 0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,
+ 0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,
+ 0x042d,0x042e,0x042f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,
+ 0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,
+ 0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,
+ 0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,
+ 0x044f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x0101,0x00e1,0x01ce,0x00e0,0x0113,0x00e9,0x011b,0x00e8,0x012b,0x00ed,
+ 0x01d0,0x00ec,0x014d,0x00f3,0x01d2,0x00f2,0x016b,0x00fa,0x01d4,0x00f9,
+ 0x01d6,0x01d8,0x01da,0x01dc,0x00fc,0x00ea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3105,0x3106,0x3107,0x3108,
+ 0x3109,0x310a,0x310b,0x310c,0x310d,0x310e,0x310f,0x3110,0x3111,0x3112,
+ 0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,0x311a,0x311b,0x311c,
+ 0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,
+ 0x3127,0x3128,0x3129,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,
+ 0x2507,0x2508,0x2509,0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,0x2510,
+ 0x2511,0x2512,0x2513,0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251a,
+ 0x251b,0x251c,0x251d,0x251e,0x251f,0x2520,0x2521,0x2522,0x2523,0x2524,
+ 0x2525,0x2526,0x2527,0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,
+ 0x252f,0x2530,0x2531,0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,0x2538,
+ 0x2539,0x253a,0x253b,0x253c,0x253d,0x253e,0x253f,0x2540,0x2541,0x2542,
+ 0x2543,0x2544,0x2545,0x2546,0x2547,0x2548,0x2549,0x254a,0x254b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x554a,0x963f,0x57c3,0x6328,0x54ce,0x5509,0x54c0,0x769a,0x764c,0x85f9,
+ 0x77ee,0x827e,0x7919,0x611b,0x9698,0x978d,0x6c28,0x5b89,0x4ffa,0x6309,
+ 0x6697,0x5cb8,0x80fa,0x6848,0x9aaf,0x6602,0x76ce,0x51f9,0x6556,0x71ac,
+ 0x7ff1,0x8956,0x50b2,0x5965,0x61ca,0x6fb3,0x82ad,0x634c,0x6252,0x53ed,
+ 0x5427,0x7b06,0x516b,0x75a4,0x5df4,0x62d4,0x8dcb,0x9776,0x628a,0x8019,
+ 0x58e9,0x9738,0x7f77,0x7238,0x767d,0x67cf,0x767e,0x64fa,0x4f70,0x6557,
+ 0x62dc,0x7a17,0x6591,0x73ed,0x642c,0x6273,0x822c,0x9812,0x677f,0x7248,
+ 0x626e,0x62cc,0x4f34,0x74e3,0x534a,0x8fa6,0x7d46,0x90a6,0x5e6b,0x6886,
+ 0x699c,0x8180,0x7d81,0x68d2,0x78c5,0x868c,0x938a,0x508d,0x8b17,0x82de,
+ 0x80de,0x5305,0x8912,0x5265
+ },
+ { /* ku 11 */
+ 0x8584,0x96f9,0x4fdd,0x5821,0x98fd,0x5bf6,0x62b1,0x5831,0x66b4,0x8c79,
+ 0x9b91,0x7206,0x676f,0x7891,0x60b2,0x5351,0x5317,0x8f29,0x80cc,0x8c9d,
+ 0x92c7,0x500d,0x72fd,0x5099,0x618a,0x7119,0x88ab,0x5954,0x82ef,0x672c,
+ 0x7b28,0x5d29,0x7db3,0x752d,0x6cf5,0x8e66,0x8ff8,0x903c,0x9f3b,0x6bd4,
+ 0x9119,0x7b46,0x5f7c,0x78a7,0x84d6,0x853d,0x7562,0x6583,0x6bd6,0x5e63,
+ 0x5e87,0x75f9,0x9589,0x655d,0x5f0a,0x5fc5,0x8f9f,0x58c1,0x81c2,0x907f,
+ 0x965b,0x97ad,0x908a,0x7de8,0x8cb6,0x6241,0x4fbf,0x8b8a,0x535e,0x8fa8,
+ 0x8faf,0x8fae,0x904d,0x6a19,0x5f6a,0x8198,0x8868,0x9c49,0x618b,0x522b,
+ 0x765f,0x5f6c,0x658c,0x7015,0x6ff1,0x8cd3,0x64ef,0x5175,0x51b0,0x67c4,
+ 0x4e19,0x79c9,0x9905,0x70b3
+ },
+ { /* ku 12 */
+ 0x75c5,0x5e76,0x73bb,0x83e0,0x64ad,0x64a5,0x9262,0x6ce2,0x535a,0x52c3,
+ 0x640f,0x9251,0x7b94,0x4f2f,0x5e1b,0x8236,0x8116,0x818a,0x6e24,0x6cca,
+ 0x99c1,0x6355,0x535c,0x54fa,0x88dc,0x57e0,0x4e0d,0x5e03,0x6b65,0x7c3f,
+ 0x90e8,0x6016,0x64e6,0x731c,0x88c1,0x6750,0x624d,0x8ca1,0x776c,0x8e29,
+ 0x91c7,0x5f69,0x83dc,0x8521,0x9910,0x53c3,0x8836,0x6b98,0x615a,0x6158,
+ 0x71e6,0x84bc,0x8259,0x5009,0x6ec4,0x85cf,0x64cd,0x7cd9,0x69fd,0x66f9,
+ 0x8349,0x53a0,0x7b56,0x5074,0x518c,0x6e2c,0x5c64,0x8e6d,0x63d2,0x53c9,
+ 0x832c,0x8336,0x67e5,0x78b4,0x643d,0x5bdf,0x5c94,0x5dee,0x8a6b,0x62c6,
+ 0x67f4,0x8c7a,0x6519,0x647b,0x87ec,0x995e,0x8b92,0x7e8f,0x93df,0x7523,
+ 0x95e1,0x986b,0x660c,0x7316
+ },
+ { /* ku 13 */
+ 0x5834,0x5617,0x5e38,0x9577,0x511f,0x8178,0x5ee0,0x655e,0x66a2,0x5531,
+ 0x5021,0x8d85,0x6284,0x9214,0x671d,0x5632,0x6f6e,0x5de2,0x5435,0x7092,
+ 0x8eca,0x626f,0x64a4,0x63a3,0x5fb9,0x6f88,0x90f4,0x81e3,0x8fb0,0x5875,
+ 0x6668,0x5ff1,0x6c89,0x9673,0x8d81,0x896f,0x6491,0x7a31,0x57ce,0x6a59,
+ 0x6210,0x5448,0x4e58,0x7a0b,0x61f2,0x6f84,0x8aa0,0x627f,0x901e,0x9a01,
+ 0x79e4,0x5403,0x75f4,0x6301,0x5319,0x6c60,0x9072,0x5f1b,0x99b3,0x803b,
+ 0x9f52,0x4f88,0x5c3a,0x8d64,0x7fc5,0x65a5,0x71be,0x5145,0x885d,0x87f2,
+ 0x5d07,0x5bf5,0x62bd,0x916c,0x7587,0x8e8a,0x7a20,0x6101,0x7c4c,0x4ec7,
+ 0x7da2,0x7785,0x919c,0x81ed,0x521d,0x51fa,0x6a71,0x53a8,0x8e87,0x92e4,
+ 0x96db,0x6ec1,0x9664,0x695a
+ },
+ { /* ku 14 */
+ 0x790e,0x5132,0x77d7,0x6410,0x89f8,0x8655,0x63e3,0x5ddd,0x7a7f,0x693d,
+ 0x50b3,0x8239,0x5598,0x4e32,0x7621,0x7a97,0x5e62,0x5e8a,0x95d6,0x5275,
+ 0x5439,0x708a,0x6376,0x9318,0x5782,0x6625,0x693f,0x9187,0x5507,0x6df3,
+ 0x7d14,0x8822,0x6233,0x7dbd,0x75b5,0x8328,0x78c1,0x96cc,0x8fad,0x6148,
+ 0x74f7,0x8a5e,0x6b64,0x523a,0x8cdc,0x6b21,0x8070,0x8471,0x56f1,0x5306,
+ 0x5f9e,0x53e2,0x51d1,0x7c97,0x918b,0x7c07,0x4fc3,0x8ea5,0x7be1,0x7ac4,
+ 0x6467,0x5d14,0x50ac,0x8106,0x7601,0x7cb9,0x6dec,0x7fe0,0x6751,0x5b58,
+ 0x5bf8,0x78cb,0x64ae,0x6413,0x63aa,0x632b,0x932f,0x642d,0x9054,0x7b54,
+ 0x7629,0x6253,0x5927,0x5446,0x6b79,0x50a3,0x6234,0x5e36,0x6b86,0x4ee3,
+ 0x8cb8,0x888b,0x5f85,0x902e
+ },
+ { /* ku 15 */
+ 0x6020,0x803d,0x64d4,0x4e39,0x55ae,0x9132,0x64a3,0x81bd,0x65e6,0x6c2e,
+ 0x4f46,0x619a,0x6de1,0x8a95,0x5f48,0x86cb,0x7576,0x64cb,0x9ee8,0x8569,
+ 0x6a94,0x5200,0x6417,0x8e48,0x5012,0x5cf6,0x79b1,0x5c0e,0x5230,0x7a3b,
+ 0x60bc,0x9053,0x76d7,0x5fb7,0x5f97,0x7684,0x8e6c,0x71c8,0x767b,0x7b49,
+ 0x77aa,0x51f3,0x9127,0x5824,0x4f4e,0x6ef4,0x8fea,0x6575,0x7b1b,0x72c4,
+ 0x6ecc,0x7fdf,0x5ae1,0x62b5,0x5e95,0x5730,0x8482,0x7b2c,0x5e1d,0x5f1f,
+ 0x905e,0x7de0,0x985b,0x6382,0x6ec7,0x7898,0x9ede,0x5178,0x975b,0x588a,
+ 0x96fb,0x4f43,0x7538,0x5e97,0x60e6,0x5960,0x6fb1,0x6bbf,0x7889,0x53fc,
+ 0x96d5,0x51cb,0x5201,0x6389,0x540a,0x91e3,0x8abf,0x8dcc,0x7239,0x789f,
+ 0x8776,0x8fed,0x8adc,0x758a
+ },
+ { /* ku 16 */
+ 0x4e01,0x76ef,0x53ee,0x91d8,0x9802,0x9f0e,0x9320,0x5b9a,0x8a02,0x4e22,
+ 0x6771,0x51ac,0x8463,0x61c2,0x52d5,0x68df,0x4f97,0x606b,0x51cd,0x6d1e,
+ 0x515c,0x6296,0x9b25,0x9661,0x8c46,0x9017,0x75d8,0x90fd,0x7763,0x6bd2,
+ 0x72a2,0x7368,0x8b80,0x5835,0x7779,0x8ced,0x675c,0x934d,0x809a,0x5ea6,
+ 0x6e21,0x5992,0x7aef,0x77ed,0x935b,0x6bb5,0x65b7,0x7dde,0x5806,0x5151,
+ 0x968a,0x5c0d,0x58a9,0x5678,0x8e72,0x6566,0x9813,0x56e4,0x920d,0x76fe,
+ 0x9041,0x6387,0x54c6,0x591a,0x596a,0x579b,0x8eb2,0x6735,0x8dfa,0x8235,
+ 0x5241,0x60f0,0x58ae,0x86fe,0x5ce8,0x9d5d,0x4fc4,0x984d,0x8a1b,0x5a25,
+ 0x60e1,0x5384,0x627c,0x904f,0x9102,0x9913,0x6069,0x800c,0x5152,0x8033,
+ 0x723e,0x990c,0x6d31,0x4e8c
+ },
+ { /* ku 17 */
+ 0x8cb3,0x767c,0x7f70,0x7b4f,0x4f10,0x4e4f,0x95a5,0x6cd5,0x73d0,0x85e9,
+ 0x5e06,0x756a,0x7ffb,0x6a0a,0x792c,0x91e9,0x7e41,0x51e1,0x7169,0x53cd,
+ 0x8fd4,0x7bc4,0x8ca9,0x72af,0x98ef,0x6cdb,0x574a,0x82b3,0x65b9,0x80aa,
+ 0x623f,0x9632,0x59a8,0x4eff,0x8a2a,0x7d21,0x653e,0x83f2,0x975e,0x5561,
+ 0x98db,0x80a5,0x532a,0x8ab9,0x5420,0x80ba,0x5ee2,0x6cb8,0x8cbb,0x82ac,
+ 0x915a,0x5429,0x6c1b,0x5206,0x7d1b,0x58b3,0x711a,0x6c7e,0x7c89,0x596e,
+ 0x4efd,0x5fff,0x61a4,0x7cde,0x8c50,0x5c01,0x6953,0x8702,0x5cf0,0x92d2,
+ 0x98a8,0x760b,0x70fd,0x9022,0x99ae,0x7e2b,0x8af7,0x5949,0x9cf3,0x4f5b,
+ 0x5426,0x592b,0x6577,0x819a,0x5b75,0x6276,0x62c2,0x8f3b,0x5e45,0x6c1f,
+ 0x7b26,0x4f0f,0x4fd8,0x670d
+ },
+ { /* ku 18 */
+ 0x6d6e,0x6daa,0x798f,0x88b1,0x5f17,0x752b,0x64ab,0x8f14,0x4fef,0x91dc,
+ 0x65a7,0x812f,0x8151,0x5e9c,0x8150,0x8d74,0x526f,0x8986,0x8ce6,0x5fa9,
+ 0x5085,0x4ed8,0x961c,0x7236,0x8179,0x8ca0,0x5bcc,0x8a03,0x9644,0x5a66,
+ 0x7e1b,0x5490,0x5676,0x560e,0x8a72,0x6539,0x6982,0x9223,0x84cb,0x6e89,
+ 0x5e79,0x7518,0x6746,0x67d1,0x7aff,0x809d,0x8d95,0x611f,0x79c6,0x6562,
+ 0x8d1b,0x5ca1,0x525b,0x92fc,0x7f38,0x809b,0x7db1,0x5d17,0x6e2f,0x6760,
+ 0x7bd9,0x768b,0x9ad8,0x818f,0x7f94,0x7cd5,0x641e,0x93ac,0x7a3f,0x544a,
+ 0x54e5,0x6b4c,0x64f1,0x6208,0x9d3f,0x80f3,0x7599,0x5272,0x9769,0x845b,
+ 0x683c,0x86e4,0x95a3,0x9694,0x927b,0x500b,0x5404,0x7d66,0x6839,0x8ddf,
+ 0x8015,0x66f4,0x5e9a,0x7fb9
+ },
+ { /* ku 19 */
+ 0x57c2,0x803f,0x6897,0x5de5,0x653b,0x529f,0x606d,0x9f94,0x4f9b,0x8eac,
+ 0x516c,0x5bab,0x5f13,0x978f,0x6c5e,0x62f1,0x8ca2,0x5171,0x920e,0x52fe,
+ 0x6e9d,0x82df,0x72d7,0x57a2,0x69cb,0x8cfc,0x591f,0x8f9c,0x83c7,0x5495,
+ 0x7b8d,0x4f30,0x6cbd,0x5b64,0x59d1,0x9f13,0x53e4,0x8831,0x9aa8,0x8c37,
+ 0x80a1,0x6545,0x9867,0x56fa,0x96c7,0x522e,0x74dc,0x526e,0x5be1,0x6302,
+ 0x8902,0x4e56,0x62d0,0x602a,0x68fa,0x95dc,0x5b98,0x51a0,0x89c0,0x7ba1,
+ 0x9928,0x7f50,0x6163,0x704c,0x8cab,0x5149,0x5ee3,0x901b,0x7470,0x898f,
+ 0x572d,0x7845,0x6b78,0x9f9c,0x95a8,0x8ecc,0x9b3c,0x8a6d,0x7678,0x6842,
+ 0x6ac3,0x8dea,0x8cb4,0x528a,0x8f25,0x6eda,0x68cd,0x934b,0x90ed,0x570b,
+ 0x679c,0x88f9,0x904e,0x54c8
+ },
+ { /* ku 1a */
+ 0x9ab8,0x5b69,0x6d77,0x6c26,0x4ea5,0x5bb3,0x99ed,0x9163,0x61a8,0x90af,
+ 0x97d3,0x542b,0x6db5,0x5bd2,0x51fd,0x558a,0x7f55,0x7ff0,0x64bc,0x634d,
+ 0x65f1,0x61be,0x608d,0x710a,0x6c57,0x6f22,0x592f,0x676d,0x822a,0x58d5,
+ 0x568e,0x8c6a,0x6beb,0x90dd,0x597d,0x8017,0x865f,0x6d69,0x5475,0x559d,
+ 0x8377,0x83cf,0x6838,0x79be,0x548c,0x4f55,0x5408,0x76d2,0x8c89,0x95a1,
+ 0x6cb3,0x6db8,0x8d6b,0x8910,0x9db4,0x8cc0,0x563f,0x9ed1,0x75d5,0x5f88,
+ 0x72e0,0x6068,0x54fc,0x4ea8,0x6a2a,0x8861,0x6052,0x8f5f,0x54c4,0x70d8,
+ 0x8679,0x9d3b,0x6d2a,0x5b8f,0x5f18,0x7d05,0x5589,0x4faf,0x7334,0x543c,
+ 0x539a,0x5019,0x5f8c,0x547c,0x4e4e,0x5ffd,0x745a,0x58fa,0x846b,0x80e1,
+ 0x8774,0x72d0,0x7cca,0x6e56
+ },
+ { /* ku 1b */
+ 0x5f27,0x864e,0x552c,0x8b77,0x4e92,0x6eec,0x6237,0x82b1,0x5629,0x83ef,
+ 0x733e,0x6ed1,0x756b,0x5283,0x5316,0x8a71,0x69d0,0x5f8a,0x61f7,0x6dee,
+ 0x58de,0x6b61,0x74b0,0x6853,0x9084,0x7de9,0x63db,0x60a3,0x559a,0x7613,
+ 0x8c62,0x7165,0x6e19,0x5ba6,0x5e7b,0x8352,0x614c,0x9ec4,0x78fa,0x8757,
+ 0x7c27,0x7687,0x51f0,0x60f6,0x714c,0x6643,0x5e4c,0x604d,0x8b0a,0x7070,
+ 0x63ee,0x8f1d,0x5fbd,0x6062,0x86d4,0x56de,0x6bc1,0x6094,0x6167,0x5349,
+ 0x60e0,0x6666,0x8cc4,0x7a62,0x6703,0x71f4,0x532f,0x8af1,0x8aa8,0x7e6a,
+ 0x8477,0x660f,0x5a5a,0x9b42,0x6e3e,0x6df7,0x8c41,0x6d3b,0x4f19,0x706b,
+ 0x7372,0x6216,0x60d1,0x970d,0x8ca8,0x798d,0x64ca,0x573e,0x57fa,0x6a5f,
+ 0x7578,0x7a3d,0x7a4d,0x7b95
+ },
+ { /* ku 1c */
+ 0x808c,0x9951,0x8ff9,0x6fc0,0x8b4f,0x9dc4,0x59ec,0x7e3e,0x7ddd,0x5409,
+ 0x6975,0x68d8,0x8f2f,0x7c4d,0x96c6,0x53ca,0x6025,0x75be,0x6c72,0x5373,
+ 0x5ac9,0x7d1a,0x64e0,0x5e7e,0x810a,0x5df1,0x858a,0x6280,0x5180,0x5b63,
+ 0x4f0e,0x796d,0x5291,0x60b8,0x6fdf,0x5bc4,0x5bc2,0x8a08,0x8a18,0x65e2,
+ 0x5fcc,0x969b,0x5993,0x7e7c,0x7d00,0x5609,0x67b7,0x593e,0x4f73,0x5bb6,
+ 0x52a0,0x83a2,0x9830,0x8cc8,0x7532,0x9240,0x5047,0x7a3c,0x50f9,0x67b6,
+ 0x99d5,0x5ac1,0x6bb2,0x76e3,0x5805,0x5c16,0x7b8b,0x9593,0x714e,0x517c,
+ 0x80a9,0x8271,0x5978,0x7dd8,0x7e6d,0x6aa2,0x67ec,0x78b1,0x9e7c,0x63c0,
+ 0x64bf,0x7c21,0x5109,0x526a,0x51cf,0x85a6,0x6abb,0x9452,0x8e10,0x8ce4,
+ 0x898b,0x9375,0x7bad,0x4ef6
+ },
+ { /* ku 1d */
+ 0x5065,0x8266,0x528d,0x991e,0x6f38,0x6ffa,0x6f97,0x5efa,0x50f5,0x59dc,
+ 0x5c07,0x6f3f,0x6c5f,0x7586,0x8523,0x69f3,0x596c,0x8b1b,0x5320,0x91ac,
+ 0x964d,0x8549,0x6912,0x7901,0x7126,0x81a0,0x4ea4,0x90ca,0x6f86,0x9a55,
+ 0x5b0c,0x56bc,0x652a,0x9278,0x77ef,0x50e5,0x811a,0x72e1,0x89d2,0x9903,
+ 0x7e73,0x7d5e,0x527f,0x6559,0x9175,0x8f4e,0x8f03,0x53eb,0x7a96,0x63ed,
+ 0x63a5,0x7686,0x79f8,0x8857,0x968e,0x622a,0x52ab,0x7bc0,0x6854,0x6770,
+ 0x6377,0x776b,0x7aed,0x6f54,0x7d50,0x89e3,0x59d0,0x6212,0x85c9,0x82a5,
+ 0x754c,0x501f,0x4ecb,0x75a5,0x8aa1,0x5c4a,0x5dfe,0x7b4b,0x65a4,0x91d1,
+ 0x4eca,0x6d25,0x895f,0x7dca,0x9326,0x50c5,0x8b39,0x9032,0x9773,0x6649,
+ 0x7981,0x8fd1,0x71fc,0x6d78
+ },
+ { /* ku 1e */
+ 0x76e1,0x52c1,0x8346,0x5162,0x8396,0x775b,0x6676,0x9be8,0x4eac,0x9a5a,
+ 0x7cbe,0x7cb3,0x7d93,0x4e95,0x8b66,0x666f,0x9838,0x975c,0x5883,0x656c,
+ 0x93e1,0x5f91,0x75d9,0x9756,0x7adf,0x7af6,0x51c8,0x70af,0x7a98,0x63ea,
+ 0x7a76,0x7cfe,0x7396,0x97ed,0x4e45,0x7078,0x4e5d,0x9152,0x53a9,0x6551,
+ 0x820a,0x81fc,0x8205,0x548e,0x5c31,0x759a,0x97a0,0x62d8,0x72d9,0x75bd,
+ 0x5c45,0x99d2,0x83ca,0x5c40,0x5480,0x77e9,0x8209,0x6cae,0x805a,0x62d2,
+ 0x64da,0x5de8,0x5177,0x8ddd,0x8e1e,0x92f8,0x4ff1,0x53e5,0x61fc,0x70ac,
+ 0x5287,0x6350,0x9d51,0x5a1f,0x5026,0x7737,0x5377,0x7d79,0x6485,0x652b,
+ 0x6289,0x6398,0x5014,0x7235,0x89ba,0x51b3,0x8a23,0x7d76,0x5747,0x83cc,
+ 0x921e,0x8ecd,0x541b,0x5cfb
+ },
+ { /* ku 1f */
+ 0x4fca,0x7ae3,0x6d5a,0x90e1,0x99ff,0x5580,0x5496,0x5361,0x54af,0x958b,
+ 0x63e9,0x6977,0x51f1,0x6168,0x520a,0x582a,0x52d8,0x574e,0x780d,0x770b,
+ 0x5eb7,0x6177,0x7ce0,0x625b,0x6297,0x4ea2,0x7095,0x8003,0x62f7,0x70e4,
+ 0x9760,0x5777,0x82db,0x67ef,0x68f5,0x78d5,0x9846,0x79d1,0x6bbb,0x54b3,
+ 0x53ef,0x6e34,0x514b,0x523b,0x5ba2,0x8ab2,0x80af,0x5543,0x58be,0x61c7,
+ 0x5751,0x542d,0x7a7a,0x6050,0x5b54,0x63a7,0x6473,0x53e3,0x6263,0x5bc7,
+ 0x67af,0x54ed,0x7a9f,0x82e6,0x9177,0x5eab,0x8932,0x8a87,0x57ae,0x630e,
+ 0x8de8,0x80ef,0x584a,0x7b77,0x5108,0x5feb,0x5bec,0x6b3e,0x5321,0x7b50,
+ 0x72c2,0x6846,0x7926,0x7736,0x66e0,0x51b5,0x8667,0x76d4,0x5dcb,0x7aba,
+ 0x8475,0x594e,0x9b41,0x5080
+ },
+ { /* ku 20 */
+ 0x994b,0x6127,0x6f70,0x5764,0x6606,0x6346,0x56f0,0x62ec,0x64f4,0x5ed3,
+ 0x95ca,0x5783,0x62c9,0x5587,0x881f,0x81d8,0x8fa3,0x5566,0x840a,0x4f86,
+ 0x8cf4,0x85cd,0x5a6a,0x6b04,0x6514,0x7c43,0x95cc,0x862d,0x703e,0x8b95,
+ 0x652c,0x89bd,0x61f6,0x7e9c,0x721b,0x6feb,0x7405,0x6994,0x72fc,0x5eca,
+ 0x90ce,0x6717,0x6d6a,0x6488,0x52de,0x7262,0x8001,0x4f6c,0x59e5,0x916a,
+ 0x70d9,0x6f87,0x52d2,0x6a02,0x96f7,0x9433,0x857e,0x78ca,0x7d2f,0x5121,
+ 0x58d8,0x64c2,0x808b,0x985e,0x6cea,0x68f1,0x695e,0x51b7,0x5398,0x68a8,
+ 0x7281,0x9ece,0x7c6c,0x72f8,0x96e2,0x7055,0x7406,0x674e,0x88cf,0x9bc9,
+ 0x79ae,0x8389,0x8354,0x540f,0x6817,0x9e97,0x53b2,0x52f5,0x792b,0x6b77,
+ 0x5229,0x5088,0x4f8b,0x4fd0
+ },
+ { /* ku 21 */
+ 0x75e2,0x7acb,0x7c92,0x701d,0x96b8,0x529b,0x7483,0x54e9,0x5006,0x806f,
+ 0x84ee,0x9023,0x942e,0x5ec9,0x6190,0x6f23,0x7c3e,0x6582,0x81c9,0x93c8,
+ 0x6200,0x7149,0x7df4,0x7ce7,0x51c9,0x6881,0x7cb1,0x826f,0x5169,0x8f1b,
+ 0x91cf,0x667e,0x4eae,0x8ad2,0x64a9,0x804a,0x50da,0x7642,0x71ce,0x5be5,
+ 0x907c,0x6f66,0x4e86,0x6482,0x9410,0x5ed6,0x6599,0x5217,0x88c2,0x70c8,
+ 0x52a3,0x7375,0x7433,0x6797,0x78f7,0x9716,0x81e8,0x9130,0x9c57,0x6dcb,
+ 0x51db,0x8cc3,0x541d,0x62ce,0x73b2,0x83f1,0x96f6,0x9f61,0x9234,0x4f36,
+ 0x7f9a,0x51cc,0x9748,0x9675,0x5dba,0x9818,0x53e6,0x4ee4,0x6e9c,0x7409,
+ 0x69b4,0x786b,0x993e,0x7559,0x5289,0x7624,0x6d41,0x67f3,0x516d,0x9f8d,
+ 0x807e,0x56a8,0x7c60,0x7abf
+ },
+ { /* ku 22 */
+ 0x9686,0x58df,0x650f,0x96b4,0x6a13,0x5a41,0x645f,0x7c0d,0x6f0f,0x964b,
+ 0x8606,0x76e7,0x9871,0x5eec,0x7210,0x64c4,0x6ef7,0x865c,0x9b6f,0x9e93,
+ 0x788c,0x9732,0x8def,0x8cc2,0x9e7f,0x6f5e,0x7984,0x9332,0x9678,0x622e,
+ 0x9a62,0x5415,0x92c1,0x4fa3,0x65c5,0x5c65,0x5c62,0x7e37,0x616e,0x6c2f,
+ 0x5f8b,0x7387,0x6ffe,0x7dd1,0x5dd2,0x6523,0x5b7f,0x7064,0x5375,0x4e82,
+ 0x63a0,0x7565,0x6384,0x8f2a,0x502b,0x4f96,0x6dea,0x7db8,0x8ad6,0x863f,
+ 0x87ba,0x7f85,0x908f,0x947c,0x7c6e,0x9a3e,0x88f8,0x843d,0x6d1b,0x99f1,
+ 0x7d61,0x5abd,0x9ebb,0x746a,0x78bc,0x879e,0x99ac,0x99e1,0x561b,0x55ce,
+ 0x57cb,0x8cb7,0x9ea5,0x8ce3,0x9081,0x8109,0x779e,0x9945,0x883b,0x6eff,
+ 0x8513,0x66fc,0x6162,0x6f2b
+ },
+ { /* ku 23 */
+ 0x8b3e,0x8292,0x832b,0x76f2,0x6c13,0x5fd9,0x83bd,0x732b,0x8305,0x9328,
+ 0x6bdb,0x77db,0x925a,0x536f,0x8302,0x5192,0x5e3d,0x8c8c,0x8cbf,0x9ebd,
+ 0x73ab,0x679a,0x6885,0x9176,0x9709,0x7164,0x6ca1,0x7709,0x5a92,0x9382,
+ 0x6bcf,0x7f8e,0x6627,0x5bd0,0x59b9,0x5a9a,0x9580,0x60b6,0x5011,0x840c,
+ 0x8499,0x6aac,0x76df,0x9333,0x731b,0x5922,0x5b5f,0x772f,0x919a,0x9761,
+ 0x7cdc,0x8ff7,0x8b0e,0x5f4c,0x7c73,0x79d8,0x8993,0x6ccc,0x871c,0x5bc6,
+ 0x5e42,0x68c9,0x7720,0x7dbf,0x5195,0x514d,0x52c9,0x5a29,0x7dec,0x9762,
+ 0x82d7,0x63cf,0x7784,0x85d0,0x79d2,0x6e3a,0x5edf,0x5999,0x8511,0x6ec5,
+ 0x6c11,0x62bf,0x76bf,0x654f,0x61ab,0x95a9,0x660e,0x879f,0x9cf4,0x9298,
+ 0x540d,0x547d,0x8b2c,0x6478
+ },
+ { /* ku 24 */
+ 0x6479,0x8611,0x6a21,0x819c,0x78e8,0x6469,0x9b54,0x62b9,0x672b,0x83ab,
+ 0x58a8,0x9ed8,0x6cab,0x6f20,0x5bde,0x964c,0x8b00,0x725f,0x67d0,0x62c7,
+ 0x7261,0x755d,0x59c6,0x6bcd,0x5893,0x66ae,0x5e55,0x52df,0x6155,0x6728,
+ 0x76ee,0x7766,0x7267,0x7a46,0x62ff,0x54ea,0x5450,0x9209,0x90a3,0x5a1c,
+ 0x7d0d,0x6c16,0x4e43,0x5976,0x8010,0x5948,0x5357,0x7537,0x96e3,0x56ca,
+ 0x6493,0x8166,0x60f1,0x9b27,0x6dd6,0x5462,0x9912,0x5185,0x5ae9,0x80fd,
+ 0x59ae,0x9713,0x502a,0x6ce5,0x5c3c,0x64ec,0x4f60,0x533f,0x81a9,0x9006,
+ 0x6eba,0x852b,0x62c8,0x5e74,0x78be,0x6506,0x637b,0x5ff5,0x5a18,0x91c0,
+ 0x9ce5,0x5c3f,0x634f,0x8076,0x5b7d,0x5699,0x9477,0x93b3,0x6d85,0x60a8,
+ 0x6ab8,0x7370,0x51dd,0x5be7
+ },
+ { /* ku 25 */
+ 0x64f0,0x6fd8,0x725b,0x626d,0x9215,0x7d10,0x81bf,0x6fc3,0x8fb2,0x5f04,
+ 0x5974,0x52aa,0x6012,0x5973,0x6696,0x8650,0x7627,0x632a,0x61e6,0x7cef,
+ 0x8afe,0x54e6,0x6b50,0x9dd7,0x6bc6,0x85d5,0x5614,0x5076,0x6f1a,0x556a,
+ 0x8db4,0x722c,0x5e15,0x6015,0x7436,0x62cd,0x6392,0x724c,0x5f98,0x6e43,
+ 0x6d3e,0x6500,0x6f58,0x76e4,0x78d0,0x76fc,0x7554,0x5224,0x53db,0x4e53,
+ 0x9f90,0x65c1,0x802a,0x80d6,0x629b,0x5486,0x5228,0x70ae,0x888d,0x8dd1,
+ 0x6ce1,0x5478,0x80da,0x57f9,0x88f4,0x8ce0,0x966a,0x914d,0x4f69,0x6c9b,
+ 0x5674,0x76c6,0x7830,0x62a8,0x70f9,0x6f8e,0x5f6d,0x84ec,0x68da,0x787c,
+ 0x7bf7,0x81a8,0x670b,0x9d6c,0x6367,0x78b0,0x576f,0x7812,0x9739,0x6279,
+ 0x62ab,0x5288,0x7435,0x6bd7
+ },
+ { /* ku 26 */
+ 0x5564,0x813e,0x75b2,0x76ae,0x5339,0x75de,0x50fb,0x5c41,0x8b6c,0x7bc7,
+ 0x504f,0x7247,0x9a19,0x98c4,0x6f02,0x74e2,0x7968,0x6487,0x77a5,0x62fc,
+ 0x983b,0x8ca7,0x54c1,0x8058,0x4e52,0x576a,0x860b,0x840d,0x5e73,0x6191,
+ 0x74f6,0x8a55,0x5c4f,0x5761,0x6f51,0x9817,0x5a46,0x7834,0x9b44,0x8feb,
+ 0x7c95,0x5256,0x64b2,0x92ea,0x50d5,0x8386,0x8461,0x83e9,0x84b2,0x57d4,
+ 0x6a38,0x5703,0x666e,0x6d66,0x8b5c,0x66dd,0x7011,0x671f,0x6b3a,0x68f2,
+ 0x621a,0x59bb,0x4e03,0x51c4,0x6f06,0x67d2,0x6c8f,0x5176,0x68cb,0x5947,
+ 0x6b67,0x7566,0x5d0e,0x81cd,0x9f4a,0x65d7,0x7948,0x7941,0x9a0e,0x8d77,
+ 0x8c48,0x4e5e,0x4f01,0x5553,0x5951,0x780c,0x5668,0x6c23,0x8fc4,0x68c4,
+ 0x6c7d,0x6ce3,0x8a16,0x6390
+ },
+ { /* ku 27 */
+ 0x6070,0x6d3d,0x727d,0x6266,0x91fa,0x925b,0x5343,0x9077,0x7c3d,0x4edf,
+ 0x8b19,0x4e7e,0x9ed4,0x9322,0x9257,0x524d,0x6f5b,0x9063,0x6dfa,0x8b74,
+ 0x5879,0x5d4c,0x6b20,0x6b49,0x69cd,0x55c6,0x8154,0x7f8c,0x58bb,0x8594,
+ 0x5f3a,0x6436,0x6a47,0x936c,0x6572,0x6084,0x6a4b,0x77a7,0x55ac,0x50d1,
+ 0x5de7,0x9798,0x64ac,0x7ff9,0x5ced,0x4fcf,0x7ac5,0x5207,0x8304,0x4e14,
+ 0x602f,0x7aca,0x6b3d,0x4fb5,0x89aa,0x79e6,0x7434,0x52e4,0x82b9,0x64d2,
+ 0x79bd,0x5be2,0x6c81,0x9752,0x8f15,0x6c2b,0x50be,0x537f,0x6e05,0x64ce,
+ 0x6674,0x6c30,0x60c5,0x9803,0x8acb,0x6176,0x74ca,0x7aae,0x79cb,0x4e18,
+ 0x90b1,0x7403,0x6c42,0x56da,0x914b,0x6cc5,0x8da8,0x5340,0x86c6,0x66f2,
+ 0x8ec0,0x5c48,0x9a45,0x6e20
+ },
+ { /* ku 28 */
+ 0x53d6,0x5a36,0x9f72,0x8da3,0x53bb,0x5708,0x9874,0x6b0a,0x919b,0x6cc9,
+ 0x5168,0x75ca,0x62f3,0x72ac,0x5238,0x52f8,0x7f3a,0x7094,0x7638,0x5374,
+ 0x9d72,0x69b7,0x78ba,0x96c0,0x88d9,0x7fa4,0x7136,0x71c3,0x5189,0x67d3,
+ 0x74e4,0x58e4,0x6518,0x56b7,0x8b93,0x9952,0x64fe,0x7e5e,0x60f9,0x71b1,
+ 0x58ec,0x4ec1,0x4eba,0x5fcd,0x97cc,0x4efb,0x8a8d,0x5203,0x598a,0x7d09,
+ 0x6254,0x4ecd,0x65e5,0x620e,0x8338,0x84c9,0x69ae,0x878d,0x7194,0x6eb6,
+ 0x5bb9,0x7d68,0x5197,0x63c9,0x67d4,0x8089,0x8339,0x8815,0x5112,0x5b7a,
+ 0x5982,0x8fb1,0x4e73,0x6c5d,0x5165,0x8925,0x8edf,0x962e,0x854a,0x745e,
+ 0x92ed,0x958f,0x6f64,0x82e5,0x5f31,0x6492,0x7051,0x85a9,0x816e,0x9c13,
+ 0x585e,0x8cfd,0x4e09,0x53c1
+ },
+ { /* ku 29 */
+ 0x5098,0x6563,0x6851,0x55d3,0x55aa,0x6414,0x9a37,0x6383,0x5ac2,0x745f,
+ 0x8272,0x6f80,0x68ee,0x50e7,0x838e,0x7802,0x6bba,0x5239,0x6c99,0x7d17,
+ 0x50bb,0x5565,0x715e,0x7be9,0x66ec,0x73ca,0x82eb,0x6749,0x5c71,0x5220,
+ 0x717d,0x886b,0x9583,0x965d,0x64c5,0x8d0d,0x81b3,0x5584,0x6c55,0x6247,
+ 0x7e55,0x5892,0x50b7,0x5546,0x8cde,0x664c,0x4e0a,0x5c1a,0x88f3,0x68a2,
+ 0x634e,0x7a0d,0x71d2,0x828d,0x52fa,0x97f6,0x5c11,0x54e8,0x90b5,0x7d39,
+ 0x5962,0x8cd2,0x86c7,0x820c,0x6368,0x8d66,0x651d,0x5c04,0x61fe,0x6d89,
+ 0x793e,0x8a2d,0x7837,0x7533,0x547b,0x4f38,0x8eab,0x6df1,0x5a20,0x7d33,
+ 0x795e,0x6c88,0x5be9,0x5b38,0x751a,0x814e,0x614e,0x6ef2,0x8072,0x751f,
+ 0x7525,0x7272,0x5347,0x7e69
+ },
+ { /* ku 2a */
+ 0x7701,0x76db,0x5269,0x52dd,0x8056,0x5e2b,0x5931,0x7345,0x65bd,0x6fd5,
+ 0x8a69,0x5c38,0x8671,0x5341,0x77f3,0x62fe,0x6642,0x4ec0,0x98df,0x8755,
+ 0x5be6,0x8b58,0x53f2,0x77e2,0x4f7f,0x5c4e,0x99db,0x59cb,0x5f0f,0x793a,
+ 0x58eb,0x4e16,0x67ff,0x4e8b,0x62ed,0x8a93,0x901d,0x52e2,0x662f,0x55dc,
+ 0x566c,0x9069,0x4ed5,0x4f8d,0x91cb,0x98fe,0x6c0f,0x5e02,0x6043,0x5ba4,
+ 0x8996,0x8a66,0x6536,0x624b,0x9996,0x5b88,0x58fd,0x6388,0x552e,0x53d7,
+ 0x7626,0x7378,0x852c,0x6a1e,0x68b3,0x6b8a,0x6292,0x8f38,0x53d4,0x8212,
+ 0x6dd1,0x758f,0x66f8,0x8d16,0x5b70,0x719f,0x85af,0x6691,0x66d9,0x7f72,
+ 0x8700,0x9ecd,0x9f20,0x5c6c,0x8853,0x8ff0,0x6a39,0x675f,0x620d,0x7aea,
+ 0x5885,0x5eb6,0x6578,0x6f31
+ },
+ { /* ku 2b */
+ 0x6055,0x5237,0x800d,0x6454,0x8870,0x7529,0x5e25,0x6813,0x62f4,0x971c,
+ 0x96d9,0x723d,0x8ab0,0x6c34,0x7761,0x7a0e,0x542e,0x77ac,0x9806,0x821c,
+ 0x8aac,0x78a9,0x6714,0x720d,0x65af,0x6495,0x5636,0x601d,0x79c1,0x53f8,
+ 0x7d72,0x6b7b,0x8086,0x5bfa,0x55e3,0x56db,0x4f3a,0x4f3c,0x98fc,0x5df3,
+ 0x9b06,0x8073,0x616b,0x980c,0x9001,0x5b8b,0x8a1f,0x8aa6,0x641c,0x8258,
+ 0x64fb,0x55fd,0x8607,0x9165,0x4fd7,0x7d20,0x901f,0x7c9f,0x50f3,0x5851,
+ 0x6eaf,0x5bbf,0x8a34,0x8085,0x9178,0x849c,0x7b97,0x96d6,0x968b,0x96a8,
+ 0x7d8f,0x9ad3,0x788e,0x6b72,0x7a57,0x9042,0x96a7,0x795f,0x5b6b,0x640d,
+ 0x7b0b,0x84d1,0x68ad,0x5506,0x7e2e,0x7463,0x7d22,0x9396,0x6240,0x584c,
+ 0x4ed6,0x5b83,0x5979,0x5854
+ },
+ { /* ku 2c */
+ 0x737a,0x64bb,0x8e4b,0x8e0f,0x80ce,0x82d4,0x62ac,0x81fa,0x6cf0,0x915e,
+ 0x592a,0x614b,0x6c70,0x574d,0x6524,0x8caa,0x7671,0x7058,0x58c7,0x6a80,
+ 0x75f0,0x6f6d,0x8b5a,0x8ac7,0x5766,0x6bef,0x8892,0x78b3,0x63a2,0x5606,
+ 0x70ad,0x6e6f,0x5858,0x642a,0x5802,0x68e0,0x819b,0x5510,0x7cd6,0x5018,
+ 0x8eba,0x6dcc,0x8d9f,0x71d9,0x638f,0x6fe4,0x6ed4,0x7e27,0x8404,0x6843,
+ 0x9003,0x6dd8,0x9676,0x8a0e,0x5957,0x7279,0x85e4,0x9a30,0x75bc,0x8b04,
+ 0x68af,0x5254,0x8e22,0x92bb,0x63d0,0x984c,0x8e44,0x557c,0x9ad4,0x66ff,
+ 0x568f,0x60d5,0x6d95,0x5243,0x5c49,0x5929,0x6dfb,0x586b,0x7530,0x751c,
+ 0x606c,0x8214,0x8146,0x6311,0x689d,0x8fe2,0x773a,0x8df3,0x8cbc,0x9435,
+ 0x5e16,0x5ef3,0x807d,0x70f4
+ },
+ { /* ku 2d */
+ 0x6c40,0x5ef7,0x505c,0x4ead,0x5ead,0x633a,0x8247,0x901a,0x6850,0x916e,
+ 0x77b3,0x540c,0x9285,0x5f64,0x7ae5,0x6876,0x6345,0x7b52,0x7d71,0x75db,
+ 0x5077,0x6295,0x982d,0x900f,0x51f8,0x79c3,0x7a81,0x5716,0x5f92,0x9014,
+ 0x5857,0x5c60,0x571f,0x5410,0x5154,0x6e4d,0x5718,0x63a8,0x983d,0x817f,
+ 0x8715,0x892a,0x9000,0x541e,0x5c6f,0x81c0,0x62d6,0x6258,0x8131,0x9d15,
+ 0x9640,0x99b1,0x99dd,0x6a62,0x59a5,0x62d3,0x553e,0x6316,0x54c7,0x86d9,
+ 0x7aaa,0x5a03,0x74e6,0x896a,0x6b6a,0x5916,0x8c4c,0x5f4e,0x7063,0x73a9,
+ 0x9811,0x4e38,0x70f7,0x5b8c,0x7897,0x633d,0x665a,0x7696,0x60cb,0x5b9b,
+ 0x5a49,0x842c,0x8155,0x6c6a,0x738b,0x4ea1,0x6789,0x7db2,0x5f80,0x65fa,
+ 0x671b,0x5fd8,0x5984,0x5a01
+ },
+ { /* ku 2e */
+ 0x5dcd,0x5fae,0x5371,0x97cb,0x9055,0x6845,0x570d,0x552f,0x60df,0x7232,
+ 0x6ff0,0x7dad,0x8466,0x840e,0x59d4,0x5049,0x50de,0x5c3e,0x7def,0x672a,
+ 0x851a,0x5473,0x754f,0x80c3,0x5582,0x9b4f,0x4f4d,0x6e2d,0x8b02,0x5c09,
+ 0x6170,0x885b,0x761f,0x6e29,0x868a,0x6587,0x805e,0x7d0b,0x543b,0x7a69,
+ 0x7d0a,0x554f,0x55e1,0x7fc1,0x74ee,0x64be,0x8778,0x6e26,0x7aa9,0x6211,
+ 0x65a1,0x5367,0x63e1,0x6c83,0x5deb,0x55da,0x93a2,0x70cf,0x6c61,0x8aa3,
+ 0x5c4b,0x7121,0x856a,0x68a7,0x543e,0x5434,0x6bcb,0x6b66,0x4e94,0x6342,
+ 0x5348,0x821e,0x4f0d,0x4fae,0x5862,0x620a,0x9727,0x6664,0x7269,0x52ff,
+ 0x52d9,0x609f,0x8aa4,0x6614,0x7199,0x6790,0x897f,0x7852,0x77fd,0x6670,
+ 0x563b,0x5438,0x932b,0x72a7
+ },
+ { /* ku 2f */
+ 0x7a00,0x606f,0x5e0c,0x6089,0x819d,0x5915,0x60dc,0x7184,0x70ef,0x6eaa,
+ 0x6c50,0x7280,0x6a84,0x8972,0x5e2d,0x7fd2,0x5ab3,0x559c,0x9291,0x6d17,
+ 0x7cfb,0x9699,0x6232,0x7d30,0x778e,0x8766,0x5323,0x971e,0x8f44,0x6687,
+ 0x5cfd,0x4fe0,0x72f9,0x4e0b,0x53a6,0x590f,0x5687,0x6380,0x9341,0x5148,
+ 0x4ed9,0x9bae,0x7e96,0x54b8,0x8ce2,0x929c,0x8237,0x9591,0x6d8e,0x5f26,
+ 0x5acc,0x986f,0x96aa,0x73fe,0x737b,0x7e23,0x817a,0x9921,0x7fa1,0x61b2,
+ 0x9677,0x9650,0x7dab,0x76f8,0x53a2,0x9472,0x9999,0x7bb1,0x8944,0x6e58,
+ 0x9109,0x7fd4,0x7965,0x8a73,0x60f3,0x97ff,0x4eab,0x9805,0x5df7,0x6a61,
+ 0x50cf,0x5411,0x8c61,0x856d,0x785d,0x9704,0x524a,0x54ee,0x56c2,0x92b7,
+ 0x6d88,0x5bb5,0x6dc6,0x66c9
+ },
+ { /* ku 30 */
+ 0x5c0f,0x5b5d,0x6821,0x8096,0x562f,0x7b11,0x6548,0x6954,0x4e9b,0x6b47,
+ 0x874e,0x978b,0x5354,0x633e,0x643a,0x90aa,0x659c,0x8105,0x8ae7,0x5beb,
+ 0x68b0,0x5378,0x87f9,0x61c8,0x6cc4,0x7009,0x8b1d,0x5c51,0x85aa,0x82af,
+ 0x92c5,0x6b23,0x8f9b,0x65b0,0x5ffb,0x5fc3,0x4fe1,0x91c1,0x661f,0x8165,
+ 0x7329,0x60fa,0x8208,0x5211,0x578b,0x5f62,0x90a2,0x884c,0x9192,0x5e78,
+ 0x674f,0x6027,0x59d3,0x5144,0x51f6,0x80f8,0x5308,0x6c79,0x96c4,0x718a,
+ 0x4f11,0x4fee,0x7f9e,0x673d,0x55c5,0x92b9,0x79c0,0x8896,0x7d89,0x589f,
+ 0x620c,0x9700,0x865a,0x5618,0x9808,0x5f90,0x8a31,0x84c4,0x9157,0x53d9,
+ 0x65ed,0x5e8f,0x755c,0x6064,0x7d6e,0x5a7f,0x7dd2,0x7e8c,0x8ed2,0x55a7,
+ 0x5ba3,0x61f8,0x65cb,0x7384
+ },
+ { /* ku 31 */
+ 0x9078,0x766c,0x7729,0x7d62,0x9774,0x859b,0x5b78,0x7a74,0x96ea,0x8840,
+ 0x52db,0x718f,0x5faa,0x65ec,0x8a62,0x5c0b,0x99b4,0x5de1,0x6b89,0x6c5b,
+ 0x8a13,0x8a0a,0x905c,0x8fc5,0x58d3,0x62bc,0x9d09,0x9d28,0x5440,0x4e2b,
+ 0x82bd,0x7259,0x869c,0x5d16,0x8859,0x6daf,0x96c5,0x555e,0x4e9e,0x8a1d,
+ 0x7109,0x54bd,0x95b9,0x70df,0x6df9,0x9e7d,0x56b4,0x7814,0x8712,0x5ca9,
+ 0x5ef6,0x8a00,0x9854,0x95bb,0x708e,0x6cbf,0x5944,0x63a9,0x773c,0x884d,
+ 0x6f14,0x8277,0x5830,0x71d5,0x53ad,0x786f,0x96c1,0x5501,0x5f66,0x7130,
+ 0x5bb4,0x8afa,0x9a57,0x6b83,0x592e,0x9d26,0x79e7,0x694a,0x63da,0x4f6f,
+ 0x760d,0x7f8a,0x6d0b,0x967d,0x6c27,0x4ef0,0x7662,0x990a,0x6a23,0x6f3e,
+ 0x9080,0x8170,0x5996,0x7476
+ },
+ { /* ku 32 */
+ 0x6447,0x582f,0x9065,0x7a91,0x8b21,0x59da,0x54ac,0x8200,0x85e5,0x8981,
+ 0x8000,0x6930,0x564e,0x8036,0x723a,0x91ce,0x51b6,0x4e5f,0x9801,0x6396,
+ 0x696d,0x8449,0x66f3,0x814b,0x591c,0x6db2,0x4e00,0x58f9,0x91ab,0x63d6,
+ 0x92a5,0x4f9d,0x4f0a,0x8863,0x9824,0x5937,0x907a,0x79fb,0x5100,0x80f0,
+ 0x7591,0x6c82,0x5b9c,0x59e8,0x5f5d,0x6905,0x87fb,0x501a,0x5df2,0x4e59,
+ 0x77e3,0x4ee5,0x85dd,0x6291,0x6613,0x9091,0x5c79,0x5104,0x5f79,0x81c6,
+ 0x9038,0x8084,0x75ab,0x4ea6,0x88d4,0x610f,0x6bc5,0x61b6,0x7fa9,0x76ca,
+ 0x6ea2,0x8a63,0x8b70,0x8abc,0x8b6f,0x5f02,0x7ffc,0x7fcc,0x7e79,0x8335,
+ 0x852d,0x56e0,0x6bb7,0x97f3,0x9670,0x59fb,0x541f,0x9280,0x6deb,0x5bc5,
+ 0x98f2,0x5c39,0x5f15,0x96b1
+ },
+ { /* ku 33 */
+ 0x5370,0x82f1,0x6afb,0x5b30,0x9df9,0x61c9,0x7e93,0x7469,0x87a2,0x71df,
+ 0x7192,0x8805,0x8fce,0x8d0f,0x76c8,0x5f71,0x7a4e,0x786c,0x6620,0x55b2,
+ 0x64c1,0x50ad,0x81c3,0x7670,0x5eb8,0x96cd,0x8e34,0x86f9,0x548f,0x6cf3,
+ 0x6d8c,0x6c38,0x607f,0x52c7,0x7528,0x5e7d,0x512a,0x60a0,0x6182,0x5c24,
+ 0x7531,0x90f5,0x923e,0x7336,0x6cb9,0x6e38,0x9149,0x6709,0x53cb,0x53f3,
+ 0x4f51,0x91c9,0x8a98,0x53c8,0x5e7c,0x8fc2,0x6de4,0x4e8e,0x76c2,0x6986,
+ 0x865e,0x611a,0x8f3f,0x9918,0x4fde,0x903e,0x9b5a,0x6109,0x6e1d,0x6f01,
+ 0x9685,0x4e88,0x5a31,0x96e8,0x8207,0x5dbc,0x79b9,0x5b87,0x8a9e,0x7fbd,
+ 0x7389,0x57df,0x828b,0x9b31,0x5401,0x9047,0x55bb,0x5cea,0x5fa1,0x6108,
+ 0x6b32,0x7344,0x80b2,0x8b7d
+ },
+ { /* ku 34 */
+ 0x6d74,0x5bd3,0x88d5,0x9810,0x8c6b,0x99ad,0x9d1b,0x6df5,0x51a4,0x5143,
+ 0x57a3,0x8881,0x539f,0x63f4,0x8f45,0x5712,0x54e1,0x5713,0x733f,0x6e90,
+ 0x7de3,0x9060,0x82d1,0x9858,0x6028,0x9662,0x66f0,0x7d04,0x8d8a,0x8e8d,
+ 0x9470,0x5cb3,0x7ca4,0x6708,0x60a6,0x95b2,0x8018,0x96f2,0x9116,0x5300,
+ 0x9695,0x5141,0x904b,0x85f4,0x9196,0x6688,0x97f5,0x5b55,0x531d,0x7838,
+ 0x96dc,0x683d,0x54c9,0x707e,0x5bb0,0x8f09,0x518d,0x5728,0x54b1,0x6522,
+ 0x66ab,0x8d0a,0x8d1c,0x81df,0x846c,0x906d,0x7cdf,0x947f,0x85fb,0x68d7,
+ 0x65e9,0x6fa1,0x86a4,0x8e81,0x566a,0x9020,0x7682,0x7ac8,0x71e5,0x8cac,
+ 0x64c7,0x5247,0x6fa4,0x8cca,0x600e,0x589e,0x618e,0x66fe,0x8d08,0x624e,
+ 0x55b3,0x6e23,0x672d,0x8ecb
+ },
+ { /* ku 35 */
+ 0x9358,0x9598,0x7728,0x6805,0x69a8,0x548b,0x4e4d,0x70b8,0x8a50,0x6458,
+ 0x9f4b,0x5b85,0x7a84,0x50b5,0x5be8,0x77bb,0x6c08,0x8a79,0x7c98,0x6cbe,
+ 0x76de,0x65ac,0x8f3e,0x5d84,0x5c55,0x8638,0x68e7,0x5360,0x6230,0x7ad9,
+ 0x6e5b,0x7dbb,0x6a1f,0x7ae0,0x5f70,0x6f33,0x5f35,0x638c,0x6f32,0x6756,
+ 0x4e08,0x5e33,0x8cec,0x4ed7,0x8139,0x7634,0x969c,0x62db,0x662d,0x627e,
+ 0x6cbc,0x8d99,0x7167,0x7f69,0x5146,0x8087,0x53ec,0x906e,0x6298,0x54f2,
+ 0x87c4,0x8f4d,0x8005,0x937a,0x8517,0x9019,0x6d59,0x73cd,0x659f,0x771f,
+ 0x7504,0x7827,0x81fb,0x8c9e,0x91dd,0x5075,0x6795,0x75b9,0x8a3a,0x9707,
+ 0x632f,0x93ae,0x9663,0x84b8,0x6399,0x775c,0x5f81,0x7319,0x722d,0x6014,
+ 0x6574,0x62ef,0x6b63,0x653f
+ },
+ { /* ku 36 */
+ 0x5e40,0x7665,0x912d,0x8b49,0x829d,0x679d,0x652f,0x5431,0x8718,0x77e5,
+ 0x80a2,0x8102,0x6c41,0x4e4b,0x7e54,0x8077,0x76f4,0x690d,0x6b96,0x57f7,
+ 0x503c,0x4f84,0x5740,0x6307,0x6b62,0x8dbe,0x8879,0x65e8,0x7d19,0x5fd7,
+ 0x646f,0x64f2,0x81f3,0x81f4,0x7f6e,0x5e5f,0x5cd9,0x5236,0x667a,0x79e9,
+ 0x7a1a,0x8cea,0x7099,0x75d4,0x6eef,0x6cbb,0x7a92,0x4e2d,0x76c5,0x5fe0,
+ 0x9418,0x8877,0x7d42,0x7a2e,0x816b,0x91cd,0x4ef2,0x8846,0x821f,0x5468,
+ 0x5dde,0x6d32,0x8b05,0x7ca5,0x8ef8,0x8098,0x5e1a,0x5492,0x76ba,0x5b99,
+ 0x665d,0x9a5f,0x73e0,0x682a,0x86db,0x6731,0x732a,0x8af8,0x8a85,0x9010,
+ 0x7af9,0x71ed,0x716e,0x62c4,0x77da,0x56d1,0x4e3b,0x8457,0x67f1,0x52a9,
+ 0x86c0,0x8caf,0x9444,0x7bc9
+ },
+ { /* ku 37 */
+ 0x4f4f,0x6ce8,0x795d,0x99d0,0x6293,0x722a,0x62fd,0x5c08,0x78da,0x8f49,
+ 0x64b0,0x8cfa,0x7bc6,0x6a01,0x838a,0x88dd,0x599d,0x649e,0x58ef,0x72c0,
+ 0x690e,0x9310,0x8ffd,0x8d05,0x589c,0x7db4,0x8ac4,0x6e96,0x6349,0x62d9,
+ 0x5353,0x684c,0x7422,0x8301,0x914c,0x5544,0x7740,0x707c,0x6fc1,0x5179,
+ 0x54a8,0x8cc7,0x59ff,0x6ecb,0x6dc4,0x5b5c,0x7d2b,0x4ed4,0x7c7d,0x6ed3,
+ 0x5b50,0x81ea,0x6f2c,0x5b57,0x9b03,0x68d5,0x8e2a,0x5b97,0x7d9c,0x7e3d,
+ 0x7e31,0x9112,0x8d70,0x594f,0x63cd,0x79df,0x8db3,0x5352,0x65cf,0x7956,
+ 0x8a5b,0x963b,0x7d44,0x947d,0x7e82,0x5634,0x9189,0x6700,0x7f6a,0x5c0a,
+ 0x9075,0x6628,0x5de6,0x4f50,0x67de,0x505a,0x4f5c,0x5750,0x5ea7,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ 0x4e8d,0x4e0c,0x5140,0x4e10,0x5eff,0x5345,0x4e15,0x4e98,0x4e1e,0x9b32,
+ 0x5b6c,0x5669,0x4e28,0x79ba,0x4e3f,0x5315,0x4e47,0x592d,0x723b,0x536e,
+ 0x6c10,0x56df,0x80e4,0x9997,0x6bd3,0x777e,0x9f17,0x4e36,0x4e9f,0x9f10,
+ 0x4e5c,0x4e69,0x4e93,0x8288,0x5b5b,0x55c7,0x560f,0x4ec4,0x5399,0x539d,
+ 0x53b4,0x53a5,0x53ae,0x9768,0x8d0b,0x531a,0x53f5,0x532d,0x5331,0x533e,
+ 0x8cfe,0x5366,0x5363,0x5202,0x5208,0x520e,0x5244,0x5233,0x528c,0x5274,
+ 0x524c,0x525e,0x5261,0x525c,0x84af,0x527d,0x5282,0x5281,0x5290,0x5293,
+ 0x5182,0x7f54,0x4ebb,0x4ec3,0x4ec9,0x4ec2,0x4ee8,0x4ee1,0x4eeb,0x4ede,
+ 0x50b4,0x4ef3,0x4f22,0x4f64,0x4ef5,0x5000,0x5096,0x4f09,0x4f47,0x4f5e,
+ 0x4f67,0x6538,0x4f5a,0x4f5d
+ },
+ { /* ku 39 */
+ 0x4f5f,0x4f57,0x4f32,0x4f3d,0x4f76,0x4f74,0x4f91,0x4f89,0x4f83,0x4f8f,
+ 0x4f7e,0x4f7b,0x5115,0x4f7c,0x5102,0x4f94,0x5114,0x513c,0x5137,0x4fc5,
+ 0x4fda,0x4fe3,0x4fdc,0x4fd1,0x4fdf,0x4ff8,0x5029,0x504c,0x4ff3,0x502c,
+ 0x500f,0x502e,0x502d,0x4ffe,0x501c,0x500c,0x5025,0x5028,0x50e8,0x5043,
+ 0x5055,0x5048,0x504e,0x506c,0x50c2,0x513b,0x5110,0x513a,0x50ba,0x50d6,
+ 0x5106,0x50ed,0x50ec,0x50e6,0x50ee,0x5107,0x510b,0x4edd,0x6c3d,0x4f58,
+ 0x50c9,0x4fce,0x9fa0,0x6c46,0x7cf4,0x516e,0x5dfd,0x9ecc,0x9998,0x56c5,
+ 0x5914,0x52f9,0x530d,0x8a07,0x5310,0x9cec,0x5919,0x5155,0x4ea0,0x5156,
+ 0x4eb3,0x886e,0x88a4,0x893b,0x81e0,0x88d2,0x7980,0x5b34,0x8803,0x7fb8,
+ 0x51ab,0x51b1,0x51bd,0x51bc
+ },
+ { /* ku 3a */
+ 0x51c7,0x5196,0x51a2,0x51a5,0x8a01,0x8a10,0x8a0c,0x8a15,0x8b33,0x8a4e,
+ 0x8a25,0x8a41,0x8a36,0x8a46,0x8a54,0x8a58,0x8a52,0x8a86,0x8a84,0x8a7f,
+ 0x8a70,0x8a7c,0x8a75,0x8a6c,0x8a6e,0x8acd,0x8ae2,0x8a61,0x8a9a,0x8aa5,
+ 0x8a91,0x8a92,0x8acf,0x8ad1,0x8ac9,0x8adb,0x8ad7,0x8ac2,0x8ab6,0x8af6,
+ 0x8aeb,0x8b14,0x8b01,0x8ae4,0x8aed,0x8afc,0x8af3,0x8ae6,0x8aee,0x8ade,
+ 0x8b28,0x8b9c,0x8b16,0x8b1a,0x8b10,0x8b2b,0x8b2d,0x8b56,0x8b59,0x8b4e,
+ 0x8b9e,0x8b6b,0x8b96,0x5369,0x537a,0x961d,0x9622,0x9621,0x9631,0x962a,
+ 0x963d,0x963c,0x9642,0x9658,0x9654,0x965f,0x9689,0x966c,0x9672,0x9674,
+ 0x9688,0x968d,0x9697,0x96b0,0x9097,0x909b,0x913a,0x9099,0x9114,0x90a1,
+ 0x90b4,0x90b3,0x90b6,0x9134
+ },
+ { /* ku 3b */
+ 0x90b8,0x90b0,0x90df,0x90c5,0x90be,0x9136,0x90c4,0x90c7,0x9106,0x9148,
+ 0x90e2,0x90dc,0x90d7,0x90db,0x90eb,0x90ef,0x90fe,0x9104,0x9122,0x911e,
+ 0x9123,0x9131,0x912f,0x9139,0x9143,0x9146,0x82bb,0x5950,0x52f1,0x52ac,
+ 0x52ad,0x52be,0x54ff,0x52d0,0x52d6,0x52f0,0x53df,0x71ee,0x77cd,0x5ef4,
+ 0x51f5,0x51fc,0x9b2f,0x53b6,0x5f01,0x755a,0x5df0,0x574c,0x580a,0x57a1,
+ 0x587e,0x58bc,0x58c5,0x58d1,0x5729,0x572c,0x572a,0x5733,0x58d9,0x572e,
+ 0x572f,0x58e2,0x573b,0x5742,0x5769,0x58e0,0x576b,0x58da,0x577c,0x577b,
+ 0x5768,0x576d,0x5776,0x5773,0x57e1,0x57a4,0x578c,0x584f,0x57cf,0x57a7,
+ 0x5816,0x5793,0x57a0,0x57d5,0x5852,0x581d,0x5864,0x57d2,0x57b8,0x57f4,
+ 0x57ef,0x57f8,0x57e4,0x57dd
+ },
+ { /* ku 3c */
+ 0x580b,0x580d,0x57fd,0x57ed,0x5800,0x581e,0x5819,0x5844,0x5820,0x5865,
+ 0x586c,0x5881,0x5889,0x589a,0x5880,0x99a8,0x9f19,0x61ff,0x8279,0x827d,
+ 0x827f,0x828f,0x828a,0x82a8,0x8284,0x828e,0x8291,0x858c,0x8299,0x82ab,
+ 0x8553,0x82be,0x82b0,0x85f6,0x82ca,0x82e3,0x8298,0x82b7,0x82ae,0x83a7,
+ 0x8407,0x84ef,0x82a9,0x82b4,0x82a1,0x82aa,0x829f,0x82c4,0x82e7,0x82a4,
+ 0x82e1,0x8309,0x82f7,0x82e4,0x8622,0x8307,0x82dc,0x82f4,0x82d2,0x82d8,
+ 0x830c,0x82fb,0x82d3,0x8526,0x831a,0x8306,0x584b,0x7162,0x82e0,0x82d5,
+ 0x831c,0x8351,0x8558,0x84fd,0x8308,0x8392,0x833c,0x8334,0x8331,0x839b,
+ 0x854e,0x832f,0x834f,0x8347,0x8343,0x8588,0x8340,0x8317,0x85ba,0x832d,
+ 0x833a,0x8333,0x7296,0x6ece
+ },
+ { /* ku 3d */
+ 0x8541,0x831b,0x85ce,0x8552,0x84c0,0x8452,0x8464,0x83b0,0x8378,0x8494,
+ 0x8435,0x83a0,0x83aa,0x8393,0x839c,0x8385,0x837c,0x859f,0x83a9,0x837d,
+ 0x8555,0x837b,0x8398,0x839e,0x83a8,0x9daf,0x8493,0x83c1,0x8401,0x83e5,
+ 0x83d8,0x5807,0x8418,0x840b,0x83dd,0x83fd,0x83d6,0x841c,0x8438,0x8411,
+ 0x8406,0x83d4,0x83df,0x840f,0x8403,0x83f8,0x83f9,0x83ea,0x83c5,0x83c0,
+ 0x7e08,0x83f0,0x83e1,0x845c,0x8451,0x845a,0x8459,0x8473,0x8546,0x8488,
+ 0x847a,0x8562,0x8478,0x843c,0x8446,0x8469,0x8476,0x851e,0x848e,0x8431,
+ 0x846d,0x84c1,0x84cd,0x84d0,0x9a40,0x84bd,0x84d3,0x84ca,0x84bf,0x84ba,
+ 0x863a,0x84a1,0x84b9,0x84b4,0x8497,0x93a3,0x8577,0x850c,0x750d,0x8538,
+ 0x84f0,0x861e,0x851f,0x85fa
+ },
+ { /* ku 3e */
+ 0x8556,0x853b,0x84ff,0x84fc,0x8559,0x8548,0x8568,0x8564,0x855e,0x857a,
+ 0x77a2,0x8543,0x8604,0x857b,0x85a4,0x85a8,0x8587,0x858f,0x8579,0x85ea,
+ 0x859c,0x8585,0x85b9,0x85b7,0x85b0,0x861a,0x85c1,0x85dc,0x85ff,0x8627,
+ 0x8605,0x8629,0x8616,0x863c,0x5efe,0x5f08,0x593c,0x5969,0x8037,0x5955,
+ 0x595a,0x5958,0x530f,0x5c22,0x5c25,0x5c2c,0x5c37,0x624c,0x636b,0x6476,
+ 0x62bb,0x62ca,0x62da,0x62d7,0x62ee,0x649f,0x62f6,0x6339,0x634b,0x6343,
+ 0x63ad,0x63f6,0x6371,0x637a,0x638e,0x6451,0x636d,0x63ac,0x638a,0x6369,
+ 0x63ae,0x645c,0x63f2,0x63f8,0x63e0,0x64b3,0x63c4,0x63de,0x63ce,0x6452,
+ 0x63c6,0x63be,0x6504,0x6441,0x640b,0x641b,0x6420,0x640c,0x6426,0x6421,
+ 0x645e,0x6516,0x646d,0x6496
+ },
+ { /* ku 3f */
+ 0x647a,0x64f7,0x64fc,0x6499,0x651b,0x64c0,0x64d0,0x64d7,0x64e4,0x64e2,
+ 0x6509,0x6525,0x652e,0x5f0b,0x5fd2,0x7519,0x5f11,0x535f,0x53f1,0x5630,
+ 0x53e9,0x53e8,0x53fb,0x5412,0x5416,0x5406,0x544b,0x5638,0x56c8,0x5454,
+ 0x56a6,0x5443,0x5421,0x5504,0x54bc,0x5423,0x5432,0x5482,0x5494,0x5477,
+ 0x5471,0x5464,0x549a,0x5680,0x5484,0x5476,0x5466,0x565d,0x54d0,0x54ad,
+ 0x54c2,0x54b4,0x5660,0x54a7,0x54a6,0x5635,0x55f6,0x5472,0x54a3,0x5666,
+ 0x54bb,0x54bf,0x54cc,0x5672,0x54da,0x568c,0x54a9,0x54aa,0x54a4,0x5665,
+ 0x54cf,0x54de,0x561c,0x54e7,0x562e,0x54fd,0x5514,0x54f3,0x55e9,0x5523,
+ 0x550f,0x5511,0x5527,0x552a,0x5616,0x558f,0x55b5,0x5549,0x56c0,0x5541,
+ 0x5555,0x553f,0x5550,0x553c
+ },
+ { /* ku 40 */
+ 0x5537,0x5556,0x5575,0x5576,0x5577,0x5533,0x5530,0x555c,0x558b,0x55d2,
+ 0x5583,0x55b1,0x55b9,0x5588,0x5581,0x559f,0x557e,0x55d6,0x5591,0x557b,
+ 0x55df,0x560d,0x56b3,0x5594,0x5599,0x55ea,0x55f7,0x55c9,0x561f,0x55d1,
+ 0x56c1,0x55ec,0x55d4,0x55e6,0x55dd,0x55c4,0x55ef,0x55e5,0x55f2,0x566f,
+ 0x55cc,0x55cd,0x55e8,0x55f5,0x55e4,0x8f61,0x561e,0x5608,0x560c,0x5601,
+ 0x56b6,0x5623,0x55fe,0x5600,0x5627,0x562d,0x5658,0x5639,0x5657,0x562c,
+ 0x564d,0x5662,0x5659,0x5695,0x564c,0x5654,0x5686,0x5664,0x5671,0x566b,
+ 0x567b,0x567c,0x5685,0x5693,0x56af,0x56d4,0x56d7,0x56dd,0x56e1,0x5707,
+ 0x56eb,0x56f9,0x56ff,0x5704,0x570a,0x5709,0x571c,0x5e43,0x5e19,0x5e14,
+ 0x5e11,0x5e6c,0x5e58,0x5e57
+ },
+ { /* ku 41 */
+ 0x5e37,0x5e44,0x5e54,0x5e5b,0x5e5e,0x5e61,0x5c8c,0x5c7a,0x5c8d,0x5c90,
+ 0x5d87,0x5c88,0x5cf4,0x5c99,0x5c91,0x5d50,0x5c9c,0x5cb5,0x5ca2,0x5d2c,
+ 0x5cac,0x5cab,0x5cb1,0x5ca3,0x5cc1,0x5cb7,0x5da7,0x5cd2,0x5da0,0x5ccb,
+ 0x5d22,0x5d97,0x5d0d,0x5d27,0x5d26,0x5d2e,0x5d24,0x5d1e,0x5d06,0x5d1b,
+ 0x5db8,0x5d3e,0x5d34,0x5d3d,0x5d6c,0x5d5b,0x5d6f,0x5d81,0x5d6b,0x5d4b,
+ 0x5d4a,0x5d69,0x5d74,0x5d82,0x5d99,0x5d9d,0x8c73,0x5db7,0x5dd4,0x5f73,
+ 0x5f77,0x5f82,0x5f87,0x5f89,0x540e,0x5fa0,0x5f99,0x5f9c,0x5fa8,0x5fad,
+ 0x5fb5,0x5fbc,0x8862,0x5f61,0x72ad,0x72b0,0x72b4,0x7377,0x7341,0x72c3,
+ 0x72c1,0x72ce,0x72cd,0x72d2,0x72e8,0x736a,0x72e9,0x733b,0x72f4,0x72f7,
+ 0x7301,0x72f3,0x736b,0x72fa
+ },
+ { /* ku 42 */
+ 0x72fb,0x7317,0x7313,0x7380,0x730a,0x731e,0x731d,0x737c,0x7322,0x7339,
+ 0x7325,0x732c,0x7338,0x7331,0x7350,0x734d,0x7357,0x7360,0x736c,0x736f,
+ 0x737e,0x821b,0x5925,0x98e7,0x5924,0x5902,0x98e0,0x9933,0x98e9,0x993c,
+ 0x98ea,0x98eb,0x98ed,0x98f4,0x9909,0x9911,0x4f59,0x991b,0x9937,0x993f,
+ 0x9943,0x9948,0x9949,0x994a,0x994c,0x9962,0x5e80,0x5ee1,0x5e8b,0x5e96,
+ 0x5ea5,0x5ea0,0x5eb9,0x5eb5,0x5ebe,0x5eb3,0x8ce1,0x5ed2,0x5ed1,0x5edb,
+ 0x5ee8,0x5eea,0x81ba,0x5fc4,0x5fc9,0x5fd6,0x61fa,0x61ae,0x5fee,0x616a,
+ 0x5fe1,0x5fe4,0x613e,0x60b5,0x6134,0x5fea,0x5fed,0x5ff8,0x6019,0x6035,
+ 0x6026,0x601b,0x600f,0x600d,0x6029,0x602b,0x600a,0x61cc,0x6021,0x615f,
+ 0x61e8,0x60fb,0x6137,0x6042
+ },
+ { /* ku 43 */
+ 0x606a,0x60f2,0x6096,0x609a,0x6173,0x609d,0x6083,0x6092,0x608c,0x609b,
+ 0x611c,0x60bb,0x60b1,0x60dd,0x60d8,0x60c6,0x60da,0x60b4,0x6120,0x6192,
+ 0x6115,0x6123,0x60f4,0x6100,0x610e,0x612b,0x614a,0x6175,0x61ac,0x6194,
+ 0x61a7,0x61b7,0x61d4,0x61f5,0x5fdd,0x96b3,0x9582,0x9586,0x95c8,0x958e,
+ 0x9594,0x958c,0x95e5,0x95ad,0x95ab,0x9b2e,0x95ac,0x95be,0x95b6,0x9b29,
+ 0x95bf,0x95bd,0x95bc,0x95c3,0x95cb,0x95d4,0x95d0,0x95d5,0x95de,0x4e2c,
+ 0x723f,0x6215,0x6c35,0x6c54,0x6c5c,0x6c4a,0x7043,0x6c85,0x6c90,0x6c94,
+ 0x6c8c,0x6c68,0x6c69,0x6c74,0x6c76,0x6c86,0x6f59,0x6cd0,0x6cd4,0x6cad,
+ 0x7027,0x7018,0x6cf1,0x6cd7,0x6cb2,0x6ce0,0x6cd6,0x6ffc,0x6ceb,0x6cee,
+ 0x6cb1,0x6cd3,0x6cef,0x6d87
+ },
+ { /* ku 44 */
+ 0x6d39,0x6d27,0x6d0c,0x6d79,0x6e5e,0x6d07,0x6d04,0x6d19,0x6d0e,0x6d2b,
+ 0x6fae,0x6d2e,0x6d35,0x6d1a,0x700f,0x6ef8,0x6f6f,0x6d33,0x6d91,0x6d6f,
+ 0x6df6,0x6f7f,0x6d5e,0x6d93,0x6d94,0x6d5c,0x6d60,0x6d7c,0x6d63,0x6e1a,
+ 0x6dc7,0x6dc5,0x6dde,0x7006,0x6dbf,0x6de0,0x6fa0,0x6de6,0x6ddd,0x6dd9,
+ 0x700b,0x6dab,0x6e0c,0x6dae,0x6e2b,0x6e6e,0x6e4e,0x6e6b,0x6eb2,0x6e5f,
+ 0x6e86,0x6e53,0x6e54,0x6e32,0x6e25,0x6e44,0x7067,0x6eb1,0x6e98,0x7044,
+ 0x6f2d,0x7005,0x6ea5,0x6ea7,0x6ebd,0x6ebb,0x6eb7,0x6f77,0x6eb4,0x6ecf,
+ 0x6e8f,0x6ec2,0x6e9f,0x6f62,0x7020,0x701f,0x6f24,0x6f15,0x6ef9,0x6f2f,
+ 0x6f36,0x7032,0x6f74,0x6f2a,0x6f09,0x6f29,0x6f89,0x6f8d,0x6f8c,0x6f78,
+ 0x6f72,0x6f7c,0x6f7a,0x7028
+ },
+ { /* ku 45 */
+ 0x6fc9,0x6fa7,0x6fb9,0x6fb6,0x6fc2,0x6fe1,0x6fee,0x6fde,0x6fe0,0x6fef,
+ 0x701a,0x7023,0x701b,0x7039,0x7035,0x705d,0x705e,0x5b80,0x5b84,0x5b95,
+ 0x5b93,0x5ba5,0x5bb8,0x752f,0x9a2b,0x6434,0x5be4,0x5bee,0x8930,0x5bf0,
+ 0x8e47,0x8b07,0x8fb6,0x8fd3,0x8fd5,0x8fe5,0x8fee,0x8fe4,0x9087,0x8fe6,
+ 0x9015,0x8fe8,0x9005,0x9004,0x900b,0x9090,0x9011,0x900d,0x9016,0x9021,
+ 0x9035,0x9036,0x902d,0x902f,0x9044,0x9051,0x9052,0x9050,0x9068,0x9058,
+ 0x9062,0x905b,0x66b9,0x9074,0x907d,0x9082,0x9088,0x9083,0x908b,0x5f50,
+ 0x5f57,0x5f56,0x5f58,0x5c3b,0x54ab,0x5c50,0x5c59,0x5b71,0x5c63,0x5c68,
+ 0x7fbc,0x5f33,0x5f29,0x5f2d,0x8274,0x5f3c,0x9b3b,0x5c6e,0x5981,0x5983,
+ 0x598d,0x5af5,0x5ad7,0x59a3
+ },
+ { /* ku 46 */
+ 0x5997,0x59ca,0x5b00,0x599e,0x59a4,0x59d2,0x59b2,0x59af,0x59d7,0x59be,
+ 0x5a6d,0x5b08,0x59dd,0x5b4c,0x59e3,0x59d8,0x59f9,0x5a0c,0x5a09,0x5aa7,
+ 0x5afb,0x5a11,0x5a23,0x5a13,0x5a40,0x5a67,0x5a4a,0x5a55,0x5a3c,0x5a62,
+ 0x5b0b,0x80ec,0x5aaa,0x5a9b,0x5a77,0x5a7a,0x5abe,0x5aeb,0x5ab2,0x5b21,
+ 0x5b2a,0x5ab8,0x5ae0,0x5ae3,0x5b19,0x5ad6,0x5ae6,0x5ad8,0x5adc,0x5b09,
+ 0x5b17,0x5b16,0x5b32,0x5b37,0x5b40,0x5c15,0x5c1c,0x5b5a,0x5b65,0x5b73,
+ 0x5b51,0x5b53,0x5b62,0x99d4,0x99df,0x99d9,0x9a36,0x9a5b,0x99d1,0x99d8,
+ 0x9a4d,0x9a4a,0x99e2,0x9a6a,0x9a0f,0x9a0d,0x9a05,0x9a42,0x9a2d,0x9a16,
+ 0x9a41,0x9a2e,0x9a38,0x9a43,0x9a44,0x9a4f,0x9a65,0x9a64,0x7cf9,0x7d06,
+ 0x7d02,0x7d07,0x7d08,0x7e8a
+ },
+ { /* ku 47 */
+ 0x7d1c,0x7d15,0x7d13,0x7d3a,0x7d32,0x7d31,0x7e10,0x7d3c,0x7d40,0x7d3f,
+ 0x7d5d,0x7d4e,0x7d73,0x7d86,0x7d83,0x7d88,0x7dbe,0x7dba,0x7dcb,0x7dd4,
+ 0x7dc4,0x7d9e,0x7dac,0x7db9,0x7da3,0x7db0,0x7dc7,0x7dd9,0x7dd7,0x7df9,
+ 0x7df2,0x7e62,0x7de6,0x7df6,0x7df1,0x7e0b,0x7de1,0x7e09,0x7e1d,0x7e1f,
+ 0x7e1e,0x7e2d,0x7e0a,0x7e11,0x7e7d,0x7e39,0x7e35,0x7e32,0x7e46,0x7e45,
+ 0x7e88,0x7e5a,0x7e52,0x7e6e,0x7e7e,0x7e70,0x7e6f,0x7e98,0x5e7a,0x757f,
+ 0x5ddb,0x753e,0x9095,0x738e,0x74a3,0x744b,0x73a2,0x739f,0x73cf,0x73c2,
+ 0x74cf,0x73b7,0x73b3,0x73c0,0x73c9,0x73c8,0x73e5,0x73d9,0x980a,0x740a,
+ 0x73e9,0x73e7,0x73de,0x74bd,0x743f,0x7489,0x742a,0x745b,0x7426,0x7425,
+ 0x7428,0x7430,0x742e,0x742c
+ },
+ { /* ku 48 */
+ 0x741b,0x741a,0x7441,0x745c,0x7457,0x7455,0x7459,0x74a6,0x746d,0x747e,
+ 0x749c,0x74d4,0x7480,0x7481,0x7487,0x748b,0x749e,0x74a8,0x74a9,0x7490,
+ 0x74a7,0x74da,0x74ba,0x97d9,0x97de,0x97dc,0x674c,0x6753,0x675e,0x6748,
+ 0x69aa,0x6aea,0x6787,0x676a,0x6773,0x6798,0x6898,0x6775,0x68d6,0x6a05,
+ 0x689f,0x678b,0x6777,0x677c,0x67f0,0x6adb,0x67d8,0x6af3,0x67e9,0x67b0,
+ 0x6ae8,0x67d9,0x67b5,0x67da,0x67b3,0x67dd,0x6800,0x67c3,0x67b8,0x67e2,
+ 0x6adf,0x67c1,0x6a89,0x6832,0x6833,0x690f,0x6a48,0x684e,0x6968,0x6844,
+ 0x69bf,0x6883,0x681d,0x6855,0x6a3a,0x6841,0x6a9c,0x6840,0x6b12,0x684a,
+ 0x6849,0x6829,0x68b5,0x688f,0x6874,0x6877,0x6893,0x686b,0x6b1e,0x696e,
+ 0x68fc,0x6add,0x69e7,0x68f9
+ },
+ { /* ku 49 */
+ 0x6b0f,0x68f0,0x690b,0x6901,0x6957,0x68e3,0x6910,0x6971,0x6939,0x6960,
+ 0x6942,0x695d,0x6b16,0x696b,0x6980,0x6998,0x6978,0x6934,0x69cc,0x6aec,
+ 0x6ada,0x69ce,0x6af8,0x6966,0x6963,0x6979,0x699b,0x69a7,0x69bb,0x69ab,
+ 0x69ad,0x69d4,0x69b1,0x69c1,0x69ca,0x6ab3,0x6995,0x6ae7,0x698d,0x69ff,
+ 0x6aa3,0x69ed,0x6a17,0x6a18,0x6a65,0x69f2,0x6a44,0x6a3e,0x6aa0,0x6a50,
+ 0x6a5b,0x6a35,0x6a8e,0x6ad3,0x6a3d,0x6a28,0x6a58,0x6ade,0x6a91,0x6a90,
+ 0x6aa9,0x6a97,0x6aab,0x7337,0x7352,0x6b81,0x6b82,0x6ba4,0x6b84,0x6b9e,
+ 0x6bae,0x6b8d,0x6bab,0x6b9b,0x6baf,0x6baa,0x8ed4,0x8edb,0x8ef2,0x8efb,
+ 0x8f64,0x8ef9,0x8efc,0x8eeb,0x8ee4,0x8f62,0x8efa,0x8efe,0x8f0a,0x8f07,
+ 0x8f05,0x8f12,0x8f26,0x8f1e
+ },
+ { /* ku 4a */
+ 0x8f1f,0x8f1c,0x8f33,0x8f46,0x8f54,0x8ece,0x6214,0x6227,0x621b,0x621f,
+ 0x6222,0x6221,0x6225,0x6224,0x6229,0x81e7,0x750c,0x74f4,0x74ff,0x750f,
+ 0x7511,0x7513,0x6534,0x65ee,0x65ef,0x65f0,0x660a,0x66c7,0x6772,0x6603,
+ 0x6615,0x6600,0x7085,0x66f7,0x661d,0x6634,0x6631,0x6636,0x6635,0x8006,
+ 0x665f,0x66c4,0x6641,0x664f,0x6689,0x6661,0x6657,0x6677,0x6684,0x668c,
+ 0x66d6,0x669d,0x66be,0x66db,0x66dc,0x66e6,0x66e9,0x8cc1,0x8cb0,0x8cba,
+ 0x8cbd,0x8d04,0x8cb2,0x8cc5,0x8d10,0x8cd1,0x8cda,0x8cd5,0x8ceb,0x8ce7,
+ 0x8cfb,0x8998,0x89ac,0x89a1,0x89bf,0x89a6,0x89af,0x89b2,0x89b7,0x726e,
+ 0x729f,0x725d,0x7266,0x726f,0x727e,0x727f,0x7284,0x728b,0x728d,0x728f,
+ 0x7292,0x6308,0x6332,0x63b0
+ },
+ { /* ku 4b */
+ 0x643f,0x64d8,0x8004,0x6bea,0x6bf3,0x6bfd,0x6bff,0x6bf9,0x6c05,0x6c0c,
+ 0x6c06,0x6c0d,0x6c15,0x6c18,0x6c19,0x6c1a,0x6c21,0x6c2c,0x6c24,0x6c2a,
+ 0x6c32,0x6535,0x6555,0x656b,0x7258,0x7252,0x7256,0x7230,0x8662,0x5216,
+ 0x809f,0x809c,0x8093,0x80bc,0x670a,0x80bd,0x80b1,0x80ab,0x80ad,0x80b4,
+ 0x80b7,0x6727,0x8156,0x80e9,0x81da,0x80db,0x80c2,0x80c4,0x80d9,0x80cd,
+ 0x80d7,0x6710,0x80dd,0x811b,0x80f1,0x80f4,0x80ed,0x81be,0x810e,0x80f2,
+ 0x80fc,0x6715,0x8112,0x8c5a,0x8161,0x811e,0x812c,0x8118,0x8132,0x8148,
+ 0x814c,0x8153,0x8174,0x8159,0x815a,0x8171,0x8160,0x8169,0x817c,0x817d,
+ 0x816d,0x8167,0x584d,0x5ab5,0x8188,0x8182,0x81cf,0x6ed5,0x81a3,0x81aa,
+ 0x81cc,0x6726,0x81ca,0x81bb
+ },
+ { /* ku 4c */
+ 0x81c1,0x81a6,0x6b5f,0x6b37,0x6b39,0x6b43,0x6b46,0x6b59,0x98ae,0x98af,
+ 0x98b6,0x98bc,0x98c6,0x98c8,0x6bb3,0x5f40,0x8f42,0x89f3,0x6590,0x9f4f,
+ 0x6595,0x65bc,0x65c6,0x65c4,0x65c3,0x65cc,0x65ce,0x65d2,0x65d6,0x716c,
+ 0x7152,0x7096,0x7197,0x70bb,0x70c0,0x70b7,0x70ab,0x70b1,0x71c1,0x70ca,
+ 0x7110,0x7113,0x71dc,0x712f,0x7131,0x7173,0x715c,0x7168,0x7145,0x7172,
+ 0x714a,0x7178,0x717a,0x7198,0x71b3,0x71b5,0x71a8,0x71a0,0x71e0,0x71d4,
+ 0x71e7,0x71f9,0x721d,0x7228,0x706c,0x71fe,0x7166,0x71b9,0x623e,0x623d,
+ 0x6243,0x6248,0x6249,0x793b,0x7940,0x7946,0x7949,0x795b,0x795c,0x7953,
+ 0x795a,0x79b0,0x7957,0x7960,0x798e,0x7967,0x797a,0x79aa,0x798a,0x799a,
+ 0x79a7,0x79b3,0x5fd1,0x5fd0
+ },
+ { /* ku 4d */
+ 0x61df,0x605d,0x605a,0x6067,0x6041,0x6059,0x6063,0x6164,0x6106,0x610d,
+ 0x615d,0x61a9,0x619d,0x61cb,0x61e3,0x6207,0x8080,0x807f,0x6c93,0x6fa9,
+ 0x6dfc,0x78ef,0x77f8,0x78ad,0x7809,0x7868,0x7818,0x7811,0x65ab,0x782d,
+ 0x78b8,0x781d,0x7839,0x792a,0x7931,0x781f,0x783c,0x7825,0x782c,0x7823,
+ 0x7829,0x784e,0x786d,0x7864,0x78fd,0x7826,0x7850,0x7847,0x784c,0x786a,
+ 0x78e7,0x7893,0x789a,0x7887,0x78e3,0x78a1,0x78a3,0x78b2,0x78b9,0x78a5,
+ 0x78d4,0x78d9,0x78c9,0x78ec,0x78f2,0x7905,0x78f4,0x7913,0x7924,0x791e,
+ 0x7934,0x9f95,0x9ef9,0x9efb,0x9efc,0x76f1,0x7704,0x7798,0x76f9,0x7707,
+ 0x7708,0x771a,0x7722,0x7719,0x772d,0x7726,0x7735,0x7738,0x775e,0x77bc,
+ 0x7747,0x7743,0x775a,0x7768
+ },
+ { /* ku 4e */
+ 0x7762,0x7765,0x777f,0x778d,0x777d,0x7780,0x778c,0x7791,0x779f,0x77a0,
+ 0x77b0,0x77b5,0x77bd,0x753a,0x7540,0x754e,0x754b,0x7548,0x755b,0x7572,
+ 0x7579,0x7583,0x7f58,0x7f61,0x7f5f,0x8a48,0x7f68,0x7f86,0x7f71,0x7f79,
+ 0x7f88,0x7f7e,0x76cd,0x76e5,0x8832,0x91d2,0x91d3,0x91d4,0x91d9,0x91d7,
+ 0x91d5,0x91f7,0x91e7,0x91e4,0x9346,0x91f5,0x91f9,0x9208,0x9226,0x9245,
+ 0x9211,0x9210,0x9201,0x9227,0x9204,0x9225,0x9200,0x923a,0x9266,0x9237,
+ 0x9233,0x9255,0x923d,0x9238,0x925e,0x926c,0x926d,0x923f,0x9460,0x9230,
+ 0x9249,0x9248,0x924d,0x922e,0x9239,0x9438,0x92ac,0x92a0,0x927a,0x92aa,
+ 0x92ee,0x92cf,0x9403,0x92e3,0x943a,0x92b1,0x92a6,0x93a7,0x9296,0x92cc,
+ 0x92a9,0x93f5,0x9293,0x927f
+ },
+ { /* ku 4f */
+ 0x93a9,0x929a,0x931a,0x92ab,0x9283,0x940b,0x92a8,0x92a3,0x9412,0x9338,
+ 0x92f1,0x93d7,0x92e5,0x92f0,0x92ef,0x92e8,0x92bc,0x92dd,0x92f6,0x9426,
+ 0x9427,0x92c3,0x92df,0x92e6,0x9312,0x9306,0x9369,0x931b,0x9340,0x9301,
+ 0x9315,0x932e,0x9343,0x9307,0x9308,0x931f,0x9319,0x9365,0x9347,0x9376,
+ 0x9354,0x9364,0x93aa,0x9370,0x9384,0x93e4,0x93d8,0x9428,0x9387,0x93cc,
+ 0x9398,0x93b8,0x93bf,0x93a6,0x93b0,0x93b5,0x944c,0x93e2,0x93dc,0x93dd,
+ 0x93cd,0x93de,0x93c3,0x93c7,0x93d1,0x9414,0x941d,0x93f7,0x9465,0x9413,
+ 0x946d,0x9420,0x9479,0x93f9,0x9419,0x944a,0x9432,0x943f,0x9454,0x9463,
+ 0x937e,0x77e7,0x77ec,0x96c9,0x79d5,0x79ed,0x79e3,0x79eb,0x7a06,0x5d47,
+ 0x7a03,0x7a02,0x7a1e,0x7a14
+ },
+ { /* ku 50 */
+ 0x7a39,0x7a37,0x7a61,0x9ecf,0x99a5,0x7a70,0x7688,0x768e,0x7693,0x7699,
+ 0x76a4,0x74de,0x74e0,0x752c,0x9ce9,0x9cf6,0x9d07,0x9d06,0x9d23,0x9d87,
+ 0x9e15,0x9d1d,0x9d1f,0x9de5,0x9d2f,0x9dd9,0x9d30,0x9d42,0x9e1e,0x9d53,
+ 0x9e1d,0x9d60,0x9d52,0x9df3,0x9d5c,0x9d61,0x9d93,0x9d6a,0x9d6f,0x9d89,
+ 0x9d98,0x9d9a,0x9dc0,0x9da5,0x9da9,0x9dc2,0x9dbc,0x9e1a,0x9dd3,0x9dda,
+ 0x9def,0x9de6,0x9df2,0x9df8,0x9e0c,0x9dfa,0x9e1b,0x7592,0x7594,0x7664,
+ 0x7658,0x759d,0x7667,0x75a3,0x75b3,0x75b4,0x75b8,0x75c4,0x75b1,0x75b0,
+ 0x75c3,0x75c2,0x7602,0x75cd,0x75e3,0x7646,0x75e6,0x75e4,0x7647,0x75e7,
+ 0x7603,0x75f1,0x75fc,0x75ff,0x7610,0x7600,0x7649,0x760c,0x761e,0x760a,
+ 0x7625,0x763b,0x7615,0x7619
+ },
+ { /* ku 51 */
+ 0x761b,0x763c,0x7622,0x7620,0x7640,0x762d,0x7630,0x766d,0x7635,0x7643,
+ 0x766e,0x7633,0x764d,0x7669,0x7654,0x765c,0x7656,0x7672,0x766f,0x7fca,
+ 0x7ae6,0x7a78,0x7a79,0x7a80,0x7a86,0x7a88,0x7a95,0x7ac7,0x7aa0,0x7aac,
+ 0x7aa8,0x7ab6,0x7ab3,0x8864,0x8869,0x8872,0x887d,0x887f,0x8882,0x88a2,
+ 0x8960,0x88b7,0x88bc,0x88c9,0x8933,0x88ce,0x895d,0x8947,0x88f1,0x891a,
+ 0x88fc,0x88e8,0x88fe,0x88f0,0x8921,0x8919,0x8913,0x8938,0x890a,0x8964,
+ 0x892b,0x8936,0x8941,0x8966,0x897b,0x758b,0x80e5,0x76b8,0x76b4,0x77dc,
+ 0x8012,0x8014,0x8016,0x801c,0x8020,0x802e,0x8025,0x8026,0x802c,0x8029,
+ 0x8028,0x8031,0x800b,0x8035,0x8043,0x8046,0x8079,0x8052,0x8075,0x8071,
+ 0x8983,0x9807,0x980e,0x980f
+ },
+ { /* ku 52 */
+ 0x9821,0x981c,0x6f41,0x9826,0x9837,0x984e,0x9853,0x9873,0x9862,0x9859,
+ 0x9865,0x986c,0x9870,0x864d,0x8654,0x866c,0x87e3,0x8806,0x867a,0x867c,
+ 0x867b,0x86a8,0x868d,0x868b,0x8706,0x869d,0x86a7,0x86a3,0x86aa,0x8693,
+ 0x86a9,0x86b6,0x86c4,0x86b5,0x8823,0x86b0,0x86ba,0x86b1,0x86af,0x86c9,
+ 0x87f6,0x86b4,0x86e9,0x86fa,0x87ef,0x86ed,0x8784,0x86d0,0x8713,0x86de,
+ 0x8810,0x86df,0x86d8,0x86d1,0x8703,0x8707,0x86f8,0x8708,0x870a,0x870d,
+ 0x8709,0x8723,0x873b,0x871e,0x8725,0x872e,0x871a,0x873e,0x87c8,0x8734,
+ 0x8731,0x8729,0x8737,0x873f,0x8782,0x8722,0x877d,0x8811,0x877b,0x8760,
+ 0x8770,0x874c,0x876e,0x878b,0x8753,0x8763,0x87bb,0x8764,0x8759,0x8765,
+ 0x8793,0x87af,0x87ce,0x87d2
+ },
+ { /* ku 53 */
+ 0x87c6,0x8788,0x8785,0x87ad,0x8797,0x8783,0x87ab,0x87e5,0x87ac,0x87b5,
+ 0x87b3,0x87cb,0x87d3,0x87bd,0x87d1,0x87c0,0x87ca,0x87db,0x87ea,0x87e0,
+ 0x87ee,0x8816,0x8813,0x87fe,0x880a,0x881b,0x8821,0x8839,0x883c,0x7f36,
+ 0x7f4c,0x7f44,0x7f45,0x8210,0x7afa,0x7afd,0x7b08,0x7be4,0x7b04,0x7b67,
+ 0x7b0a,0x7b2b,0x7b0f,0x7b47,0x7b38,0x7b2a,0x7b19,0x7b2e,0x7b31,0x7b20,
+ 0x7b25,0x7b24,0x7b33,0x7c69,0x7b1e,0x7b58,0x7bf3,0x7b45,0x7b75,0x7b4c,
+ 0x7b8f,0x7b60,0x7b6e,0x7b7b,0x7b62,0x7b72,0x7b71,0x7b90,0x7c00,0x7bcb,
+ 0x7bb8,0x7bac,0x7b9d,0x7c5c,0x7b85,0x7c1e,0x7b9c,0x7ba2,0x7c2b,0x7bb4,
+ 0x7c23,0x7bc1,0x7bcc,0x7bdd,0x7bda,0x7be5,0x7be6,0x7bea,0x7c0c,0x7bfe,
+ 0x7bfc,0x7c0f,0x7c6a,0x7c0b
+ },
+ { /* ku 54 */
+ 0x7c1f,0x7c2a,0x7c26,0x7c38,0x7c5f,0x7c40,0x81fe,0x8201,0x8202,0x8204,
+ 0x81ec,0x8844,0x8221,0x8222,0x8264,0x822d,0x822f,0x8228,0x822b,0x8238,
+ 0x826b,0x8233,0x8234,0x823e,0x8244,0x8249,0x824b,0x824f,0x825a,0x825f,
+ 0x8268,0x887e,0x88ca,0x8888,0x88d8,0x88df,0x895e,0x7f9d,0x7fa5,0x7fa7,
+ 0x7faf,0x7fb0,0x7fb2,0x7c7c,0x6549,0x7c91,0x7cf2,0x7cf6,0x7c9e,0x7ca2,
+ 0x7cb2,0x7cbc,0x7cbd,0x7cdd,0x7cc7,0x7ccc,0x7ccd,0x7cc8,0x7cc5,0x7cd7,
+ 0x7ce8,0x826e,0x66a8,0x7fbf,0x7fce,0x7fd5,0x7fe5,0x7fe1,0x7fe6,0x7fe9,
+ 0x7fee,0x7ff3,0x7cf8,0x7e36,0x7da6,0x7dae,0x7e47,0x7e9b,0x9ea9,0x9eb4,
+ 0x8d73,0x8d84,0x8d94,0x8d91,0x8db2,0x8d67,0x8d6d,0x8c47,0x8c49,0x914a,
+ 0x9150,0x914e,0x914f,0x9164
+ },
+ { /* ku 55 */
+ 0x9162,0x9161,0x9170,0x9169,0x916f,0x91c5,0x91c3,0x9172,0x9174,0x9179,
+ 0x918c,0x9185,0x9190,0x918d,0x9191,0x91a2,0x91a3,0x91aa,0x91ad,0x91ae,
+ 0x91af,0x91b5,0x91b4,0x91ba,0x8c55,0x9e7a,0x8e89,0x8deb,0x8e05,0x8e59,
+ 0x8e69,0x8db5,0x8dbf,0x8dbc,0x8dba,0x8e4c,0x8dd6,0x8dd7,0x8dda,0x8e92,
+ 0x8dce,0x8dcf,0x8ddb,0x8dc6,0x8dec,0x8e7a,0x8e55,0x8de3,0x8e9a,0x8e8b,
+ 0x8de4,0x8e09,0x8dfd,0x8e14,0x8e1d,0x8e1f,0x8e93,0x8e2e,0x8e23,0x8e91,
+ 0x8e3a,0x8e40,0x8e39,0x8e35,0x8e3d,0x8e31,0x8e49,0x8e41,0x8e42,0x8ea1,
+ 0x8e63,0x8e4a,0x8e70,0x8e76,0x8e7c,0x8e6f,0x8e74,0x8e85,0x8eaa,0x8e94,
+ 0x8e90,0x8ea6,0x8e9e,0x8c78,0x8c82,0x8c8a,0x8c85,0x8c98,0x8c94,0x659b,
+ 0x89d6,0x89f4,0x89da,0x89dc
+ },
+ { /* ku 56 */
+ 0x89e5,0x89eb,0x89f6,0x8a3e,0x8b26,0x975a,0x96e9,0x9742,0x96ef,0x9706,
+ 0x973d,0x9708,0x970f,0x970e,0x972a,0x9744,0x9730,0x973e,0x9f54,0x9f5f,
+ 0x9f59,0x9f60,0x9f5c,0x9f66,0x9f6c,0x9f6a,0x9f77,0x9efd,0x9eff,0x9f09,
+ 0x96b9,0x96bc,0x96bd,0x96ce,0x96d2,0x77bf,0x8b8e,0x928e,0x947e,0x92c8,
+ 0x93e8,0x936a,0x93ca,0x938f,0x943e,0x946b,0x9b77,0x9b74,0x9b81,0x9b83,
+ 0x9b8e,0x9c78,0x7a4c,0x9b92,0x9c5f,0x9b90,0x9bad,0x9b9a,0x9baa,0x9b9e,
+ 0x9c6d,0x9bab,0x9b9d,0x9c58,0x9bc1,0x9c7a,0x9c31,0x9c39,0x9c23,0x9c37,
+ 0x9bc0,0x9bca,0x9bc7,0x9bfd,0x9bd6,0x9bea,0x9beb,0x9be1,0x9be4,0x9be7,
+ 0x9bdd,0x9be2,0x9bf0,0x9bdb,0x9bf4,0x9bd4,0x9c5d,0x9c08,0x9c10,0x9c0d,
+ 0x9c12,0x9c09,0x9bff,0x9c20
+ },
+ { /* ku 57 */
+ 0x9c32,0x9c2d,0x9c28,0x9c25,0x9c29,0x9c33,0x9c3e,0x9c48,0x9c3b,0x9c35,
+ 0x9c45,0x9c56,0x9c54,0x9c52,0x9c67,0x977c,0x9785,0x97c3,0x97bd,0x9794,
+ 0x97c9,0x97ab,0x97a3,0x97b2,0x97b4,0x9ab1,0x9ab0,0x9ab7,0x9dbb,0x9ab6,
+ 0x9aba,0x9abc,0x9ac1,0x9ac0,0x9acf,0x9ac2,0x9ad6,0x9ad5,0x9ad1,0x9b45,
+ 0x9b43,0x9b58,0x9b4e,0x9b48,0x9b4d,0x9b51,0x9957,0x995c,0x992e,0x9955,
+ 0x9954,0x9adf,0x9ae1,0x9ae6,0x9aef,0x9aeb,0x9afb,0x9aed,0x9af9,0x9b08,
+ 0x9b0f,0x9b22,0x9b1f,0x9b23,0x4e48,0x9ebe,0x7e3b,0x9e82,0x9e87,0x9e88,
+ 0x9e8b,0x9e92,0x93d6,0x9e9d,0x9e9f,0x9edb,0x9edc,0x9edd,0x9ee0,0x9edf,
+ 0x9ee2,0x9ef7,0x9ee7,0x9ee5,0x9ef2,0x9eef,0x9f22,0x9f2c,0x9f2f,0x9f39,
+ 0x9f37,0x9f3d,0x9f3e,0x9f44
+ },
+ { /* ku 58 */
+ 0x896c,0x95c6,0x9336,0x5f46,0x8514,0x7e94,0x5382,0x51b2,0x4e11,0x9f63,
+ 0x5679,0x515a,0x6dc0,0x9f15,0x6597,0x5641,0x9aee,0x8303,0x4e30,0x8907,
+ 0x5e72,0x7a40,0x98b3,0x5e7f,0x95a4,0x9b0d,0x5212,0x8ff4,0x5f59,0x7a6b,
+ 0x98e2,0x51e0,0x50a2,0x4ef7,0x8350,0x8591,0x5118,0x636e,0x6372,0x524b,
+ 0x5938,0x774f,0x8721,0x814a,0x7e8d,0x91cc,0x66c6,0x5e18,0x77ad,0x9e75,
+ 0x56c9,0x9ef4,0x6fdb,0x61de,0x77c7,0x7030,0x9eb5,0x884a,0x95e2,0x82f9,
+ 0x51ed,0x6251,0x4ec6,0x6734,0x97c6,0x7c64,0x7e34,0x97a6,0x9eaf,0x786e,
+ 0x820d,0x672f,0x677e,0x56cc,0x53f0,0x98b1,0x6aaf,0x7f4e,0x6d82,0x7cf0,
+ 0x4e07,0x4fc2,0x7e6b,0x9e79,0x56ae,0x9b1a,0x846f,0x53f6,0x90c1,0x79a6,
+ 0x7c72,0x613f,0x4e91,0x9ad2
+ },
+ { /* ku 59 */
+ 0x75c7,0x96bb,0x53ea,0x7dfb,0x88fd,0x79cd,0x7843,0x7b51,0x51c6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/gb_2312.c b/imap/src/charset/gb_2312.c
new file mode 100644
index 00000000..78efbbb8
--- /dev/null
+++ b/imap/src/charset/gb_2312.c
@@ -0,0 +1,2795 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GBK conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* GB 2312 is the national standard of the People's Republic of China
+ * (mainland China). This is actually the GBK extended version.
+ */
+
+#define BASE_GB2312_KU 0x81
+#define BASE_GB2312_TEN 0x40
+#define MAX_GB2312_KU 125
+#define MAX_GB2312_TEN 191
+
+
+#define GBTOUNICODE(c,c1,ku,ten) \
+ ((((ku = c - BASE_GB2312_KU) < MAX_GB2312_KU) && \
+ ((ten = c1 - BASE_GB2312_TEN) < MAX_GB2312_TEN)) ? \
+ gb2312tab[ku][ten] : UBOGON)
+
+static const unsigned short gb2312tab[MAX_GB2312_KU][MAX_GB2312_TEN] = {
+ { /* ku 01 */
+ 0x4e02,0x4e04,0x4e05,0x4e06,0x4e0f,0x4e12,0x4e17,0x4e1f,0x4e20,0x4e21,
+ 0x4e23,0x4e26,0x4e29,0x4e2e,0x4e2f,0x4e31,0x4e33,0x4e35,0x4e37,0x4e3c,
+ 0x4e40,0x4e41,0x4e42,0x4e44,0x4e46,0x4e4a,0x4e51,0x4e55,0x4e57,0x4e5a,
+ 0x4e5b,0x4e62,0x4e63,0x4e64,0x4e65,0x4e67,0x4e68,0x4e6a,0x4e6b,0x4e6c,
+ 0x4e6d,0x4e6e,0x4e6f,0x4e72,0x4e74,0x4e75,0x4e76,0x4e77,0x4e78,0x4e79,
+ 0x4e7a,0x4e7b,0x4e7c,0x4e7d,0x4e7f,0x4e80,0x4e81,0x4e82,0x4e83,0x4e84,
+ 0x4e85,0x4e87,0x4e8a,UBOGON,0x4e90,0x4e96,0x4e97,0x4e99,0x4e9c,0x4e9d,
+ 0x4e9e,0x4ea3,0x4eaa,0x4eaf,0x4eb0,0x4eb1,0x4eb4,0x4eb6,0x4eb7,0x4eb8,
+ 0x4eb9,0x4ebc,0x4ebd,0x4ebe,0x4ec8,0x4ecc,0x4ecf,0x4ed0,0x4ed2,0x4eda,
+ 0x4edb,0x4edc,0x4ee0,0x4ee2,0x4ee6,0x4ee7,0x4ee9,0x4eed,0x4eee,0x4eef,
+ 0x4ef1,0x4ef4,0x4ef8,0x4ef9,0x4efa,0x4efc,0x4efe,0x4f00,0x4f02,0x4f03,
+ 0x4f04,0x4f05,0x4f06,0x4f07,0x4f08,0x4f0b,0x4f0c,0x4f12,0x4f13,0x4f14,
+ 0x4f15,0x4f16,0x4f1c,0x4f1d,0x4f21,0x4f23,0x4f28,0x4f29,0x4f2c,0x4f2d,
+ 0x4f2e,0x4f31,0x4f33,0x4f35,0x4f37,0x4f39,0x4f3b,0x4f3e,0x4f3f,0x4f40,
+ 0x4f41,0x4f42,0x4f44,0x4f45,0x4f47,0x4f48,0x4f49,0x4f4a,0x4f4b,0x4f4c,
+ 0x4f52,0x4f54,0x4f56,0x4f61,0x4f62,0x4f66,0x4f68,0x4f6a,0x4f6b,0x4f6d,
+ 0x4f6e,0x4f71,0x4f72,0x4f75,0x4f77,0x4f78,0x4f79,0x4f7a,0x4f7d,0x4f80,
+ 0x4f81,0x4f82,0x4f85,0x4f86,0x4f87,0x4f8a,0x4f8c,0x4f8e,0x4f90,0x4f92,
+ 0x4f93,0x4f95,0x4f96,0x4f98,0x4f99,0x4f9a,0x4f9c,0x4f9e,0x4f9f,0x4fa1,
+ 0x4fa2
+ },
+ { /* ku 02 */
+ 0x4fa4,0x4fab,0x4fad,0x4fb0,0x4fb1,0x4fb2,0x4fb3,0x4fb4,0x4fb6,0x4fb7,
+ 0x4fb8,0x4fb9,0x4fba,0x4fbb,0x4fbc,0x4fbd,0x4fbe,0x4fc0,0x4fc1,0x4fc2,
+ 0x4fc6,0x4fc7,0x4fc8,0x4fc9,0x4fcb,0x4fcc,0x4fcd,0x4fd2,0x4fd3,0x4fd4,
+ 0x4fd5,0x4fd6,0x4fd9,0x4fdb,0x4fe0,0x4fe2,0x4fe4,0x4fe5,0x4fe7,0x4feb,
+ 0x4fec,0x4ff0,0x4ff2,0x4ff4,0x4ff5,0x4ff6,0x4ff7,0x4ff9,0x4ffb,0x4ffc,
+ 0x4ffd,0x4fff,0x5000,0x5001,0x5002,0x5003,0x5004,0x5005,0x5006,0x5007,
+ 0x5008,0x5009,0x500a,UBOGON,0x500b,0x500e,0x5010,0x5011,0x5013,0x5015,
+ 0x5016,0x5017,0x501b,0x501d,0x501e,0x5020,0x5022,0x5023,0x5024,0x5027,
+ 0x502b,0x502f,0x5030,0x5031,0x5032,0x5033,0x5034,0x5035,0x5036,0x5037,
+ 0x5038,0x5039,0x503b,0x503d,0x503f,0x5040,0x5041,0x5042,0x5044,0x5045,
+ 0x5046,0x5049,0x504a,0x504b,0x504d,0x5050,0x5051,0x5052,0x5053,0x5054,
+ 0x5056,0x5057,0x5058,0x5059,0x505b,0x505d,0x505e,0x505f,0x5060,0x5061,
+ 0x5062,0x5063,0x5064,0x5066,0x5067,0x5068,0x5069,0x506a,0x506b,0x506d,
+ 0x506e,0x506f,0x5070,0x5071,0x5072,0x5073,0x5074,0x5075,0x5078,0x5079,
+ 0x507a,0x507c,0x507d,0x5081,0x5082,0x5083,0x5084,0x5086,0x5087,0x5089,
+ 0x508a,0x508b,0x508c,0x508e,0x508f,0x5090,0x5091,0x5092,0x5093,0x5094,
+ 0x5095,0x5096,0x5097,0x5098,0x5099,0x509a,0x509b,0x509c,0x509d,0x509e,
+ 0x509f,0x50a0,0x50a1,0x50a2,0x50a4,0x50a6,0x50aa,0x50ab,0x50ad,0x50ae,
+ 0x50af,0x50b0,0x50b1,0x50b3,0x50b4,0x50b5,0x50b6,0x50b7,0x50b8,0x50b9,
+ 0x50bc
+ },
+ { /* ku 03 */
+ 0x50bd,0x50be,0x50bf,0x50c0,0x50c1,0x50c2,0x50c3,0x50c4,0x50c5,0x50c6,
+ 0x50c7,0x50c8,0x50c9,0x50ca,0x50cb,0x50cc,0x50cd,0x50ce,0x50d0,0x50d1,
+ 0x50d2,0x50d3,0x50d4,0x50d5,0x50d7,0x50d8,0x50d9,0x50db,0x50dc,0x50dd,
+ 0x50de,0x50df,0x50e0,0x50e1,0x50e2,0x50e3,0x50e4,0x50e5,0x50e8,0x50e9,
+ 0x50ea,0x50eb,0x50ef,0x50f0,0x50f1,0x50f2,0x50f4,0x50f6,0x50f7,0x50f8,
+ 0x50f9,0x50fa,0x50fc,0x50fd,0x50fe,0x50ff,0x5100,0x5101,0x5102,0x5103,
+ 0x5104,0x5105,0x5108,UBOGON,0x5109,0x510a,0x510c,0x510d,0x510e,0x510f,
+ 0x5110,0x5111,0x5113,0x5114,0x5115,0x5116,0x5117,0x5118,0x5119,0x511a,
+ 0x511b,0x511c,0x511d,0x511e,0x511f,0x5120,0x5122,0x5123,0x5124,0x5125,
+ 0x5126,0x5127,0x5128,0x5129,0x512a,0x512b,0x512c,0x512d,0x512e,0x512f,
+ 0x5130,0x5131,0x5132,0x5133,0x5134,0x5135,0x5136,0x5137,0x5138,0x5139,
+ 0x513a,0x513b,0x513c,0x513d,0x513e,0x5142,0x5147,0x514a,0x514c,0x514e,
+ 0x514f,0x5150,0x5152,0x5153,0x5157,0x5158,0x5159,0x515b,0x515d,0x515e,
+ 0x515f,0x5160,0x5161,0x5163,0x5164,0x5166,0x5167,0x5169,0x516a,0x516f,
+ 0x5172,0x517a,0x517e,0x517f,0x5183,0x5184,0x5186,0x5187,0x518a,0x518b,
+ 0x518e,0x518f,0x5190,0x5191,0x5193,0x5194,0x5198,0x519a,0x519d,0x519e,
+ 0x519f,0x51a1,0x51a3,0x51a6,0x51a7,0x51a8,0x51a9,0x51aa,0x51ad,0x51ae,
+ 0x51b4,0x51b8,0x51b9,0x51ba,0x51be,0x51bf,0x51c1,0x51c2,0x51c3,0x51c5,
+ 0x51c8,0x51ca,0x51cd,0x51ce,0x51d0,0x51d2,0x51d3,0x51d4,0x51d5,0x51d6,
+ 0x51d7
+ },
+ { /* ku 04 */
+ 0x51d8,0x51d9,0x51da,0x51dc,0x51de,0x51df,0x51e2,0x51e3,0x51e5,0x51e6,
+ 0x51e7,0x51e8,0x51e9,0x51ea,0x51ec,0x51ee,0x51f1,0x51f2,0x51f4,0x51f7,
+ 0x51fe,0x5204,0x5205,0x5209,0x520b,0x520c,0x520f,0x5210,0x5213,0x5214,
+ 0x5215,0x521c,0x521e,0x521f,0x5221,0x5222,0x5223,0x5225,0x5226,0x5227,
+ 0x522a,0x522c,0x522f,0x5231,0x5232,0x5234,0x5235,0x523c,0x523e,0x5244,
+ 0x5245,0x5246,0x5247,0x5248,0x5249,0x524b,0x524e,0x524f,0x5252,0x5253,
+ 0x5255,0x5257,0x5258,UBOGON,0x5259,0x525a,0x525b,0x525d,0x525f,0x5260,
+ 0x5262,0x5263,0x5264,0x5266,0x5268,0x526b,0x526c,0x526d,0x526e,0x5270,
+ 0x5271,0x5273,0x5274,0x5275,0x5276,0x5277,0x5278,0x5279,0x527a,0x527b,
+ 0x527c,0x527e,0x5280,0x5283,0x5284,0x5285,0x5286,0x5287,0x5289,0x528a,
+ 0x528b,0x528c,0x528d,0x528e,0x528f,0x5291,0x5292,0x5294,0x5295,0x5296,
+ 0x5297,0x5298,0x5299,0x529a,0x529c,0x52a4,0x52a5,0x52a6,0x52a7,0x52ae,
+ 0x52af,0x52b0,0x52b4,0x52b5,0x52b6,0x52b7,0x52b8,0x52b9,0x52ba,0x52bb,
+ 0x52bc,0x52bd,0x52c0,0x52c1,0x52c2,0x52c4,0x52c5,0x52c6,0x52c8,0x52ca,
+ 0x52cc,0x52cd,0x52ce,0x52cf,0x52d1,0x52d3,0x52d4,0x52d5,0x52d7,0x52d9,
+ 0x52da,0x52db,0x52dc,0x52dd,0x52de,0x52e0,0x52e1,0x52e2,0x52e3,0x52e5,
+ 0x52e6,0x52e7,0x52e8,0x52e9,0x52ea,0x52eb,0x52ec,0x52ed,0x52ee,0x52ef,
+ 0x52f1,0x52f2,0x52f3,0x52f4,0x52f5,0x52f6,0x52f7,0x52f8,0x52fb,0x52fc,
+ 0x52fd,0x5301,0x5302,0x5303,0x5304,0x5307,0x5309,0x530a,0x530b,0x530c,
+ 0x530e
+ },
+ { /* ku 05 */
+ 0x5311,0x5312,0x5313,0x5314,0x5318,0x531b,0x531c,0x531e,0x531f,0x5322,
+ 0x5324,0x5325,0x5327,0x5328,0x5329,0x532b,0x532c,0x532d,0x532f,0x5330,
+ 0x5331,0x5332,0x5333,0x5334,0x5335,0x5336,0x5337,0x5338,0x533c,0x533d,
+ 0x5340,0x5342,0x5344,0x5346,0x534b,0x534c,0x534d,0x5350,0x5354,0x5358,
+ 0x5359,0x535b,0x535d,0x5365,0x5368,0x536a,0x536c,0x536d,0x5372,0x5376,
+ 0x5379,0x537b,0x537c,0x537d,0x537e,0x5380,0x5381,0x5383,0x5387,0x5388,
+ 0x538a,0x538e,0x538f,UBOGON,0x5390,0x5391,0x5392,0x5393,0x5394,0x5396,
+ 0x5397,0x5399,0x539b,0x539c,0x539e,0x53a0,0x53a1,0x53a4,0x53a7,0x53aa,
+ 0x53ab,0x53ac,0x53ad,0x53af,0x53b0,0x53b1,0x53b2,0x53b3,0x53b4,0x53b5,
+ 0x53b7,0x53b8,0x53b9,0x53ba,0x53bc,0x53bd,0x53be,0x53c0,0x53c3,0x53c4,
+ 0x53c5,0x53c6,0x53c7,0x53ce,0x53cf,0x53d0,0x53d2,0x53d3,0x53d5,0x53da,
+ 0x53dc,0x53dd,0x53de,0x53e1,0x53e2,0x53e7,0x53f4,0x53fa,0x53fe,0x53ff,
+ 0x5400,0x5402,0x5405,0x5407,0x540b,0x5414,0x5418,0x5419,0x541a,0x541c,
+ 0x5422,0x5424,0x5425,0x542a,0x5430,0x5433,0x5436,0x5437,0x543a,0x543d,
+ 0x543f,0x5441,0x5442,0x5444,0x5445,0x5447,0x5449,0x544c,0x544d,0x544e,
+ 0x544f,0x5451,0x545a,0x545d,0x545e,0x545f,0x5460,0x5461,0x5463,0x5465,
+ 0x5467,0x5469,0x546a,0x546b,0x546c,0x546d,0x546e,0x546f,0x5470,0x5474,
+ 0x5479,0x547a,0x547e,0x547f,0x5481,0x5483,0x5485,0x5487,0x5488,0x5489,
+ 0x548a,0x548d,0x5491,0x5493,0x5497,0x5498,0x549c,0x549e,0x549f,0x54a0,
+ 0x54a1
+ },
+ { /* ku 06 */
+ 0x54a2,0x54a5,0x54ae,0x54b0,0x54b2,0x54b5,0x54b6,0x54b7,0x54b9,0x54ba,
+ 0x54bc,0x54be,0x54c3,0x54c5,0x54ca,0x54cb,0x54d6,0x54d8,0x54db,0x54e0,
+ 0x54e1,0x54e2,0x54e3,0x54e4,0x54eb,0x54ec,0x54ef,0x54f0,0x54f1,0x54f4,
+ 0x54f5,0x54f6,0x54f7,0x54f8,0x54f9,0x54fb,0x54fe,0x5500,0x5502,0x5503,
+ 0x5504,0x5505,0x5508,0x550a,0x550b,0x550c,0x550d,0x550e,0x5512,0x5513,
+ 0x5515,0x5516,0x5517,0x5518,0x5519,0x551a,0x551c,0x551d,0x551e,0x551f,
+ 0x5521,0x5525,0x5526,UBOGON,0x5528,0x5529,0x552b,0x552d,0x5532,0x5534,
+ 0x5535,0x5536,0x5538,0x5539,0x553a,0x553b,0x553d,0x5540,0x5542,0x5545,
+ 0x5547,0x5548,0x554b,0x554c,0x554d,0x554e,0x554f,0x5551,0x5552,0x5553,
+ 0x5554,0x5557,0x5558,0x5559,0x555a,0x555b,0x555d,0x555e,0x555f,0x5560,
+ 0x5562,0x5563,0x5568,0x5569,0x556b,0x556f,0x5570,0x5571,0x5572,0x5573,
+ 0x5574,0x5579,0x557a,0x557d,0x557f,0x5585,0x5586,0x558c,0x558d,0x558e,
+ 0x5590,0x5592,0x5593,0x5595,0x5596,0x5597,0x559a,0x559b,0x559e,0x55a0,
+ 0x55a1,0x55a2,0x55a3,0x55a4,0x55a5,0x55a6,0x55a8,0x55a9,0x55aa,0x55ab,
+ 0x55ac,0x55ad,0x55ae,0x55af,0x55b0,0x55b2,0x55b4,0x55b6,0x55b8,0x55ba,
+ 0x55bc,0x55bf,0x55c0,0x55c1,0x55c2,0x55c3,0x55c6,0x55c7,0x55c8,0x55ca,
+ 0x55cb,0x55ce,0x55cf,0x55d0,0x55d5,0x55d7,0x55d8,0x55d9,0x55da,0x55db,
+ 0x55de,0x55e0,0x55e2,0x55e7,0x55e9,0x55ed,0x55ee,0x55f0,0x55f1,0x55f4,
+ 0x55f6,0x55f8,0x55f9,0x55fa,0x55fb,0x55fc,0x55ff,0x5602,0x5603,0x5604,
+ 0x5605
+ },
+ { /* ku 07 */
+ 0x5606,0x5607,0x560a,0x560b,0x560d,0x5610,0x5611,0x5612,0x5613,0x5614,
+ 0x5615,0x5616,0x5617,0x5619,0x561a,0x561c,0x561d,0x5620,0x5621,0x5622,
+ 0x5625,0x5626,0x5628,0x5629,0x562a,0x562b,0x562e,0x562f,0x5630,0x5633,
+ 0x5635,0x5637,0x5638,0x563a,0x563c,0x563d,0x563e,0x5640,0x5641,0x5642,
+ 0x5643,0x5644,0x5645,0x5646,0x5647,0x5648,0x5649,0x564a,0x564b,0x564f,
+ 0x5650,0x5651,0x5652,0x5653,0x5655,0x5656,0x565a,0x565b,0x565d,0x565e,
+ 0x565f,0x5660,0x5661,UBOGON,0x5663,0x5665,0x5666,0x5667,0x566d,0x566e,
+ 0x566f,0x5670,0x5672,0x5673,0x5674,0x5675,0x5677,0x5678,0x5679,0x567a,
+ 0x567d,0x567e,0x567f,0x5680,0x5681,0x5682,0x5683,0x5684,0x5687,0x5688,
+ 0x5689,0x568a,0x568b,0x568c,0x568d,0x5690,0x5691,0x5692,0x5694,0x5695,
+ 0x5696,0x5697,0x5698,0x5699,0x569a,0x569b,0x569c,0x569d,0x569e,0x569f,
+ 0x56a0,0x56a1,0x56a2,0x56a4,0x56a5,0x56a6,0x56a7,0x56a8,0x56a9,0x56aa,
+ 0x56ab,0x56ac,0x56ad,0x56ae,0x56b0,0x56b1,0x56b2,0x56b3,0x56b4,0x56b5,
+ 0x56b6,0x56b8,0x56b9,0x56ba,0x56bb,0x56bd,0x56be,0x56bf,0x56c0,0x56c1,
+ 0x56c2,0x56c3,0x56c4,0x56c5,0x56c6,0x56c7,0x56c8,0x56c9,0x56cb,0x56cc,
+ 0x56cd,0x56ce,0x56cf,0x56d0,0x56d1,0x56d2,0x56d3,0x56d5,0x56d6,0x56d8,
+ 0x56d9,0x56dc,0x56e3,0x56e5,0x56e6,0x56e7,0x56e8,0x56e9,0x56ea,0x56ec,
+ 0x56ee,0x56ef,0x56f2,0x56f3,0x56f6,0x56f7,0x56f8,0x56fb,0x56fc,0x5700,
+ 0x5701,0x5702,0x5705,0x5707,0x570b,0x570c,0x570d,0x570e,0x570f,0x5710,
+ 0x5711
+ },
+ { /* ku 08 */
+ 0x5712,0x5713,0x5714,0x5715,0x5716,0x5717,0x5718,0x5719,0x571a,0x571b,
+ 0x571d,0x571e,0x5720,0x5721,0x5722,0x5724,0x5725,0x5726,0x5727,0x572b,
+ 0x5731,0x5732,0x5734,0x5735,0x5736,0x5737,0x5738,0x573c,0x573d,0x573f,
+ 0x5741,0x5743,0x5744,0x5745,0x5746,0x5748,0x5749,0x574b,0x5752,0x5753,
+ 0x5754,0x5755,0x5756,0x5758,0x5759,0x5762,0x5763,0x5765,0x5767,0x576c,
+ 0x576e,0x5770,0x5771,0x5772,0x5774,0x5775,0x5778,0x5779,0x577a,0x577d,
+ 0x577e,0x577f,0x5780,UBOGON,0x5781,0x5787,0x5788,0x5789,0x578a,0x578d,
+ 0x578e,0x578f,0x5790,0x5791,0x5794,0x5795,0x5796,0x5797,0x5798,0x5799,
+ 0x579a,0x579c,0x579d,0x579e,0x579f,0x57a5,0x57a8,0x57aa,0x57ac,0x57af,
+ 0x57b0,0x57b1,0x57b3,0x57b5,0x57b6,0x57b7,0x57b9,0x57ba,0x57bb,0x57bc,
+ 0x57bd,0x57be,0x57bf,0x57c0,0x57c1,0x57c4,0x57c5,0x57c6,0x57c7,0x57c8,
+ 0x57c9,0x57ca,0x57cc,0x57cd,0x57d0,0x57d1,0x57d3,0x57d6,0x57d7,0x57db,
+ 0x57dc,0x57de,0x57e1,0x57e2,0x57e3,0x57e5,0x57e6,0x57e7,0x57e8,0x57e9,
+ 0x57ea,0x57eb,0x57ec,0x57ee,0x57f0,0x57f1,0x57f2,0x57f3,0x57f5,0x57f6,
+ 0x57f7,0x57fb,0x57fc,0x57fe,0x57ff,0x5801,0x5803,0x5804,0x5805,0x5808,
+ 0x5809,0x580a,0x580c,0x580e,0x580f,0x5810,0x5812,0x5813,0x5814,0x5816,
+ 0x5817,0x5818,0x581a,0x581b,0x581c,0x581d,0x581f,0x5822,0x5823,0x5825,
+ 0x5826,0x5827,0x5828,0x5829,0x582b,0x582c,0x582d,0x582e,0x582f,0x5831,
+ 0x5832,0x5833,0x5834,0x5836,0x5837,0x5838,0x5839,0x583a,0x583b,0x583c,
+ 0x583d
+ },
+ { /* ku 09 */
+ 0x583e,0x583f,0x5840,0x5841,0x5842,0x5843,0x5845,0x5846,0x5847,0x5848,
+ 0x5849,0x584a,0x584b,0x584e,0x584f,0x5850,0x5852,0x5853,0x5855,0x5856,
+ 0x5857,0x5859,0x585a,0x585b,0x585c,0x585d,0x585f,0x5860,0x5861,0x5862,
+ 0x5863,0x5864,0x5866,0x5867,0x5868,0x5869,0x586a,0x586d,0x586e,0x586f,
+ 0x5870,0x5871,0x5872,0x5873,0x5874,0x5875,0x5876,0x5877,0x5878,0x5879,
+ 0x587a,0x587b,0x587c,0x587d,0x587f,0x5882,0x5884,0x5886,0x5887,0x5888,
+ 0x588a,0x588b,0x588c,UBOGON,0x588d,0x588e,0x588f,0x5890,0x5891,0x5894,
+ 0x5895,0x5896,0x5897,0x5898,0x589b,0x589c,0x589d,0x58a0,0x58a1,0x58a2,
+ 0x58a3,0x58a4,0x58a5,0x58a6,0x58a7,0x58aa,0x58ab,0x58ac,0x58ad,0x58ae,
+ 0x58af,0x58b0,0x58b1,0x58b2,0x58b3,0x58b4,0x58b5,0x58b6,0x58b7,0x58b8,
+ 0x58b9,0x58ba,0x58bb,0x58bd,0x58be,0x58bf,0x58c0,0x58c2,0x58c3,0x58c4,
+ 0x58c6,0x58c7,0x58c8,0x58c9,0x58ca,0x58cb,0x58cc,0x58cd,0x58ce,0x58cf,
+ 0x58d0,0x58d2,0x58d3,0x58d4,0x58d6,0x58d7,0x58d8,0x58d9,0x58da,0x58db,
+ 0x58dc,0x58dd,0x58de,0x58df,0x58e0,0x58e1,0x58e2,0x58e3,0x58e5,0x58e6,
+ 0x58e7,0x58e8,0x58e9,0x58ea,0x58ed,0x58ef,0x58f1,0x58f2,0x58f4,0x58f5,
+ 0x58f7,0x58f8,0x58fa,0x58fb,0x58fc,0x58fd,0x58fe,0x58ff,0x5900,0x5901,
+ 0x5903,0x5905,0x5906,0x5908,0x5909,0x590a,0x590b,0x590c,0x590e,0x5910,
+ 0x5911,0x5912,0x5913,0x5917,0x5918,0x591b,0x591d,0x591e,0x5920,0x5921,
+ 0x5922,0x5923,0x5926,0x5928,0x592c,0x5930,0x5932,0x5933,0x5935,0x5936,
+ 0x593b
+ },
+ { /* ku 0a */
+ 0x593d,0x593e,0x593f,0x5940,0x5943,0x5945,0x5946,0x594a,0x594c,0x594d,
+ 0x5950,0x5952,0x5953,0x5959,0x595b,0x595c,0x595d,0x595e,0x595f,0x5961,
+ 0x5963,0x5964,0x5966,0x5967,0x5968,0x5969,0x596a,0x596b,0x596c,0x596d,
+ 0x596e,0x596f,0x5970,0x5971,0x5972,0x5975,0x5977,0x597a,0x597b,0x597c,
+ 0x597e,0x597f,0x5980,0x5985,0x5989,0x598b,0x598c,0x598e,0x598f,0x5990,
+ 0x5991,0x5994,0x5995,0x5998,0x599a,0x599b,0x599c,0x599d,0x599f,0x59a0,
+ 0x59a1,0x59a2,0x59a6,UBOGON,0x59a7,0x59ac,0x59ad,0x59b0,0x59b1,0x59b3,
+ 0x59b4,0x59b5,0x59b6,0x59b7,0x59b8,0x59ba,0x59bc,0x59bd,0x59bf,0x59c0,
+ 0x59c1,0x59c2,0x59c3,0x59c4,0x59c5,0x59c7,0x59c8,0x59c9,0x59cc,0x59cd,
+ 0x59ce,0x59cf,0x59d5,0x59d6,0x59d9,0x59db,0x59de,0x59df,0x59e0,0x59e1,
+ 0x59e2,0x59e4,0x59e6,0x59e7,0x59e9,0x59ea,0x59eb,0x59ed,0x59ee,0x59ef,
+ 0x59f0,0x59f1,0x59f2,0x59f3,0x59f4,0x59f5,0x59f6,0x59f7,0x59f8,0x59fa,
+ 0x59fc,0x59fd,0x59fe,0x5a00,0x5a02,0x5a0a,0x5a0b,0x5a0d,0x5a0e,0x5a0f,
+ 0x5a10,0x5a12,0x5a14,0x5a15,0x5a16,0x5a17,0x5a19,0x5a1a,0x5a1b,0x5a1d,
+ 0x5a1e,0x5a21,0x5a22,0x5a24,0x5a26,0x5a27,0x5a28,0x5a2a,0x5a2b,0x5a2c,
+ 0x5a2d,0x5a2e,0x5a2f,0x5a30,0x5a33,0x5a35,0x5a37,0x5a38,0x5a39,0x5a3a,
+ 0x5a3b,0x5a3d,0x5a3e,0x5a3f,0x5a41,0x5a42,0x5a43,0x5a44,0x5a45,0x5a47,
+ 0x5a48,0x5a4b,0x5a4c,0x5a4d,0x5a4e,0x5a4f,0x5a50,0x5a51,0x5a52,0x5a53,
+ 0x5a54,0x5a56,0x5a57,0x5a58,0x5a59,0x5a5b,0x5a5c,0x5a5d,0x5a5e,0x5a5f,
+ 0x5a60
+ },
+ { /* ku 0b */
+ 0x5a61,0x5a63,0x5a64,0x5a65,0x5a66,0x5a68,0x5a69,0x5a6b,0x5a6c,0x5a6d,
+ 0x5a6e,0x5a6f,0x5a70,0x5a71,0x5a72,0x5a73,0x5a78,0x5a79,0x5a7b,0x5a7c,
+ 0x5a7d,0x5a7e,0x5a80,0x5a81,0x5a82,0x5a83,0x5a84,0x5a85,0x5a86,0x5a87,
+ 0x5a88,0x5a89,0x5a8a,0x5a8b,0x5a8c,0x5a8d,0x5a8e,0x5a8f,0x5a90,0x5a91,
+ 0x5a93,0x5a94,0x5a95,0x5a96,0x5a97,0x5a98,0x5a99,0x5a9c,0x5a9d,0x5a9e,
+ 0x5a9f,0x5aa0,0x5aa1,0x5aa2,0x5aa3,0x5aa4,0x5aa5,0x5aa6,0x5aa7,0x5aa8,
+ 0x5aa9,0x5aab,0x5aac,UBOGON,0x5aad,0x5aae,0x5aaf,0x5ab0,0x5ab1,0x5ab4,
+ 0x5ab6,0x5ab7,0x5ab9,0x5aba,0x5abb,0x5abc,0x5abd,0x5abf,0x5ac0,0x5ac3,
+ 0x5ac4,0x5ac5,0x5ac6,0x5ac7,0x5ac8,0x5aca,0x5acb,0x5acd,0x5ace,0x5acf,
+ 0x5ad0,0x5ad1,0x5ad3,0x5ad5,0x5ad7,0x5ad9,0x5ada,0x5adb,0x5add,0x5ade,
+ 0x5adf,0x5ae2,0x5ae4,0x5ae5,0x5ae7,0x5ae8,0x5aea,0x5aec,0x5aed,0x5aee,
+ 0x5aef,0x5af0,0x5af2,0x5af3,0x5af4,0x5af5,0x5af6,0x5af7,0x5af8,0x5af9,
+ 0x5afa,0x5afb,0x5afc,0x5afd,0x5afe,0x5aff,0x5b00,0x5b01,0x5b02,0x5b03,
+ 0x5b04,0x5b05,0x5b06,0x5b07,0x5b08,0x5b0a,0x5b0b,0x5b0c,0x5b0d,0x5b0e,
+ 0x5b0f,0x5b10,0x5b11,0x5b12,0x5b13,0x5b14,0x5b15,0x5b18,0x5b19,0x5b1a,
+ 0x5b1b,0x5b1c,0x5b1d,0x5b1e,0x5b1f,0x5b20,0x5b21,0x5b22,0x5b23,0x5b24,
+ 0x5b25,0x5b26,0x5b27,0x5b28,0x5b29,0x5b2a,0x5b2b,0x5b2c,0x5b2d,0x5b2e,
+ 0x5b2f,0x5b30,0x5b31,0x5b33,0x5b35,0x5b36,0x5b38,0x5b39,0x5b3a,0x5b3b,
+ 0x5b3c,0x5b3d,0x5b3e,0x5b3f,0x5b41,0x5b42,0x5b43,0x5b44,0x5b45,0x5b46,
+ 0x5b47
+ },
+ { /* ku 0c */
+ 0x5b48,0x5b49,0x5b4a,0x5b4b,0x5b4c,0x5b4d,0x5b4e,0x5b4f,0x5b52,0x5b56,
+ 0x5b5e,0x5b60,0x5b61,0x5b67,0x5b68,0x5b6b,0x5b6d,0x5b6e,0x5b6f,0x5b72,
+ 0x5b74,0x5b76,0x5b77,0x5b78,0x5b79,0x5b7b,0x5b7c,0x5b7e,0x5b7f,0x5b82,
+ 0x5b86,0x5b8a,0x5b8d,0x5b8e,0x5b90,0x5b91,0x5b92,0x5b94,0x5b96,0x5b9f,
+ 0x5ba7,0x5ba8,0x5ba9,0x5bac,0x5bad,0x5bae,0x5baf,0x5bb1,0x5bb2,0x5bb7,
+ 0x5bba,0x5bbb,0x5bbc,0x5bc0,0x5bc1,0x5bc3,0x5bc8,0x5bc9,0x5bca,0x5bcb,
+ 0x5bcd,0x5bce,0x5bcf,UBOGON,0x5bd1,0x5bd4,0x5bd5,0x5bd6,0x5bd7,0x5bd8,
+ 0x5bd9,0x5bda,0x5bdb,0x5bdc,0x5be0,0x5be2,0x5be3,0x5be6,0x5be7,0x5be9,
+ 0x5bea,0x5beb,0x5bec,0x5bed,0x5bef,0x5bf1,0x5bf2,0x5bf3,0x5bf4,0x5bf5,
+ 0x5bf6,0x5bf7,0x5bfd,0x5bfe,0x5c00,0x5c02,0x5c03,0x5c05,0x5c07,0x5c08,
+ 0x5c0b,0x5c0c,0x5c0d,0x5c0e,0x5c10,0x5c12,0x5c13,0x5c17,0x5c19,0x5c1b,
+ 0x5c1e,0x5c1f,0x5c20,0x5c21,0x5c23,0x5c26,0x5c28,0x5c29,0x5c2a,0x5c2b,
+ 0x5c2d,0x5c2e,0x5c2f,0x5c30,0x5c32,0x5c33,0x5c35,0x5c36,0x5c37,0x5c43,
+ 0x5c44,0x5c46,0x5c47,0x5c4c,0x5c4d,0x5c52,0x5c53,0x5c54,0x5c56,0x5c57,
+ 0x5c58,0x5c5a,0x5c5b,0x5c5c,0x5c5d,0x5c5f,0x5c62,0x5c64,0x5c67,0x5c68,
+ 0x5c69,0x5c6a,0x5c6b,0x5c6c,0x5c6d,0x5c70,0x5c72,0x5c73,0x5c74,0x5c75,
+ 0x5c76,0x5c77,0x5c78,0x5c7b,0x5c7c,0x5c7d,0x5c7e,0x5c80,0x5c83,0x5c84,
+ 0x5c85,0x5c86,0x5c87,0x5c89,0x5c8a,0x5c8b,0x5c8e,0x5c8f,0x5c92,0x5c93,
+ 0x5c95,0x5c9d,0x5c9e,0x5c9f,0x5ca0,0x5ca1,0x5ca4,0x5ca5,0x5ca6,0x5ca7,
+ 0x5ca8
+ },
+ { /* ku 0d */
+ 0x5caa,0x5cae,0x5caf,0x5cb0,0x5cb2,0x5cb4,0x5cb6,0x5cb9,0x5cba,0x5cbb,
+ 0x5cbc,0x5cbe,0x5cc0,0x5cc2,0x5cc3,0x5cc5,0x5cc6,0x5cc7,0x5cc8,0x5cc9,
+ 0x5cca,0x5ccc,0x5ccd,0x5cce,0x5ccf,0x5cd0,0x5cd1,0x5cd3,0x5cd4,0x5cd5,
+ 0x5cd6,0x5cd7,0x5cd8,0x5cda,0x5cdb,0x5cdc,0x5cdd,0x5cde,0x5cdf,0x5ce0,
+ 0x5ce2,0x5ce3,0x5ce7,0x5ce9,0x5ceb,0x5cec,0x5cee,0x5cef,0x5cf1,0x5cf2,
+ 0x5cf3,0x5cf4,0x5cf5,0x5cf6,0x5cf7,0x5cf8,0x5cf9,0x5cfa,0x5cfc,0x5cfd,
+ 0x5cfe,0x5cff,0x5d00,UBOGON,0x5d01,0x5d04,0x5d05,0x5d08,0x5d09,0x5d0a,
+ 0x5d0b,0x5d0c,0x5d0d,0x5d0f,0x5d10,0x5d11,0x5d12,0x5d13,0x5d15,0x5d17,
+ 0x5d18,0x5d19,0x5d1a,0x5d1c,0x5d1d,0x5d1f,0x5d20,0x5d21,0x5d22,0x5d23,
+ 0x5d25,0x5d28,0x5d2a,0x5d2b,0x5d2c,0x5d2f,0x5d30,0x5d31,0x5d32,0x5d33,
+ 0x5d35,0x5d36,0x5d37,0x5d38,0x5d39,0x5d3a,0x5d3b,0x5d3c,0x5d3f,0x5d40,
+ 0x5d41,0x5d42,0x5d43,0x5d44,0x5d45,0x5d46,0x5d48,0x5d49,0x5d4d,0x5d4e,
+ 0x5d4f,0x5d50,0x5d51,0x5d52,0x5d53,0x5d54,0x5d55,0x5d56,0x5d57,0x5d59,
+ 0x5d5a,0x5d5c,0x5d5e,0x5d5f,0x5d60,0x5d61,0x5d62,0x5d63,0x5d64,0x5d65,
+ 0x5d66,0x5d67,0x5d68,0x5d6a,0x5d6d,0x5d6e,0x5d70,0x5d71,0x5d72,0x5d73,
+ 0x5d75,0x5d76,0x5d77,0x5d78,0x5d79,0x5d7a,0x5d7b,0x5d7c,0x5d7d,0x5d7e,
+ 0x5d7f,0x5d80,0x5d81,0x5d83,0x5d84,0x5d85,0x5d86,0x5d87,0x5d88,0x5d89,
+ 0x5d8a,0x5d8b,0x5d8c,0x5d8d,0x5d8e,0x5d8f,0x5d90,0x5d91,0x5d92,0x5d93,
+ 0x5d94,0x5d95,0x5d96,0x5d97,0x5d98,0x5d9a,0x5d9b,0x5d9c,0x5d9e,0x5d9f,
+ 0x5da0
+ },
+ { /* ku 0e */
+ 0x5da1,0x5da2,0x5da3,0x5da4,0x5da5,0x5da6,0x5da7,0x5da8,0x5da9,0x5daa,
+ 0x5dab,0x5dac,0x5dad,0x5dae,0x5daf,0x5db0,0x5db1,0x5db2,0x5db3,0x5db4,
+ 0x5db5,0x5db6,0x5db8,0x5db9,0x5dba,0x5dbb,0x5dbc,0x5dbd,0x5dbe,0x5dbf,
+ 0x5dc0,0x5dc1,0x5dc2,0x5dc3,0x5dc4,0x5dc6,0x5dc7,0x5dc8,0x5dc9,0x5dca,
+ 0x5dcb,0x5dcc,0x5dce,0x5dcf,0x5dd0,0x5dd1,0x5dd2,0x5dd3,0x5dd4,0x5dd5,
+ 0x5dd6,0x5dd7,0x5dd8,0x5dd9,0x5dda,0x5ddc,0x5ddf,0x5de0,0x5de3,0x5de4,
+ 0x5dea,0x5dec,0x5ded,UBOGON,0x5df0,0x5df5,0x5df6,0x5df8,0x5df9,0x5dfa,
+ 0x5dfb,0x5dfc,0x5dff,0x5e00,0x5e04,0x5e07,0x5e09,0x5e0a,0x5e0b,0x5e0d,
+ 0x5e0e,0x5e12,0x5e13,0x5e17,0x5e1e,0x5e1f,0x5e20,0x5e21,0x5e22,0x5e23,
+ 0x5e24,0x5e25,0x5e28,0x5e29,0x5e2a,0x5e2b,0x5e2c,0x5e2f,0x5e30,0x5e32,
+ 0x5e33,0x5e34,0x5e35,0x5e36,0x5e39,0x5e3a,0x5e3e,0x5e3f,0x5e40,0x5e41,
+ 0x5e43,0x5e46,0x5e47,0x5e48,0x5e49,0x5e4a,0x5e4b,0x5e4d,0x5e4e,0x5e4f,
+ 0x5e50,0x5e51,0x5e52,0x5e53,0x5e56,0x5e57,0x5e58,0x5e59,0x5e5a,0x5e5c,
+ 0x5e5d,0x5e5f,0x5e60,0x5e63,0x5e64,0x5e65,0x5e66,0x5e67,0x5e68,0x5e69,
+ 0x5e6a,0x5e6b,0x5e6c,0x5e6d,0x5e6e,0x5e6f,0x5e70,0x5e71,0x5e75,0x5e77,
+ 0x5e79,0x5e7e,0x5e81,0x5e82,0x5e83,0x5e85,0x5e88,0x5e89,0x5e8c,0x5e8d,
+ 0x5e8e,0x5e92,0x5e98,0x5e9b,0x5e9d,0x5ea1,0x5ea2,0x5ea3,0x5ea4,0x5ea8,
+ 0x5ea9,0x5eaa,0x5eab,0x5eac,0x5eae,0x5eaf,0x5eb0,0x5eb1,0x5eb2,0x5eb4,
+ 0x5eba,0x5ebb,0x5ebc,0x5ebd,0x5ebf,0x5ec0,0x5ec1,0x5ec2,0x5ec3,0x5ec4,
+ 0x5ec5
+ },
+ { /* ku 0f */
+ 0x5ec6,0x5ec7,0x5ec8,0x5ecb,0x5ecc,0x5ecd,0x5ece,0x5ecf,0x5ed0,0x5ed4,
+ 0x5ed5,0x5ed7,0x5ed8,0x5ed9,0x5eda,0x5edc,0x5edd,0x5ede,0x5edf,0x5ee0,
+ 0x5ee1,0x5ee2,0x5ee3,0x5ee4,0x5ee5,0x5ee6,0x5ee7,0x5ee9,0x5eeb,0x5eec,
+ 0x5eed,0x5eee,0x5eef,0x5ef0,0x5ef1,0x5ef2,0x5ef3,0x5ef5,0x5ef8,0x5ef9,
+ 0x5efb,0x5efc,0x5efd,0x5f05,0x5f06,0x5f07,0x5f09,0x5f0c,0x5f0d,0x5f0e,
+ 0x5f10,0x5f12,0x5f14,0x5f16,0x5f19,0x5f1a,0x5f1c,0x5f1d,0x5f1e,0x5f21,
+ 0x5f22,0x5f23,0x5f24,UBOGON,0x5f28,0x5f2b,0x5f2c,0x5f2e,0x5f30,0x5f32,
+ 0x5f33,0x5f34,0x5f35,0x5f36,0x5f37,0x5f38,0x5f3b,0x5f3d,0x5f3e,0x5f3f,
+ 0x5f41,0x5f42,0x5f43,0x5f44,0x5f45,0x5f46,0x5f47,0x5f48,0x5f49,0x5f4a,
+ 0x5f4b,0x5f4c,0x5f4d,0x5f4e,0x5f4f,0x5f51,0x5f54,0x5f59,0x5f5a,0x5f5b,
+ 0x5f5c,0x5f5e,0x5f5f,0x5f60,0x5f63,0x5f65,0x5f67,0x5f68,0x5f6b,0x5f6e,
+ 0x5f6f,0x5f72,0x5f74,0x5f75,0x5f76,0x5f78,0x5f7a,0x5f7d,0x5f7e,0x5f7f,
+ 0x5f83,0x5f86,0x5f8d,0x5f8e,0x5f8f,0x5f91,0x5f93,0x5f94,0x5f96,0x5f9a,
+ 0x5f9b,0x5f9d,0x5f9e,0x5f9f,0x5fa0,0x5fa2,0x5fa3,0x5fa4,0x5fa5,0x5fa6,
+ 0x5fa7,0x5fa9,0x5fab,0x5fac,0x5faf,0x5fb0,0x5fb1,0x5fb2,0x5fb3,0x5fb4,
+ 0x5fb6,0x5fb8,0x5fb9,0x5fba,0x5fbb,0x5fbe,0x5fbf,0x5fc0,0x5fc1,0x5fc2,
+ 0x5fc7,0x5fc8,0x5fca,0x5fcb,0x5fce,0x5fd3,0x5fd4,0x5fd5,0x5fda,0x5fdb,
+ 0x5fdc,0x5fde,0x5fdf,0x5fe2,0x5fe3,0x5fe5,0x5fe6,0x5fe8,0x5fe9,0x5fec,
+ 0x5fef,0x5ff0,0x5ff2,0x5ff3,0x5ff4,0x5ff6,0x5ff7,0x5ff9,0x5ffa,0x5ffc,
+ 0x6007
+ },
+ { /* ku 10 */
+ 0x6008,0x6009,0x600b,0x600c,0x6010,0x6011,0x6013,0x6017,0x6018,0x601a,
+ 0x601e,0x601f,0x6022,0x6023,0x6024,0x602c,0x602d,0x602e,0x6030,0x6031,
+ 0x6032,0x6033,0x6034,0x6036,0x6037,0x6038,0x6039,0x603a,0x603d,0x603e,
+ 0x6040,0x6044,0x6045,0x6046,0x6047,0x6048,0x6049,0x604a,0x604c,0x604e,
+ 0x604f,0x6051,0x6053,0x6054,0x6056,0x6057,0x6058,0x605b,0x605c,0x605e,
+ 0x605f,0x6060,0x6061,0x6065,0x6066,0x606e,0x6071,0x6072,0x6074,0x6075,
+ 0x6077,0x607e,0x6080,UBOGON,0x6081,0x6082,0x6085,0x6086,0x6087,0x6088,
+ 0x608a,0x608b,0x608e,0x608f,0x6090,0x6091,0x6093,0x6095,0x6097,0x6098,
+ 0x6099,0x609c,0x609e,0x60a1,0x60a2,0x60a4,0x60a5,0x60a7,0x60a9,0x60aa,
+ 0x60ae,0x60b0,0x60b3,0x60b5,0x60b6,0x60b7,0x60b9,0x60ba,0x60bd,0x60be,
+ 0x60bf,0x60c0,0x60c1,0x60c2,0x60c3,0x60c4,0x60c7,0x60c8,0x60c9,0x60cc,
+ 0x60cd,0x60ce,0x60cf,0x60d0,0x60d2,0x60d3,0x60d4,0x60d6,0x60d7,0x60d9,
+ 0x60db,0x60de,0x60e1,0x60e2,0x60e3,0x60e4,0x60e5,0x60ea,0x60f1,0x60f2,
+ 0x60f5,0x60f7,0x60f8,0x60fb,0x60fc,0x60fd,0x60fe,0x60ff,0x6102,0x6103,
+ 0x6104,0x6105,0x6107,0x610a,0x610b,0x610c,0x6110,0x6111,0x6112,0x6113,
+ 0x6114,0x6116,0x6117,0x6118,0x6119,0x611b,0x611c,0x611d,0x611e,0x6121,
+ 0x6122,0x6125,0x6128,0x6129,0x612a,0x612c,0x612d,0x612e,0x612f,0x6130,
+ 0x6131,0x6132,0x6133,0x6134,0x6135,0x6136,0x6137,0x6138,0x6139,0x613a,
+ 0x613b,0x613c,0x613d,0x613e,0x6140,0x6141,0x6142,0x6143,0x6144,0x6145,
+ 0x6146
+ },
+ { /* ku 11 */
+ 0x6147,0x6149,0x614b,0x614d,0x614f,0x6150,0x6152,0x6153,0x6154,0x6156,
+ 0x6157,0x6158,0x6159,0x615a,0x615b,0x615c,0x615e,0x615f,0x6160,0x6161,
+ 0x6163,0x6164,0x6165,0x6166,0x6169,0x616a,0x616b,0x616c,0x616d,0x616e,
+ 0x616f,0x6171,0x6172,0x6173,0x6174,0x6176,0x6178,0x6179,0x617a,0x617b,
+ 0x617c,0x617d,0x617e,0x617f,0x6180,0x6181,0x6182,0x6183,0x6184,0x6185,
+ 0x6186,0x6187,0x6188,0x6189,0x618a,0x618c,0x618d,0x618f,0x6190,0x6191,
+ 0x6192,0x6193,0x6195,UBOGON,0x6196,0x6197,0x6198,0x6199,0x619a,0x619b,
+ 0x619c,0x619e,0x619f,0x61a0,0x61a1,0x61a2,0x61a3,0x61a4,0x61a5,0x61a6,
+ 0x61aa,0x61ab,0x61ad,0x61ae,0x61af,0x61b0,0x61b1,0x61b2,0x61b3,0x61b4,
+ 0x61b5,0x61b6,0x61b8,0x61b9,0x61ba,0x61bb,0x61bc,0x61bd,0x61bf,0x61c0,
+ 0x61c1,0x61c3,0x61c4,0x61c5,0x61c6,0x61c7,0x61c9,0x61cc,0x61cd,0x61ce,
+ 0x61cf,0x61d0,0x61d3,0x61d5,0x61d6,0x61d7,0x61d8,0x61d9,0x61da,0x61db,
+ 0x61dc,0x61dd,0x61de,0x61df,0x61e0,0x61e1,0x61e2,0x61e3,0x61e4,0x61e5,
+ 0x61e7,0x61e8,0x61e9,0x61ea,0x61eb,0x61ec,0x61ed,0x61ee,0x61ef,0x61f0,
+ 0x61f1,0x61f2,0x61f3,0x61f4,0x61f6,0x61f7,0x61f8,0x61f9,0x61fa,0x61fb,
+ 0x61fc,0x61fd,0x61fe,0x6200,0x6201,0x6202,0x6203,0x6204,0x6205,0x6207,
+ 0x6209,0x6213,0x6214,0x6219,0x621c,0x621d,0x621e,0x6220,0x6223,0x6226,
+ 0x6227,0x6228,0x6229,0x622b,0x622d,0x622f,0x6230,0x6231,0x6232,0x6235,
+ 0x6236,0x6238,0x6239,0x623a,0x623b,0x623c,0x6242,0x6244,0x6245,0x6246,
+ 0x624a
+ },
+ { /* ku 12 */
+ 0x624f,0x6250,0x6255,0x6256,0x6257,0x6259,0x625a,0x625c,0x625d,0x625e,
+ 0x625f,0x6260,0x6261,0x6262,0x6264,0x6265,0x6268,0x6271,0x6272,0x6274,
+ 0x6275,0x6277,0x6278,0x627a,0x627b,0x627d,0x6281,0x6282,0x6283,0x6285,
+ 0x6286,0x6287,0x6288,0x628b,0x628c,0x628d,0x628e,0x628f,0x6290,0x6294,
+ 0x6299,0x629c,0x629d,0x629e,0x62a3,0x62a6,0x62a7,0x62a9,0x62aa,0x62ad,
+ 0x62ae,0x62af,0x62b0,0x62b2,0x62b3,0x62b4,0x62b6,0x62b7,0x62b8,0x62ba,
+ 0x62be,0x62c0,0x62c1,UBOGON,0x62c3,0x62cb,0x62cf,0x62d1,0x62d5,0x62dd,
+ 0x62de,0x62e0,0x62e1,0x62e4,0x62ea,0x62eb,0x62f0,0x62f2,0x62f5,0x62f8,
+ 0x62f9,0x62fa,0x62fb,0x6300,0x6303,0x6304,0x6305,0x6306,0x630a,0x630b,
+ 0x630c,0x630d,0x630f,0x6310,0x6312,0x6313,0x6314,0x6315,0x6317,0x6318,
+ 0x6319,0x631c,0x6326,0x6327,0x6329,0x632c,0x632d,0x632e,0x6330,0x6331,
+ 0x6333,0x6334,0x6335,0x6336,0x6337,0x6338,0x633b,0x633c,0x633e,0x633f,
+ 0x6340,0x6341,0x6344,0x6347,0x6348,0x634a,0x6351,0x6352,0x6353,0x6354,
+ 0x6356,0x6357,0x6358,0x6359,0x635a,0x635b,0x635c,0x635d,0x6360,0x6364,
+ 0x6365,0x6366,0x6368,0x636a,0x636b,0x636c,0x636f,0x6370,0x6372,0x6373,
+ 0x6374,0x6375,0x6378,0x6379,0x637c,0x637d,0x637e,0x637f,0x6381,0x6383,
+ 0x6384,0x6385,0x6386,0x638b,0x638d,0x6391,0x6393,0x6394,0x6395,0x6397,
+ 0x6399,0x639a,0x639b,0x639c,0x639d,0x639e,0x639f,0x63a1,0x63a4,0x63a6,
+ 0x63ab,0x63af,0x63b1,0x63b2,0x63b5,0x63b6,0x63b9,0x63bb,0x63bd,0x63bf,
+ 0x63c0
+ },
+ { /* ku 13 */
+ 0x63c1,0x63c2,0x63c3,0x63c5,0x63c7,0x63c8,0x63ca,0x63cb,0x63cc,0x63d1,
+ 0x63d3,0x63d4,0x63d5,0x63d7,0x63d8,0x63d9,0x63da,0x63db,0x63dc,0x63dd,
+ 0x63df,0x63e2,0x63e4,0x63e5,0x63e6,0x63e7,0x63e8,0x63eb,0x63ec,0x63ee,
+ 0x63ef,0x63f0,0x63f1,0x63f3,0x63f5,0x63f7,0x63f9,0x63fa,0x63fb,0x63fc,
+ 0x63fe,0x6403,0x6404,0x6406,0x6407,0x6408,0x6409,0x640a,0x640d,0x640e,
+ 0x6411,0x6412,0x6415,0x6416,0x6417,0x6418,0x6419,0x641a,0x641d,0x641f,
+ 0x6422,0x6423,0x6424,UBOGON,0x6425,0x6427,0x6428,0x6429,0x642b,0x642e,
+ 0x642f,0x6430,0x6431,0x6432,0x6433,0x6435,0x6436,0x6437,0x6438,0x6439,
+ 0x643b,0x643c,0x643e,0x6440,0x6442,0x6443,0x6449,0x644b,0x644c,0x644d,
+ 0x644e,0x644f,0x6450,0x6451,0x6453,0x6455,0x6456,0x6457,0x6459,0x645a,
+ 0x645b,0x645c,0x645d,0x645f,0x6460,0x6461,0x6462,0x6463,0x6464,0x6465,
+ 0x6466,0x6468,0x646a,0x646b,0x646c,0x646e,0x646f,0x6470,0x6471,0x6472,
+ 0x6473,0x6474,0x6475,0x6476,0x6477,0x647b,0x647c,0x647d,0x647e,0x647f,
+ 0x6480,0x6481,0x6483,0x6486,0x6488,0x6489,0x648a,0x648b,0x648c,0x648d,
+ 0x648e,0x648f,0x6490,0x6493,0x6494,0x6497,0x6498,0x649a,0x649b,0x649c,
+ 0x649d,0x649f,0x64a0,0x64a1,0x64a2,0x64a3,0x64a5,0x64a6,0x64a7,0x64a8,
+ 0x64aa,0x64ab,0x64af,0x64b1,0x64b2,0x64b3,0x64b4,0x64b6,0x64b9,0x64bb,
+ 0x64bd,0x64be,0x64bf,0x64c1,0x64c3,0x64c4,0x64c6,0x64c7,0x64c8,0x64c9,
+ 0x64ca,0x64cb,0x64cc,0x64cf,0x64d1,0x64d3,0x64d4,0x64d5,0x64d6,0x64d9,
+ 0x64da
+ },
+ { /* ku 14 */
+ 0x64db,0x64dc,0x64dd,0x64df,0x64e0,0x64e1,0x64e3,0x64e5,0x64e7,0x64e8,
+ 0x64e9,0x64ea,0x64eb,0x64ec,0x64ed,0x64ee,0x64ef,0x64f0,0x64f1,0x64f2,
+ 0x64f3,0x64f4,0x64f5,0x64f6,0x64f7,0x64f8,0x64f9,0x64fa,0x64fb,0x64fc,
+ 0x64fd,0x64fe,0x64ff,0x6501,0x6502,0x6503,0x6504,0x6505,0x6506,0x6507,
+ 0x6508,0x650a,0x650b,0x650c,0x650d,0x650e,0x650f,0x6510,0x6511,0x6513,
+ 0x6514,0x6515,0x6516,0x6517,0x6519,0x651a,0x651b,0x651c,0x651d,0x651e,
+ 0x651f,0x6520,0x6521,UBOGON,0x6522,0x6523,0x6524,0x6526,0x6527,0x6528,
+ 0x6529,0x652a,0x652c,0x652d,0x6530,0x6531,0x6532,0x6533,0x6537,0x653a,
+ 0x653c,0x653d,0x6540,0x6541,0x6542,0x6543,0x6544,0x6546,0x6547,0x654a,
+ 0x654b,0x654d,0x654e,0x6550,0x6552,0x6553,0x6554,0x6557,0x6558,0x655a,
+ 0x655c,0x655f,0x6560,0x6561,0x6564,0x6565,0x6567,0x6568,0x6569,0x656a,
+ 0x656d,0x656e,0x656f,0x6571,0x6573,0x6575,0x6576,0x6578,0x6579,0x657a,
+ 0x657b,0x657c,0x657d,0x657e,0x657f,0x6580,0x6581,0x6582,0x6583,0x6584,
+ 0x6585,0x6586,0x6588,0x6589,0x658a,0x658d,0x658e,0x658f,0x6592,0x6594,
+ 0x6595,0x6596,0x6598,0x659a,0x659d,0x659e,0x65a0,0x65a2,0x65a3,0x65a6,
+ 0x65a8,0x65aa,0x65ac,0x65ae,0x65b1,0x65b2,0x65b3,0x65b4,0x65b5,0x65b6,
+ 0x65b7,0x65b8,0x65ba,0x65bb,0x65be,0x65bf,0x65c0,0x65c2,0x65c7,0x65c8,
+ 0x65c9,0x65ca,0x65cd,0x65d0,0x65d1,0x65d3,0x65d4,0x65d5,0x65d8,0x65d9,
+ 0x65da,0x65db,0x65dc,0x65dd,0x65de,0x65df,0x65e1,0x65e3,0x65e4,0x65ea,
+ 0x65eb
+ },
+ { /* ku 15 */
+ 0x65f2,0x65f3,0x65f4,0x65f5,0x65f8,0x65f9,0x65fb,0x65fc,0x65fd,0x65fe,
+ 0x65ff,0x6601,0x6604,0x6605,0x6607,0x6608,0x6609,0x660b,0x660d,0x6610,
+ 0x6611,0x6612,0x6616,0x6617,0x6618,0x661a,0x661b,0x661c,0x661e,0x6621,
+ 0x6622,0x6623,0x6624,0x6626,0x6629,0x662a,0x662b,0x662c,0x662e,0x6630,
+ 0x6632,0x6633,0x6637,0x6638,0x6639,0x663a,0x663b,0x663d,0x663f,0x6640,
+ 0x6642,0x6644,0x6645,0x6646,0x6647,0x6648,0x6649,0x664a,0x664d,0x664e,
+ 0x6650,0x6651,0x6658,UBOGON,0x6659,0x665b,0x665c,0x665d,0x665e,0x6660,
+ 0x6662,0x6663,0x6665,0x6667,0x6669,0x666a,0x666b,0x666c,0x666d,0x6671,
+ 0x6672,0x6673,0x6675,0x6678,0x6679,0x667b,0x667c,0x667d,0x667f,0x6680,
+ 0x6681,0x6683,0x6685,0x6686,0x6688,0x6689,0x668a,0x668b,0x668d,0x668e,
+ 0x668f,0x6690,0x6692,0x6693,0x6694,0x6695,0x6698,0x6699,0x669a,0x669b,
+ 0x669c,0x669e,0x669f,0x66a0,0x66a1,0x66a2,0x66a3,0x66a4,0x66a5,0x66a6,
+ 0x66a9,0x66aa,0x66ab,0x66ac,0x66ad,0x66af,0x66b0,0x66b1,0x66b2,0x66b3,
+ 0x66b5,0x66b6,0x66b7,0x66b8,0x66ba,0x66bb,0x66bc,0x66bd,0x66bf,0x66c0,
+ 0x66c1,0x66c2,0x66c3,0x66c4,0x66c5,0x66c6,0x66c7,0x66c8,0x66c9,0x66ca,
+ 0x66cb,0x66cc,0x66cd,0x66ce,0x66cf,0x66d0,0x66d1,0x66d2,0x66d3,0x66d4,
+ 0x66d5,0x66d6,0x66d7,0x66d8,0x66da,0x66de,0x66df,0x66e0,0x66e1,0x66e2,
+ 0x66e3,0x66e4,0x66e5,0x66e7,0x66e8,0x66ea,0x66eb,0x66ec,0x66ed,0x66ee,
+ 0x66ef,0x66f1,0x66f5,0x66f6,0x66f8,0x66fa,0x66fb,0x66fd,0x6701,0x6702,
+ 0x6703
+ },
+ { /* ku 16 */
+ 0x6704,0x6705,0x6706,0x6707,0x670c,0x670e,0x670f,0x6711,0x6712,0x6713,
+ 0x6716,0x6718,0x6719,0x671a,0x671c,0x671e,0x6720,0x6721,0x6722,0x6723,
+ 0x6724,0x6725,0x6727,0x6729,0x672e,0x6730,0x6732,0x6733,0x6736,0x6737,
+ 0x6738,0x6739,0x673b,0x673c,0x673e,0x673f,0x6741,0x6744,0x6745,0x6747,
+ 0x674a,0x674b,0x674d,0x6752,0x6754,0x6755,0x6757,0x6758,0x6759,0x675a,
+ 0x675b,0x675d,0x6762,0x6763,0x6764,0x6766,0x6767,0x676b,0x676c,0x676e,
+ 0x6771,0x6774,0x6776,UBOGON,0x6778,0x6779,0x677a,0x677b,0x677d,0x6780,
+ 0x6782,0x6783,0x6785,0x6786,0x6788,0x678a,0x678c,0x678d,0x678e,0x678f,
+ 0x6791,0x6792,0x6793,0x6794,0x6796,0x6799,0x679b,0x679f,0x67a0,0x67a1,
+ 0x67a4,0x67a6,0x67a9,0x67ac,0x67ae,0x67b1,0x67b2,0x67b4,0x67b9,0x67ba,
+ 0x67bb,0x67bc,0x67bd,0x67be,0x67bf,0x67c0,0x67c2,0x67c5,0x67c6,0x67c7,
+ 0x67c8,0x67c9,0x67ca,0x67cb,0x67cc,0x67cd,0x67ce,0x67d5,0x67d6,0x67d7,
+ 0x67db,0x67df,0x67e1,0x67e3,0x67e4,0x67e6,0x67e7,0x67e8,0x67ea,0x67eb,
+ 0x67ed,0x67ee,0x67f2,0x67f5,0x67f6,0x67f7,0x67f8,0x67f9,0x67fa,0x67fb,
+ 0x67fc,0x67fe,0x6801,0x6802,0x6803,0x6804,0x6806,0x680d,0x6810,0x6812,
+ 0x6814,0x6815,0x6818,0x6819,0x681a,0x681b,0x681c,0x681e,0x681f,0x6820,
+ 0x6822,0x6823,0x6824,0x6825,0x6826,0x6827,0x6828,0x682b,0x682c,0x682d,
+ 0x682e,0x682f,0x6830,0x6831,0x6834,0x6835,0x6836,0x683a,0x683b,0x683f,
+ 0x6847,0x684b,0x684d,0x684f,0x6852,0x6856,0x6857,0x6858,0x6859,0x685a,
+ 0x685b
+ },
+ { /* ku 17 */
+ 0x685c,0x685d,0x685e,0x685f,0x686a,0x686c,0x686d,0x686e,0x686f,0x6870,
+ 0x6871,0x6872,0x6873,0x6875,0x6878,0x6879,0x687a,0x687b,0x687c,0x687d,
+ 0x687e,0x687f,0x6880,0x6882,0x6884,0x6887,0x6888,0x6889,0x688a,0x688b,
+ 0x688c,0x688d,0x688e,0x6890,0x6891,0x6892,0x6894,0x6895,0x6896,0x6898,
+ 0x6899,0x689a,0x689b,0x689c,0x689d,0x689e,0x689f,0x68a0,0x68a1,0x68a3,
+ 0x68a4,0x68a5,0x68a9,0x68aa,0x68ab,0x68ac,0x68ae,0x68b1,0x68b2,0x68b4,
+ 0x68b6,0x68b7,0x68b8,UBOGON,0x68b9,0x68ba,0x68bb,0x68bc,0x68bd,0x68be,
+ 0x68bf,0x68c1,0x68c3,0x68c4,0x68c5,0x68c6,0x68c7,0x68c8,0x68ca,0x68cc,
+ 0x68ce,0x68cf,0x68d0,0x68d1,0x68d3,0x68d4,0x68d6,0x68d7,0x68d9,0x68db,
+ 0x68dc,0x68dd,0x68de,0x68df,0x68e1,0x68e2,0x68e4,0x68e5,0x68e6,0x68e7,
+ 0x68e8,0x68e9,0x68ea,0x68eb,0x68ec,0x68ed,0x68ef,0x68f2,0x68f3,0x68f4,
+ 0x68f6,0x68f7,0x68f8,0x68fb,0x68fd,0x68fe,0x68ff,0x6900,0x6902,0x6903,
+ 0x6904,0x6906,0x6907,0x6908,0x6909,0x690a,0x690c,0x690f,0x6911,0x6913,
+ 0x6914,0x6915,0x6916,0x6917,0x6918,0x6919,0x691a,0x691b,0x691c,0x691d,
+ 0x691e,0x6921,0x6922,0x6923,0x6925,0x6926,0x6927,0x6928,0x6929,0x692a,
+ 0x692b,0x692c,0x692e,0x692f,0x6931,0x6932,0x6933,0x6935,0x6936,0x6937,
+ 0x6938,0x693a,0x693b,0x693c,0x693e,0x6940,0x6941,0x6943,0x6944,0x6945,
+ 0x6946,0x6947,0x6948,0x6949,0x694a,0x694b,0x694c,0x694d,0x694e,0x694f,
+ 0x6950,0x6951,0x6952,0x6953,0x6955,0x6956,0x6958,0x6959,0x695b,0x695c,
+ 0x695f
+ },
+ { /* ku 18 */
+ 0x6961,0x6962,0x6964,0x6965,0x6967,0x6968,0x6969,0x696a,0x696c,0x696d,
+ 0x696f,0x6970,0x6972,0x6973,0x6974,0x6975,0x6976,0x697a,0x697b,0x697d,
+ 0x697e,0x697f,0x6981,0x6983,0x6985,0x698a,0x698b,0x698c,0x698e,0x698f,
+ 0x6990,0x6991,0x6992,0x6993,0x6996,0x6997,0x6999,0x699a,0x699d,0x699e,
+ 0x699f,0x69a0,0x69a1,0x69a2,0x69a3,0x69a4,0x69a5,0x69a6,0x69a9,0x69aa,
+ 0x69ac,0x69ae,0x69af,0x69b0,0x69b2,0x69b3,0x69b5,0x69b6,0x69b8,0x69b9,
+ 0x69ba,0x69bc,0x69bd,UBOGON,0x69be,0x69bf,0x69c0,0x69c2,0x69c3,0x69c4,
+ 0x69c5,0x69c6,0x69c7,0x69c8,0x69c9,0x69cb,0x69cd,0x69cf,0x69d1,0x69d2,
+ 0x69d3,0x69d5,0x69d6,0x69d7,0x69d8,0x69d9,0x69da,0x69dc,0x69dd,0x69de,
+ 0x69e1,0x69e2,0x69e3,0x69e4,0x69e5,0x69e6,0x69e7,0x69e8,0x69e9,0x69ea,
+ 0x69eb,0x69ec,0x69ee,0x69ef,0x69f0,0x69f1,0x69f3,0x69f4,0x69f5,0x69f6,
+ 0x69f7,0x69f8,0x69f9,0x69fa,0x69fb,0x69fc,0x69fe,0x6a00,0x6a01,0x6a02,
+ 0x6a03,0x6a04,0x6a05,0x6a06,0x6a07,0x6a08,0x6a09,0x6a0b,0x6a0c,0x6a0d,
+ 0x6a0e,0x6a0f,0x6a10,0x6a11,0x6a12,0x6a13,0x6a14,0x6a15,0x6a16,0x6a19,
+ 0x6a1a,0x6a1b,0x6a1c,0x6a1d,0x6a1e,0x6a20,0x6a22,0x6a23,0x6a24,0x6a25,
+ 0x6a26,0x6a27,0x6a29,0x6a2b,0x6a2c,0x6a2d,0x6a2e,0x6a30,0x6a32,0x6a33,
+ 0x6a34,0x6a36,0x6a37,0x6a38,0x6a39,0x6a3a,0x6a3b,0x6a3c,0x6a3f,0x6a40,
+ 0x6a41,0x6a42,0x6a43,0x6a45,0x6a46,0x6a48,0x6a49,0x6a4a,0x6a4b,0x6a4c,
+ 0x6a4d,0x6a4e,0x6a4f,0x6a51,0x6a52,0x6a53,0x6a54,0x6a55,0x6a56,0x6a57,
+ 0x6a5a
+ },
+ { /* ku 19 */
+ 0x6a5c,0x6a5d,0x6a5e,0x6a5f,0x6a60,0x6a62,0x6a63,0x6a64,0x6a66,0x6a67,
+ 0x6a68,0x6a69,0x6a6a,0x6a6b,0x6a6c,0x6a6d,0x6a6e,0x6a6f,0x6a70,0x6a72,
+ 0x6a73,0x6a74,0x6a75,0x6a76,0x6a77,0x6a78,0x6a7a,0x6a7b,0x6a7d,0x6a7e,
+ 0x6a7f,0x6a81,0x6a82,0x6a83,0x6a85,0x6a86,0x6a87,0x6a88,0x6a89,0x6a8a,
+ 0x6a8b,0x6a8c,0x6a8d,0x6a8f,0x6a92,0x6a93,0x6a94,0x6a95,0x6a96,0x6a98,
+ 0x6a99,0x6a9a,0x6a9b,0x6a9c,0x6a9d,0x6a9e,0x6a9f,0x6aa1,0x6aa2,0x6aa3,
+ 0x6aa4,0x6aa5,0x6aa6,UBOGON,0x6aa7,0x6aa8,0x6aaa,0x6aad,0x6aae,0x6aaf,
+ 0x6ab0,0x6ab1,0x6ab2,0x6ab3,0x6ab4,0x6ab5,0x6ab6,0x6ab7,0x6ab8,0x6ab9,
+ 0x6aba,0x6abb,0x6abc,0x6abd,0x6abe,0x6abf,0x6ac0,0x6ac1,0x6ac2,0x6ac3,
+ 0x6ac4,0x6ac5,0x6ac6,0x6ac7,0x6ac8,0x6ac9,0x6aca,0x6acb,0x6acc,0x6acd,
+ 0x6ace,0x6acf,0x6ad0,0x6ad1,0x6ad2,0x6ad3,0x6ad4,0x6ad5,0x6ad6,0x6ad7,
+ 0x6ad8,0x6ad9,0x6ada,0x6adb,0x6adc,0x6add,0x6ade,0x6adf,0x6ae0,0x6ae1,
+ 0x6ae2,0x6ae3,0x6ae4,0x6ae5,0x6ae6,0x6ae7,0x6ae8,0x6ae9,0x6aea,0x6aeb,
+ 0x6aec,0x6aed,0x6aee,0x6aef,0x6af0,0x6af1,0x6af2,0x6af3,0x6af4,0x6af5,
+ 0x6af6,0x6af7,0x6af8,0x6af9,0x6afa,0x6afb,0x6afc,0x6afd,0x6afe,0x6aff,
+ 0x6b00,0x6b01,0x6b02,0x6b03,0x6b04,0x6b05,0x6b06,0x6b07,0x6b08,0x6b09,
+ 0x6b0a,0x6b0b,0x6b0c,0x6b0d,0x6b0e,0x6b0f,0x6b10,0x6b11,0x6b12,0x6b13,
+ 0x6b14,0x6b15,0x6b16,0x6b17,0x6b18,0x6b19,0x6b1a,0x6b1b,0x6b1c,0x6b1d,
+ 0x6b1e,0x6b1f,0x6b25,0x6b26,0x6b28,0x6b29,0x6b2a,0x6b2b,0x6b2c,0x6b2d,
+ 0x6b2e
+ },
+ { /* ku 1a */
+ 0x6b2f,0x6b30,0x6b31,0x6b33,0x6b34,0x6b35,0x6b36,0x6b38,0x6b3b,0x6b3c,
+ 0x6b3d,0x6b3f,0x6b40,0x6b41,0x6b42,0x6b44,0x6b45,0x6b48,0x6b4a,0x6b4b,
+ 0x6b4d,0x6b4e,0x6b4f,0x6b50,0x6b51,0x6b52,0x6b53,0x6b54,0x6b55,0x6b56,
+ 0x6b57,0x6b58,0x6b5a,0x6b5b,0x6b5c,0x6b5d,0x6b5e,0x6b5f,0x6b60,0x6b61,
+ 0x6b68,0x6b69,0x6b6b,0x6b6c,0x6b6d,0x6b6e,0x6b6f,0x6b70,0x6b71,0x6b72,
+ 0x6b73,0x6b74,0x6b75,0x6b76,0x6b77,0x6b78,0x6b7a,0x6b7d,0x6b7e,0x6b7f,
+ 0x6b80,0x6b85,0x6b88,UBOGON,0x6b8c,0x6b8e,0x6b8f,0x6b90,0x6b91,0x6b94,
+ 0x6b95,0x6b97,0x6b98,0x6b99,0x6b9c,0x6b9d,0x6b9e,0x6b9f,0x6ba0,0x6ba2,
+ 0x6ba3,0x6ba4,0x6ba5,0x6ba6,0x6ba7,0x6ba8,0x6ba9,0x6bab,0x6bac,0x6bad,
+ 0x6bae,0x6baf,0x6bb0,0x6bb1,0x6bb2,0x6bb6,0x6bb8,0x6bb9,0x6bba,0x6bbb,
+ 0x6bbc,0x6bbd,0x6bbe,0x6bc0,0x6bc3,0x6bc4,0x6bc6,0x6bc7,0x6bc8,0x6bc9,
+ 0x6bca,0x6bcc,0x6bce,0x6bd0,0x6bd1,0x6bd8,0x6bda,0x6bdc,0x6bdd,0x6bde,
+ 0x6bdf,0x6be0,0x6be2,0x6be3,0x6be4,0x6be5,0x6be6,0x6be7,0x6be8,0x6be9,
+ 0x6bec,0x6bed,0x6bee,0x6bf0,0x6bf1,0x6bf2,0x6bf4,0x6bf6,0x6bf7,0x6bf8,
+ 0x6bfa,0x6bfb,0x6bfc,0x6bfe,0x6bff,0x6c00,0x6c01,0x6c02,0x6c03,0x6c04,
+ 0x6c08,0x6c09,0x6c0a,0x6c0b,0x6c0c,0x6c0e,0x6c12,0x6c17,0x6c1c,0x6c1d,
+ 0x6c1e,0x6c20,0x6c23,0x6c25,0x6c2b,0x6c2c,0x6c2d,0x6c31,0x6c33,0x6c36,
+ 0x6c37,0x6c39,0x6c3a,0x6c3b,0x6c3c,0x6c3e,0x6c3f,0x6c43,0x6c44,0x6c45,
+ 0x6c48,0x6c4b,0x6c4c,0x6c4d,0x6c4e,0x6c4f,0x6c51,0x6c52,0x6c53,0x6c56,
+ 0x6c58
+ },
+ { /* ku 1b */
+ 0x6c59,0x6c5a,0x6c62,0x6c63,0x6c65,0x6c66,0x6c67,0x6c6b,0x6c6c,0x6c6d,
+ 0x6c6e,0x6c6f,0x6c71,0x6c73,0x6c75,0x6c77,0x6c78,0x6c7a,0x6c7b,0x6c7c,
+ 0x6c7f,0x6c80,0x6c84,0x6c87,0x6c8a,0x6c8b,0x6c8d,0x6c8e,0x6c91,0x6c92,
+ 0x6c95,0x6c96,0x6c97,0x6c98,0x6c9a,0x6c9c,0x6c9d,0x6c9e,0x6ca0,0x6ca2,
+ 0x6ca8,0x6cac,0x6caf,0x6cb0,0x6cb4,0x6cb5,0x6cb6,0x6cb7,0x6cba,0x6cc0,
+ 0x6cc1,0x6cc2,0x6cc3,0x6cc6,0x6cc7,0x6cc8,0x6ccb,0x6ccd,0x6cce,0x6ccf,
+ 0x6cd1,0x6cd2,0x6cd8,UBOGON,0x6cd9,0x6cda,0x6cdc,0x6cdd,0x6cdf,0x6ce4,
+ 0x6ce6,0x6ce7,0x6ce9,0x6cec,0x6ced,0x6cf2,0x6cf4,0x6cf9,0x6cff,0x6d00,
+ 0x6d02,0x6d03,0x6d05,0x6d06,0x6d08,0x6d09,0x6d0a,0x6d0d,0x6d0f,0x6d10,
+ 0x6d11,0x6d13,0x6d14,0x6d15,0x6d16,0x6d18,0x6d1c,0x6d1d,0x6d1f,0x6d20,
+ 0x6d21,0x6d22,0x6d23,0x6d24,0x6d26,0x6d28,0x6d29,0x6d2c,0x6d2d,0x6d2f,
+ 0x6d30,0x6d34,0x6d36,0x6d37,0x6d38,0x6d3a,0x6d3f,0x6d40,0x6d42,0x6d44,
+ 0x6d49,0x6d4c,0x6d50,0x6d55,0x6d56,0x6d57,0x6d58,0x6d5b,0x6d5d,0x6d5f,
+ 0x6d61,0x6d62,0x6d64,0x6d65,0x6d67,0x6d68,0x6d6b,0x6d6c,0x6d6d,0x6d70,
+ 0x6d71,0x6d72,0x6d73,0x6d75,0x6d76,0x6d79,0x6d7a,0x6d7b,0x6d7d,0x6d7e,
+ 0x6d7f,0x6d80,0x6d81,0x6d83,0x6d84,0x6d86,0x6d87,0x6d8a,0x6d8b,0x6d8d,
+ 0x6d8f,0x6d90,0x6d92,0x6d96,0x6d97,0x6d98,0x6d99,0x6d9a,0x6d9c,0x6da2,
+ 0x6da5,0x6dac,0x6dad,0x6db0,0x6db1,0x6db3,0x6db4,0x6db6,0x6db7,0x6db9,
+ 0x6dba,0x6dbb,0x6dbc,0x6dbd,0x6dbe,0x6dc1,0x6dc2,0x6dc3,0x6dc8,0x6dc9,
+ 0x6dca
+ },
+ { /* ku 1c */
+ 0x6dcd,0x6dce,0x6dcf,0x6dd0,0x6dd2,0x6dd3,0x6dd4,0x6dd5,0x6dd7,0x6dda,
+ 0x6ddb,0x6ddc,0x6ddf,0x6de2,0x6de3,0x6de5,0x6de7,0x6de8,0x6de9,0x6dea,
+ 0x6ded,0x6def,0x6df0,0x6df2,0x6df4,0x6df5,0x6df6,0x6df8,0x6dfa,0x6dfd,
+ 0x6dfe,0x6dff,0x6e00,0x6e01,0x6e02,0x6e03,0x6e04,0x6e06,0x6e07,0x6e08,
+ 0x6e09,0x6e0b,0x6e0f,0x6e12,0x6e13,0x6e15,0x6e18,0x6e19,0x6e1b,0x6e1c,
+ 0x6e1e,0x6e1f,0x6e22,0x6e26,0x6e27,0x6e28,0x6e2a,0x6e2c,0x6e2e,0x6e30,
+ 0x6e31,0x6e33,0x6e35,UBOGON,0x6e36,0x6e37,0x6e39,0x6e3b,0x6e3c,0x6e3d,
+ 0x6e3e,0x6e3f,0x6e40,0x6e41,0x6e42,0x6e45,0x6e46,0x6e47,0x6e48,0x6e49,
+ 0x6e4a,0x6e4b,0x6e4c,0x6e4f,0x6e50,0x6e51,0x6e52,0x6e55,0x6e57,0x6e59,
+ 0x6e5a,0x6e5c,0x6e5d,0x6e5e,0x6e60,0x6e61,0x6e62,0x6e63,0x6e64,0x6e65,
+ 0x6e66,0x6e67,0x6e68,0x6e69,0x6e6a,0x6e6c,0x6e6d,0x6e6f,0x6e70,0x6e71,
+ 0x6e72,0x6e73,0x6e74,0x6e75,0x6e76,0x6e77,0x6e78,0x6e79,0x6e7a,0x6e7b,
+ 0x6e7c,0x6e7d,0x6e80,0x6e81,0x6e82,0x6e84,0x6e87,0x6e88,0x6e8a,0x6e8b,
+ 0x6e8c,0x6e8d,0x6e8e,0x6e91,0x6e92,0x6e93,0x6e94,0x6e95,0x6e96,0x6e97,
+ 0x6e99,0x6e9a,0x6e9b,0x6e9d,0x6e9e,0x6ea0,0x6ea1,0x6ea3,0x6ea4,0x6ea6,
+ 0x6ea8,0x6ea9,0x6eab,0x6eac,0x6ead,0x6eae,0x6eb0,0x6eb3,0x6eb5,0x6eb8,
+ 0x6eb9,0x6ebc,0x6ebe,0x6ebf,0x6ec0,0x6ec3,0x6ec4,0x6ec5,0x6ec6,0x6ec8,
+ 0x6ec9,0x6eca,0x6ecc,0x6ecd,0x6ece,0x6ed0,0x6ed2,0x6ed6,0x6ed8,0x6ed9,
+ 0x6edb,0x6edc,0x6edd,0x6ee3,0x6ee7,0x6eea,0x6eeb,0x6eec,0x6eed,0x6eee,
+ 0x6eef
+ },
+ { /* ku 1d */
+ 0x6ef0,0x6ef1,0x6ef2,0x6ef3,0x6ef5,0x6ef6,0x6ef7,0x6ef8,0x6efa,0x6efb,
+ 0x6efc,0x6efd,0x6efe,0x6eff,0x6f00,0x6f01,0x6f03,0x6f04,0x6f05,0x6f07,
+ 0x6f08,0x6f0a,0x6f0b,0x6f0c,0x6f0d,0x6f0e,0x6f10,0x6f11,0x6f12,0x6f16,
+ 0x6f17,0x6f18,0x6f19,0x6f1a,0x6f1b,0x6f1c,0x6f1d,0x6f1e,0x6f1f,0x6f21,
+ 0x6f22,0x6f23,0x6f25,0x6f26,0x6f27,0x6f28,0x6f2c,0x6f2e,0x6f30,0x6f32,
+ 0x6f34,0x6f35,0x6f37,0x6f38,0x6f39,0x6f3a,0x6f3b,0x6f3c,0x6f3d,0x6f3f,
+ 0x6f40,0x6f41,0x6f42,UBOGON,0x6f43,0x6f44,0x6f45,0x6f48,0x6f49,0x6f4a,
+ 0x6f4c,0x6f4e,0x6f4f,0x6f50,0x6f51,0x6f52,0x6f53,0x6f54,0x6f55,0x6f56,
+ 0x6f57,0x6f59,0x6f5a,0x6f5b,0x6f5d,0x6f5f,0x6f60,0x6f61,0x6f63,0x6f64,
+ 0x6f65,0x6f67,0x6f68,0x6f69,0x6f6a,0x6f6b,0x6f6c,0x6f6f,0x6f70,0x6f71,
+ 0x6f73,0x6f75,0x6f76,0x6f77,0x6f79,0x6f7b,0x6f7d,0x6f7e,0x6f7f,0x6f80,
+ 0x6f81,0x6f82,0x6f83,0x6f85,0x6f86,0x6f87,0x6f8a,0x6f8b,0x6f8f,0x6f90,
+ 0x6f91,0x6f92,0x6f93,0x6f94,0x6f95,0x6f96,0x6f97,0x6f98,0x6f99,0x6f9a,
+ 0x6f9b,0x6f9d,0x6f9e,0x6f9f,0x6fa0,0x6fa2,0x6fa3,0x6fa4,0x6fa5,0x6fa6,
+ 0x6fa8,0x6fa9,0x6faa,0x6fab,0x6fac,0x6fad,0x6fae,0x6faf,0x6fb0,0x6fb1,
+ 0x6fb2,0x6fb4,0x6fb5,0x6fb7,0x6fb8,0x6fba,0x6fbb,0x6fbc,0x6fbd,0x6fbe,
+ 0x6fbf,0x6fc1,0x6fc3,0x6fc4,0x6fc5,0x6fc6,0x6fc7,0x6fc8,0x6fca,0x6fcb,
+ 0x6fcc,0x6fcd,0x6fce,0x6fcf,0x6fd0,0x6fd3,0x6fd4,0x6fd5,0x6fd6,0x6fd7,
+ 0x6fd8,0x6fd9,0x6fda,0x6fdb,0x6fdc,0x6fdd,0x6fdf,0x6fe2,0x6fe3,0x6fe4,
+ 0x6fe5
+ },
+ { /* ku 1e */
+ 0x6fe6,0x6fe7,0x6fe8,0x6fe9,0x6fea,0x6feb,0x6fec,0x6fed,0x6ff0,0x6ff1,
+ 0x6ff2,0x6ff3,0x6ff4,0x6ff5,0x6ff6,0x6ff7,0x6ff8,0x6ff9,0x6ffa,0x6ffb,
+ 0x6ffc,0x6ffd,0x6ffe,0x6fff,0x7000,0x7001,0x7002,0x7003,0x7004,0x7005,
+ 0x7006,0x7007,0x7008,0x7009,0x700a,0x700b,0x700c,0x700d,0x700e,0x700f,
+ 0x7010,0x7012,0x7013,0x7014,0x7015,0x7016,0x7017,0x7018,0x7019,0x701c,
+ 0x701d,0x701e,0x701f,0x7020,0x7021,0x7022,0x7024,0x7025,0x7026,0x7027,
+ 0x7028,0x7029,0x702a,UBOGON,0x702b,0x702c,0x702d,0x702e,0x702f,0x7030,
+ 0x7031,0x7032,0x7033,0x7034,0x7036,0x7037,0x7038,0x703a,0x703b,0x703c,
+ 0x703d,0x703e,0x703f,0x7040,0x7041,0x7042,0x7043,0x7044,0x7045,0x7046,
+ 0x7047,0x7048,0x7049,0x704a,0x704b,0x704d,0x704e,0x7050,0x7051,0x7052,
+ 0x7053,0x7054,0x7055,0x7056,0x7057,0x7058,0x7059,0x705a,0x705b,0x705c,
+ 0x705d,0x705f,0x7060,0x7061,0x7062,0x7063,0x7064,0x7065,0x7066,0x7067,
+ 0x7068,0x7069,0x706a,0x706e,0x7071,0x7072,0x7073,0x7074,0x7077,0x7079,
+ 0x707a,0x707b,0x707d,0x7081,0x7082,0x7083,0x7084,0x7086,0x7087,0x7088,
+ 0x708b,0x708c,0x708d,0x708f,0x7090,0x7091,0x7093,0x7097,0x7098,0x709a,
+ 0x709b,0x709e,0x709f,0x70a0,0x70a1,0x70a2,0x70a3,0x70a4,0x70a5,0x70a6,
+ 0x70a7,0x70a8,0x70a9,0x70aa,0x70b0,0x70b2,0x70b4,0x70b5,0x70b6,0x70ba,
+ 0x70be,0x70bf,0x70c4,0x70c5,0x70c6,0x70c7,0x70c9,0x70cb,0x70cc,0x70cd,
+ 0x70ce,0x70cf,0x70d0,0x70d1,0x70d2,0x70d3,0x70d4,0x70d5,0x70d6,0x70d7,
+ 0x70da
+ },
+ { /* ku 1f */
+ 0x70dc,0x70dd,0x70de,0x70e0,0x70e1,0x70e2,0x70e3,0x70e5,0x70ea,0x70ee,
+ 0x70f0,0x70f1,0x70f2,0x70f3,0x70f4,0x70f5,0x70f6,0x70f8,0x70fa,0x70fb,
+ 0x70fc,0x70fe,0x70ff,0x7100,0x7101,0x7102,0x7103,0x7104,0x7105,0x7106,
+ 0x7107,0x7108,0x710b,0x710c,0x710d,0x710e,0x710f,0x7111,0x7112,0x7114,
+ 0x7117,0x711b,0x711c,0x711d,0x711e,0x711f,0x7120,0x7121,0x7122,0x7123,
+ 0x7124,0x7125,0x7127,0x7128,0x7129,0x712a,0x712b,0x712c,0x712d,0x712e,
+ 0x7132,0x7133,0x7134,UBOGON,0x7135,0x7137,0x7138,0x7139,0x713a,0x713b,
+ 0x713c,0x713d,0x713e,0x713f,0x7140,0x7141,0x7142,0x7143,0x7144,0x7146,
+ 0x7147,0x7148,0x7149,0x714b,0x714d,0x714f,0x7150,0x7151,0x7152,0x7153,
+ 0x7154,0x7155,0x7156,0x7157,0x7158,0x7159,0x715a,0x715b,0x715d,0x715f,
+ 0x7160,0x7161,0x7162,0x7163,0x7165,0x7169,0x716a,0x716b,0x716c,0x716d,
+ 0x716f,0x7170,0x7171,0x7174,0x7175,0x7176,0x7177,0x7179,0x717b,0x717c,
+ 0x717e,0x717f,0x7180,0x7181,0x7182,0x7183,0x7185,0x7186,0x7187,0x7188,
+ 0x7189,0x718b,0x718c,0x718d,0x718e,0x7190,0x7191,0x7192,0x7193,0x7195,
+ 0x7196,0x7197,0x719a,0x719b,0x719c,0x719d,0x719e,0x71a1,0x71a2,0x71a3,
+ 0x71a4,0x71a5,0x71a6,0x71a7,0x71a9,0x71aa,0x71ab,0x71ad,0x71ae,0x71af,
+ 0x71b0,0x71b1,0x71b2,0x71b4,0x71b6,0x71b7,0x71b8,0x71ba,0x71bb,0x71bc,
+ 0x71bd,0x71be,0x71bf,0x71c0,0x71c1,0x71c2,0x71c4,0x71c5,0x71c6,0x71c7,
+ 0x71c8,0x71c9,0x71ca,0x71cb,0x71cc,0x71cd,0x71cf,0x71d0,0x71d1,0x71d2,
+ 0x71d3
+ },
+ { /* ku 20 */
+ 0x71d6,0x71d7,0x71d8,0x71d9,0x71da,0x71db,0x71dc,0x71dd,0x71de,0x71df,
+ 0x71e1,0x71e2,0x71e3,0x71e4,0x71e6,0x71e8,0x71e9,0x71ea,0x71eb,0x71ec,
+ 0x71ed,0x71ef,0x71f0,0x71f1,0x71f2,0x71f3,0x71f4,0x71f5,0x71f6,0x71f7,
+ 0x71f8,0x71fa,0x71fb,0x71fc,0x71fd,0x71fe,0x71ff,0x7200,0x7201,0x7202,
+ 0x7203,0x7204,0x7205,0x7207,0x7208,0x7209,0x720a,0x720b,0x720c,0x720d,
+ 0x720e,0x720f,0x7210,0x7211,0x7212,0x7213,0x7214,0x7215,0x7216,0x7217,
+ 0x7218,0x7219,0x721a,UBOGON,0x721b,0x721c,0x721e,0x721f,0x7220,0x7221,
+ 0x7222,0x7223,0x7224,0x7225,0x7226,0x7227,0x7229,0x722b,0x722d,0x722e,
+ 0x722f,0x7232,0x7233,0x7234,0x723a,0x723c,0x723e,0x7240,0x7241,0x7242,
+ 0x7243,0x7244,0x7245,0x7246,0x7249,0x724a,0x724b,0x724e,0x724f,0x7250,
+ 0x7251,0x7253,0x7254,0x7255,0x7257,0x7258,0x725a,0x725c,0x725e,0x7260,
+ 0x7263,0x7264,0x7265,0x7268,0x726a,0x726b,0x726c,0x726d,0x7270,0x7271,
+ 0x7273,0x7274,0x7276,0x7277,0x7278,0x727b,0x727c,0x727d,0x7282,0x7283,
+ 0x7285,0x7286,0x7287,0x7288,0x7289,0x728c,0x728e,0x7290,0x7291,0x7293,
+ 0x7294,0x7295,0x7296,0x7297,0x7298,0x7299,0x729a,0x729b,0x729c,0x729d,
+ 0x729e,0x72a0,0x72a1,0x72a2,0x72a3,0x72a4,0x72a5,0x72a6,0x72a7,0x72a8,
+ 0x72a9,0x72aa,0x72ab,0x72ae,0x72b1,0x72b2,0x72b3,0x72b5,0x72ba,0x72bb,
+ 0x72bc,0x72bd,0x72be,0x72bf,0x72c0,0x72c5,0x72c6,0x72c7,0x72c9,0x72ca,
+ 0x72cb,0x72cc,0x72cf,0x72d1,0x72d3,0x72d4,0x72d5,0x72d6,0x72d8,0x72da,
+ 0x72db
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3000,0x3001,0x3002,
+ 0x00b7,0x02c9,0x02c7,0x00a8,0x3003,0x3005,0x2014,0xff5e,0x2016,0x2026,
+ 0x2018,0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,0x3009,0x300a,0x300b,
+ 0x300c,0x300d,0x300e,0x300f,0x3016,0x3017,0x3010,0x3011,0x00b1,0x00d7,
+ 0x00f7,0x2236,0x2227,0x2228,0x2211,0x220f,0x222a,0x2229,0x2208,0x2237,
+ 0x221a,0x22a5,0x2225,0x2220,0x2312,0x2299,0x222b,0x222e,0x2261,0x224c,
+ 0x2248,0x223d,0x221d,0x2260,0x226e,0x226f,0x2264,0x2265,0x221e,0x2235,
+ 0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,0xff04,0x00a4,0xffe0,
+ 0xffe1,0x2030,0x00a7,0x2116,0x2606,0x2605,0x25cb,0x25cf,0x25ce,0x25c7,
+ 0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x203b,0x2192,0x2190,0x2191,0x2193,
+ 0x3013
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2170,0x2171,0x2172,
+ 0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x2488,0x2489,0x248a,0x248b,0x248c,0x248d,0x248e,
+ 0x248f,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,0x2498,
+ 0x2499,0x249a,0x249b,0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,
+ 0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,0x2482,0x2483,0x2484,
+ 0x2485,0x2486,0x2487,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,
+ 0x2467,0x2468,0x2469,UBOGON,UBOGON,0x3220,0x3221,0x3222,0x3223,0x3224,
+ 0x3225,0x3226,0x3227,0x3228,0x3229,UBOGON,UBOGON,0x2160,0x2161,0x2162,
+ 0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216a,0x216b,UBOGON,
+ UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xff01,0xff02,0xff03,
+ 0xffe5,0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,0xff0b,0xff0c,0xff0d,
+ 0xff0e,0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,
+ 0xff18,0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,0xff1f,0xff20,0xff21,
+ 0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,
+ 0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,
+ 0xff36,0xff37,0xff38,0xff39,0xff3a,0xff3b,0xff3c,0xff3d,0xff3e,0xff3f,
+ 0xff40,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,
+ 0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,
+ 0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,0xff5b,0xff5c,0xff5d,
+ 0xffe3
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3041,0x3042,0x3043,
+ 0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,
+ 0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,
+ 0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,
+ 0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,
+ 0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,
+ 0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,
+ 0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,
+ 0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x30a1,0x30a2,0x30a3,
+ 0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,
+ 0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,
+ 0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,
+ 0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,
+ 0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,
+ 0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,
+ 0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,0x30e7,0x30e8,0x30e9,
+ 0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,0x30f1,0x30f2,0x30f3,
+ 0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0391,0x0392,0x0393,
+ 0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,
+ 0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x03b1,
+ 0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,
+ 0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0xfe35,0xfe36,0xfe39,0xfe3a,0xfe3f,0xfe40,0xfe3d,0xfe3e,0xfe41,0xfe42,
+ 0xfe43,0xfe44,UBOGON,UBOGON,0xfe3b,0xfe3c,0xfe37,0xfe38,0xfe31,UBOGON,
+ 0xfe33,0xfe34,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0410,0x0411,0x0412,
+ 0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,
+ 0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,
+ 0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,0x0432,0x0433,0x0434,
+ 0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,
+ 0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 28 */
+ 0x02ca,0x02cb,0x02d9,0x2013,0x2015,0x2025,0x2035,0x2105,0x2109,0x2196,
+ 0x2197,0x2198,0x2199,0x2215,0x221f,0x2223,0x2252,0x2266,0x2267,0x22bf,
+ 0x2550,0x2551,0x2552,0x2553,0x2554,0x2555,0x2556,0x2557,0x2558,0x2559,
+ 0x255a,0x255b,0x255c,0x255d,0x255e,0x255f,0x2560,0x2561,0x2562,0x2563,
+ 0x2564,0x2565,0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x256d,
+ 0x256e,0x256f,0x2570,0x2571,0x2572,0x2573,0x2581,0x2582,0x2583,0x2584,
+ 0x2585,0x2586,0x2587,UBOGON,0x2588,0x2589,0x258a,0x258b,0x258c,0x258d,
+ 0x258e,0x258f,0x2593,0x2594,0x2595,0x25bc,0x25bd,0x25e2,0x25e3,0x25e4,
+ 0x25e5,0x2609,0x2295,0x3012,0x301d,0x301e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0101,0x00e1,0x01ce,
+ 0x00e0,0x0113,0x00e9,0x011b,0x00e8,0x012b,0x00ed,0x01d0,0x00ec,0x014d,
+ 0x00f3,0x01d2,0x00f2,0x016b,0x00fa,0x01d4,0x00f9,0x01d6,0x01d8,0x01da,
+ 0x01dc,0x00fc,0x00ea,0x0251,UBOGON,0x0144,0x0148,UBOGON,0x0261,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3105,0x3106,0x3107,0x3108,0x3109,0x310a,0x310b,
+ 0x310c,0x310d,0x310e,0x310f,0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,
+ 0x3116,0x3117,0x3118,0x3119,0x311a,0x311b,0x311c,0x311d,0x311e,0x311f,
+ 0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,0x3127,0x3128,0x3129,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 29 */
+ 0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029,0x32a3,
+ 0x338e,0x338f,0x339c,0x339d,0x339e,0x33a1,0x33c4,0x33ce,0x33d1,0x33d2,
+ 0x33d5,0xfe30,0xffe2,0xffe4,UBOGON,0x2121,0x3231,UBOGON,0x2010,UBOGON,
+ UBOGON,UBOGON,0x30fc,0x309b,0x309c,0x30fd,0x30fe,0x3006,0x309d,0x309e,
+ 0xfe49,0xfe4a,0xfe4b,0xfe4c,0xfe4d,0xfe4e,0xfe4f,0xfe50,0xfe51,0xfe52,
+ 0xfe54,0xfe55,0xfe56,0xfe57,0xfe59,0xfe5a,0xfe5b,0xfe5c,0xfe5d,0xfe5e,
+ 0xfe5f,0xfe60,0xfe61,UBOGON,0xfe62,0xfe63,0xfe64,0xfe65,0xfe66,0xfe68,
+ 0xfe69,0xfe6a,0xfe6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3007,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,0x2507,0x2508,0x2509,
+ 0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,0x2510,0x2511,0x2512,0x2513,
+ 0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251a,0x251b,0x251c,0x251d,
+ 0x251e,0x251f,0x2520,0x2521,0x2522,0x2523,0x2524,0x2525,0x2526,0x2527,
+ 0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,0x252f,0x2530,0x2531,
+ 0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,0x2538,0x2539,0x253a,0x253b,
+ 0x253c,0x253d,0x253e,0x253f,0x2540,0x2541,0x2542,0x2543,0x2544,0x2545,
+ 0x2546,0x2547,0x2548,0x2549,0x254a,0x254b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2a */
+ 0x72dc,0x72dd,0x72df,0x72e2,0x72e3,0x72e4,0x72e5,0x72e6,0x72e7,0x72ea,
+ 0x72eb,0x72f5,0x72f6,0x72f9,0x72fd,0x72fe,0x72ff,0x7300,0x7302,0x7304,
+ 0x7305,0x7306,0x7307,0x7308,0x7309,0x730b,0x730c,0x730d,0x730f,0x7310,
+ 0x7311,0x7312,0x7314,0x7318,0x7319,0x731a,0x731f,0x7320,0x7323,0x7324,
+ 0x7326,0x7327,0x7328,0x732d,0x732f,0x7330,0x7332,0x7333,0x7335,0x7336,
+ 0x733a,0x733b,0x733c,0x733d,0x7340,0x7341,0x7342,0x7343,0x7344,0x7345,
+ 0x7346,0x7347,0x7348,UBOGON,0x7349,0x734a,0x734b,0x734c,0x734e,0x734f,
+ 0x7351,0x7353,0x7354,0x7355,0x7356,0x7358,0x7359,0x735a,0x735b,0x735c,
+ 0x735d,0x735e,0x735f,0x7361,0x7362,0x7363,0x7364,0x7365,0x7366,0x7367,
+ 0x7368,0x7369,0x736a,0x736b,0x736e,0x7370,0x7371,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2b */
+ 0x7372,0x7373,0x7374,0x7375,0x7376,0x7377,0x7378,0x7379,0x737a,0x737b,
+ 0x737c,0x737d,0x737f,0x7380,0x7381,0x7382,0x7383,0x7385,0x7386,0x7388,
+ 0x738a,0x738c,0x738d,0x738f,0x7390,0x7392,0x7393,0x7394,0x7395,0x7397,
+ 0x7398,0x7399,0x739a,0x739c,0x739d,0x739e,0x73a0,0x73a1,0x73a3,0x73a4,
+ 0x73a5,0x73a6,0x73a7,0x73a8,0x73aa,0x73ac,0x73ad,0x73b1,0x73b4,0x73b5,
+ 0x73b6,0x73b8,0x73b9,0x73bc,0x73bd,0x73be,0x73bf,0x73c1,0x73c3,0x73c4,
+ 0x73c5,0x73c6,0x73c7,UBOGON,0x73cb,0x73cc,0x73ce,0x73d2,0x73d3,0x73d4,
+ 0x73d5,0x73d6,0x73d7,0x73d8,0x73da,0x73db,0x73dc,0x73dd,0x73df,0x73e1,
+ 0x73e2,0x73e3,0x73e4,0x73e6,0x73e8,0x73ea,0x73eb,0x73ec,0x73ee,0x73ef,
+ 0x73f0,0x73f1,0x73f3,0x73f4,0x73f5,0x73f6,0x73f7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2c */
+ 0x73f8,0x73f9,0x73fa,0x73fb,0x73fc,0x73fd,0x73fe,0x73ff,0x7400,0x7401,
+ 0x7402,0x7404,0x7407,0x7408,0x740b,0x740c,0x740d,0x740e,0x7411,0x7412,
+ 0x7413,0x7414,0x7415,0x7416,0x7417,0x7418,0x7419,0x741c,0x741d,0x741e,
+ 0x741f,0x7420,0x7421,0x7423,0x7424,0x7427,0x7429,0x742b,0x742d,0x742f,
+ 0x7431,0x7432,0x7437,0x7438,0x7439,0x743a,0x743b,0x743d,0x743e,0x743f,
+ 0x7440,0x7442,0x7443,0x7444,0x7445,0x7446,0x7447,0x7448,0x7449,0x744a,
+ 0x744b,0x744c,0x744d,UBOGON,0x744e,0x744f,0x7450,0x7451,0x7452,0x7453,
+ 0x7454,0x7456,0x7458,0x745d,0x7460,0x7461,0x7462,0x7463,0x7464,0x7465,
+ 0x7466,0x7467,0x7468,0x7469,0x746a,0x746b,0x746c,0x746e,0x746f,0x7471,
+ 0x7472,0x7473,0x7474,0x7475,0x7478,0x7479,0x747a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2d */
+ 0x747b,0x747c,0x747d,0x747f,0x7482,0x7484,0x7485,0x7486,0x7488,0x7489,
+ 0x748a,0x748c,0x748d,0x748f,0x7491,0x7492,0x7493,0x7494,0x7495,0x7496,
+ 0x7497,0x7498,0x7499,0x749a,0x749b,0x749d,0x749f,0x74a0,0x74a1,0x74a2,
+ 0x74a3,0x74a4,0x74a5,0x74a6,0x74aa,0x74ab,0x74ac,0x74ad,0x74ae,0x74af,
+ 0x74b0,0x74b1,0x74b2,0x74b3,0x74b4,0x74b5,0x74b6,0x74b7,0x74b8,0x74b9,
+ 0x74bb,0x74bc,0x74bd,0x74be,0x74bf,0x74c0,0x74c1,0x74c2,0x74c3,0x74c4,
+ 0x74c5,0x74c6,0x74c7,UBOGON,0x74c8,0x74c9,0x74ca,0x74cb,0x74cc,0x74cd,
+ 0x74ce,0x74cf,0x74d0,0x74d1,0x74d3,0x74d4,0x74d5,0x74d6,0x74d7,0x74d8,
+ 0x74d9,0x74da,0x74db,0x74dd,0x74df,0x74e1,0x74e5,0x74e7,0x74e8,0x74e9,
+ 0x74ea,0x74eb,0x74ec,0x74ed,0x74f0,0x74f1,0x74f2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2e */
+ 0x74f3,0x74f5,0x74f8,0x74f9,0x74fa,0x74fb,0x74fc,0x74fd,0x74fe,0x7500,
+ 0x7501,0x7502,0x7503,0x7505,0x7506,0x7507,0x7508,0x7509,0x750a,0x750b,
+ 0x750c,0x750e,0x7510,0x7512,0x7514,0x7515,0x7516,0x7517,0x751b,0x751d,
+ 0x751e,0x7520,0x7521,0x7522,0x7523,0x7524,0x7526,0x7527,0x752a,0x752e,
+ 0x7534,0x7536,0x7539,0x753c,0x753d,0x753f,0x7541,0x7542,0x7543,0x7544,
+ 0x7546,0x7547,0x7549,0x754a,0x754d,0x7550,0x7551,0x7552,0x7553,0x7555,
+ 0x7556,0x7557,0x7558,UBOGON,0x755d,0x755e,0x755f,0x7560,0x7561,0x7562,
+ 0x7563,0x7564,0x7567,0x7568,0x7569,0x756b,0x756c,0x756d,0x756e,0x756f,
+ 0x7570,0x7571,0x7573,0x7575,0x7576,0x7577,0x757a,0x757b,0x757c,0x757d,
+ 0x757e,0x7580,0x7581,0x7582,0x7584,0x7585,0x7587,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2f */
+ 0x7588,0x7589,0x758a,0x758c,0x758d,0x758e,0x7590,0x7593,0x7595,0x7598,
+ 0x759b,0x759c,0x759e,0x75a2,0x75a6,0x75a7,0x75a8,0x75a9,0x75aa,0x75ad,
+ 0x75b6,0x75b7,0x75ba,0x75bb,0x75bf,0x75c0,0x75c1,0x75c6,0x75cb,0x75cc,
+ 0x75ce,0x75cf,0x75d0,0x75d1,0x75d3,0x75d7,0x75d9,0x75da,0x75dc,0x75dd,
+ 0x75df,0x75e0,0x75e1,0x75e5,0x75e9,0x75ec,0x75ed,0x75ee,0x75ef,0x75f2,
+ 0x75f3,0x75f5,0x75f6,0x75f7,0x75f8,0x75fa,0x75fb,0x75fd,0x75fe,0x7602,
+ 0x7604,0x7606,0x7607,UBOGON,0x7608,0x7609,0x760b,0x760d,0x760e,0x760f,
+ 0x7611,0x7612,0x7613,0x7614,0x7616,0x761a,0x761c,0x761d,0x761e,0x7621,
+ 0x7623,0x7627,0x7628,0x762c,0x762e,0x762f,0x7631,0x7632,0x7636,0x7637,
+ 0x7639,0x763a,0x763b,0x763d,0x7641,0x7642,0x7644,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 30 */
+ 0x7645,0x7646,0x7647,0x7648,0x7649,0x764a,0x764b,0x764e,0x764f,0x7650,
+ 0x7651,0x7652,0x7653,0x7655,0x7657,0x7658,0x7659,0x765a,0x765b,0x765d,
+ 0x765f,0x7660,0x7661,0x7662,0x7664,0x7665,0x7666,0x7667,0x7668,0x7669,
+ 0x766a,0x766c,0x766d,0x766e,0x7670,0x7671,0x7672,0x7673,0x7674,0x7675,
+ 0x7676,0x7677,0x7679,0x767a,0x767c,0x767f,0x7680,0x7681,0x7683,0x7685,
+ 0x7689,0x768a,0x768c,0x768d,0x768f,0x7690,0x7692,0x7694,0x7695,0x7697,
+ 0x7698,0x769a,0x769b,UBOGON,0x769c,0x769d,0x769e,0x769f,0x76a0,0x76a1,
+ 0x76a2,0x76a3,0x76a5,0x76a6,0x76a7,0x76a8,0x76a9,0x76aa,0x76ab,0x76ac,
+ 0x76ad,0x76af,0x76b0,0x76b3,0x76b5,0x76b6,0x76b7,0x76b8,0x76b9,0x76ba,
+ 0x76bb,0x76bc,0x76bd,0x76be,0x76c0,0x76c1,0x76c3,0x554a,0x963f,0x57c3,
+ 0x6328,0x54ce,0x5509,0x54c0,0x7691,0x764c,0x853c,0x77ee,0x827e,0x788d,
+ 0x7231,0x9698,0x978d,0x6c28,0x5b89,0x4ffa,0x6309,0x6697,0x5cb8,0x80fa,
+ 0x6848,0x80ae,0x6602,0x76ce,0x51f9,0x6556,0x71ac,0x7ff1,0x8884,0x50b2,
+ 0x5965,0x61ca,0x6fb3,0x82ad,0x634c,0x6252,0x53ed,0x5427,0x7b06,0x516b,
+ 0x75a4,0x5df4,0x62d4,0x8dcb,0x9776,0x628a,0x8019,0x575d,0x9738,0x7f62,
+ 0x7238,0x767d,0x67cf,0x767e,0x6446,0x4f70,0x8d25,0x62dc,0x7a17,0x6591,
+ 0x73ed,0x642c,0x6273,0x822c,0x9881,0x677f,0x7248,0x626e,0x62cc,0x4f34,
+ 0x74e3,0x534a,0x529e,0x7eca,0x90a6,0x5e2e,0x6886,0x699c,0x8180,0x7ed1,
+ 0x68d2,0x78c5,0x868c,0x9551,0x508d,0x8c24,0x82de,0x80de,0x5305,0x8912,
+ 0x5265
+ },
+ { /* ku 31 */
+ 0x76c4,0x76c7,0x76c9,0x76cb,0x76cc,0x76d3,0x76d5,0x76d9,0x76da,0x76dc,
+ 0x76dd,0x76de,0x76e0,0x76e1,0x76e2,0x76e3,0x76e4,0x76e6,0x76e7,0x76e8,
+ 0x76e9,0x76ea,0x76eb,0x76ec,0x76ed,0x76f0,0x76f3,0x76f5,0x76f6,0x76f7,
+ 0x76fa,0x76fb,0x76fd,0x76ff,0x7700,0x7702,0x7703,0x7705,0x7706,0x770a,
+ 0x770c,0x770e,0x770f,0x7710,0x7711,0x7712,0x7713,0x7714,0x7715,0x7716,
+ 0x7717,0x7718,0x771b,0x771c,0x771d,0x771e,0x7721,0x7723,0x7724,0x7725,
+ 0x7727,0x772a,0x772b,UBOGON,0x772c,0x772e,0x7730,0x7731,0x7732,0x7733,
+ 0x7734,0x7739,0x773b,0x773d,0x773e,0x773f,0x7742,0x7744,0x7745,0x7746,
+ 0x7748,0x7749,0x774a,0x774b,0x774c,0x774d,0x774e,0x774f,0x7752,0x7753,
+ 0x7754,0x7755,0x7756,0x7757,0x7758,0x7759,0x775c,0x8584,0x96f9,0x4fdd,
+ 0x5821,0x9971,0x5b9d,0x62b1,0x62a5,0x66b4,0x8c79,0x9c8d,0x7206,0x676f,
+ 0x7891,0x60b2,0x5351,0x5317,0x8f88,0x80cc,0x8d1d,0x94a1,0x500d,0x72c8,
+ 0x5907,0x60eb,0x7119,0x88ab,0x5954,0x82ef,0x672c,0x7b28,0x5d29,0x7ef7,
+ 0x752d,0x6cf5,0x8e66,0x8ff8,0x903c,0x9f3b,0x6bd4,0x9119,0x7b14,0x5f7c,
+ 0x78a7,0x84d6,0x853d,0x6bd5,0x6bd9,0x6bd6,0x5e01,0x5e87,0x75f9,0x95ed,
+ 0x655d,0x5f0a,0x5fc5,0x8f9f,0x58c1,0x81c2,0x907f,0x965b,0x97ad,0x8fb9,
+ 0x7f16,0x8d2c,0x6241,0x4fbf,0x53d8,0x535e,0x8fa8,0x8fa9,0x8fab,0x904d,
+ 0x6807,0x5f6a,0x8198,0x8868,0x9cd6,0x618b,0x522b,0x762a,0x5f6c,0x658c,
+ 0x6fd2,0x6ee8,0x5bbe,0x6448,0x5175,0x51b0,0x67c4,0x4e19,0x79c9,0x997c,
+ 0x70b3
+ },
+ { /* ku 32 */
+ 0x775d,0x775e,0x775f,0x7760,0x7764,0x7767,0x7769,0x776a,0x776d,0x776e,
+ 0x776f,0x7770,0x7771,0x7772,0x7773,0x7774,0x7775,0x7776,0x7777,0x7778,
+ 0x777a,0x777b,0x777c,0x7781,0x7782,0x7783,0x7786,0x7787,0x7788,0x7789,
+ 0x778a,0x778b,0x778f,0x7790,0x7793,0x7794,0x7795,0x7796,0x7797,0x7798,
+ 0x7799,0x779a,0x779b,0x779c,0x779d,0x779e,0x77a1,0x77a3,0x77a4,0x77a6,
+ 0x77a8,0x77ab,0x77ad,0x77ae,0x77af,0x77b1,0x77b2,0x77b4,0x77b6,0x77b7,
+ 0x77b8,0x77b9,0x77ba,UBOGON,0x77bc,0x77be,0x77c0,0x77c1,0x77c2,0x77c3,
+ 0x77c4,0x77c5,0x77c6,0x77c7,0x77c8,0x77c9,0x77ca,0x77cb,0x77cc,0x77ce,
+ 0x77cf,0x77d0,0x77d1,0x77d2,0x77d3,0x77d4,0x77d5,0x77d6,0x77d8,0x77d9,
+ 0x77da,0x77dd,0x77de,0x77df,0x77e0,0x77e1,0x77e4,0x75c5,0x5e76,0x73bb,
+ 0x83e0,0x64ad,0x62e8,0x94b5,0x6ce2,0x535a,0x52c3,0x640f,0x94c2,0x7b94,
+ 0x4f2f,0x5e1b,0x8236,0x8116,0x818a,0x6e24,0x6cca,0x9a73,0x6355,0x535c,
+ 0x54fa,0x8865,0x57e0,0x4e0d,0x5e03,0x6b65,0x7c3f,0x90e8,0x6016,0x64e6,
+ 0x731c,0x88c1,0x6750,0x624d,0x8d22,0x776c,0x8e29,0x91c7,0x5f69,0x83dc,
+ 0x8521,0x9910,0x53c2,0x8695,0x6b8b,0x60ed,0x60e8,0x707f,0x82cd,0x8231,
+ 0x4ed3,0x6ca7,0x85cf,0x64cd,0x7cd9,0x69fd,0x66f9,0x8349,0x5395,0x7b56,
+ 0x4fa7,0x518c,0x6d4b,0x5c42,0x8e6d,0x63d2,0x53c9,0x832c,0x8336,0x67e5,
+ 0x78b4,0x643d,0x5bdf,0x5c94,0x5dee,0x8be7,0x62c6,0x67f4,0x8c7a,0x6400,
+ 0x63ba,0x8749,0x998b,0x8c17,0x7f20,0x94f2,0x4ea7,0x9610,0x98a4,0x660c,
+ 0x7316
+ },
+ { /* ku 33 */
+ 0x77e6,0x77e8,0x77ea,0x77ef,0x77f0,0x77f1,0x77f2,0x77f4,0x77f5,0x77f7,
+ 0x77f9,0x77fa,0x77fb,0x77fc,0x7803,0x7804,0x7805,0x7806,0x7807,0x7808,
+ 0x780a,0x780b,0x780e,0x780f,0x7810,0x7813,0x7815,0x7819,0x781b,0x781e,
+ 0x7820,0x7821,0x7822,0x7824,0x7828,0x782a,0x782b,0x782e,0x782f,0x7831,
+ 0x7832,0x7833,0x7835,0x7836,0x783d,0x783f,0x7841,0x7842,0x7843,0x7844,
+ 0x7846,0x7848,0x7849,0x784a,0x784b,0x784d,0x784f,0x7851,0x7853,0x7854,
+ 0x7858,0x7859,0x785a,UBOGON,0x785b,0x785c,0x785e,0x785f,0x7860,0x7861,
+ 0x7862,0x7863,0x7864,0x7865,0x7866,0x7867,0x7868,0x7869,0x786f,0x7870,
+ 0x7871,0x7872,0x7873,0x7874,0x7875,0x7876,0x7878,0x7879,0x787a,0x787b,
+ 0x787d,0x787e,0x787f,0x7880,0x7881,0x7882,0x7883,0x573a,0x5c1d,0x5e38,
+ 0x957f,0x507f,0x80a0,0x5382,0x655e,0x7545,0x5531,0x5021,0x8d85,0x6284,
+ 0x949e,0x671d,0x5632,0x6f6e,0x5de2,0x5435,0x7092,0x8f66,0x626f,0x64a4,
+ 0x63a3,0x5f7b,0x6f88,0x90f4,0x81e3,0x8fb0,0x5c18,0x6668,0x5ff1,0x6c89,
+ 0x9648,0x8d81,0x886c,0x6491,0x79f0,0x57ce,0x6a59,0x6210,0x5448,0x4e58,
+ 0x7a0b,0x60e9,0x6f84,0x8bda,0x627f,0x901e,0x9a8b,0x79e4,0x5403,0x75f4,
+ 0x6301,0x5319,0x6c60,0x8fdf,0x5f1b,0x9a70,0x803b,0x9f7f,0x4f88,0x5c3a,
+ 0x8d64,0x7fc5,0x65a5,0x70bd,0x5145,0x51b2,0x866b,0x5d07,0x5ba0,0x62bd,
+ 0x916c,0x7574,0x8e0c,0x7a20,0x6101,0x7b79,0x4ec7,0x7ef8,0x7785,0x4e11,
+ 0x81ed,0x521d,0x51fa,0x6a71,0x53a8,0x8e87,0x9504,0x96cf,0x6ec1,0x9664,
+ 0x695a
+ },
+ { /* ku 34 */
+ 0x7884,0x7885,0x7886,0x7888,0x788a,0x788b,0x788f,0x7890,0x7892,0x7894,
+ 0x7895,0x7896,0x7899,0x789d,0x789e,0x78a0,0x78a2,0x78a4,0x78a6,0x78a8,
+ 0x78a9,0x78aa,0x78ab,0x78ac,0x78ad,0x78ae,0x78af,0x78b5,0x78b6,0x78b7,
+ 0x78b8,0x78ba,0x78bb,0x78bc,0x78bd,0x78bf,0x78c0,0x78c2,0x78c3,0x78c4,
+ 0x78c6,0x78c7,0x78c8,0x78cc,0x78cd,0x78ce,0x78cf,0x78d1,0x78d2,0x78d3,
+ 0x78d6,0x78d7,0x78d8,0x78da,0x78db,0x78dc,0x78dd,0x78de,0x78df,0x78e0,
+ 0x78e1,0x78e2,0x78e3,UBOGON,0x78e4,0x78e5,0x78e6,0x78e7,0x78e9,0x78ea,
+ 0x78eb,0x78ed,0x78ee,0x78ef,0x78f0,0x78f1,0x78f3,0x78f5,0x78f6,0x78f8,
+ 0x78f9,0x78fb,0x78fc,0x78fd,0x78fe,0x78ff,0x7900,0x7902,0x7903,0x7904,
+ 0x7906,0x7907,0x7908,0x7909,0x790a,0x790b,0x790c,0x7840,0x50a8,0x77d7,
+ 0x6410,0x89e6,0x5904,0x63e3,0x5ddd,0x7a7f,0x693d,0x4f20,0x8239,0x5598,
+ 0x4e32,0x75ae,0x7a97,0x5e62,0x5e8a,0x95ef,0x521b,0x5439,0x708a,0x6376,
+ 0x9524,0x5782,0x6625,0x693f,0x9187,0x5507,0x6df3,0x7eaf,0x8822,0x6233,
+ 0x7ef0,0x75b5,0x8328,0x78c1,0x96cc,0x8f9e,0x6148,0x74f7,0x8bcd,0x6b64,
+ 0x523a,0x8d50,0x6b21,0x806a,0x8471,0x56f1,0x5306,0x4ece,0x4e1b,0x51d1,
+ 0x7c97,0x918b,0x7c07,0x4fc3,0x8e7f,0x7be1,0x7a9c,0x6467,0x5d14,0x50ac,
+ 0x8106,0x7601,0x7cb9,0x6dec,0x7fe0,0x6751,0x5b58,0x5bf8,0x78cb,0x64ae,
+ 0x6413,0x63aa,0x632b,0x9519,0x642d,0x8fbe,0x7b54,0x7629,0x6253,0x5927,
+ 0x5446,0x6b79,0x50a3,0x6234,0x5e26,0x6b86,0x4ee3,0x8d37,0x888b,0x5f85,
+ 0x902e
+ },
+ { /* ku 35 */
+ 0x790d,0x790e,0x790f,0x7910,0x7911,0x7912,0x7914,0x7915,0x7916,0x7917,
+ 0x7918,0x7919,0x791a,0x791b,0x791c,0x791d,0x791f,0x7920,0x7921,0x7922,
+ 0x7923,0x7925,0x7926,0x7927,0x7928,0x7929,0x792a,0x792b,0x792c,0x792d,
+ 0x792e,0x792f,0x7930,0x7931,0x7932,0x7933,0x7935,0x7936,0x7937,0x7938,
+ 0x7939,0x793d,0x793f,0x7942,0x7943,0x7944,0x7945,0x7947,0x794a,0x794b,
+ 0x794c,0x794d,0x794e,0x794f,0x7950,0x7951,0x7952,0x7954,0x7955,0x7958,
+ 0x7959,0x7961,0x7963,UBOGON,0x7964,0x7966,0x7969,0x796a,0x796b,0x796c,
+ 0x796e,0x7970,0x7971,0x7972,0x7973,0x7974,0x7975,0x7976,0x7979,0x797b,
+ 0x797c,0x797d,0x797e,0x797f,0x7982,0x7983,0x7986,0x7987,0x7988,0x7989,
+ 0x798b,0x798c,0x798d,0x798e,0x7990,0x7991,0x7992,0x6020,0x803d,0x62c5,
+ 0x4e39,0x5355,0x90f8,0x63b8,0x80c6,0x65e6,0x6c2e,0x4f46,0x60ee,0x6de1,
+ 0x8bde,0x5f39,0x86cb,0x5f53,0x6321,0x515a,0x8361,0x6863,0x5200,0x6363,
+ 0x8e48,0x5012,0x5c9b,0x7977,0x5bfc,0x5230,0x7a3b,0x60bc,0x9053,0x76d7,
+ 0x5fb7,0x5f97,0x7684,0x8e6c,0x706f,0x767b,0x7b49,0x77aa,0x51f3,0x9093,
+ 0x5824,0x4f4e,0x6ef4,0x8fea,0x654c,0x7b1b,0x72c4,0x6da4,0x7fdf,0x5ae1,
+ 0x62b5,0x5e95,0x5730,0x8482,0x7b2c,0x5e1d,0x5f1f,0x9012,0x7f14,0x98a0,
+ 0x6382,0x6ec7,0x7898,0x70b9,0x5178,0x975b,0x57ab,0x7535,0x4f43,0x7538,
+ 0x5e97,0x60e6,0x5960,0x6dc0,0x6bbf,0x7889,0x53fc,0x96d5,0x51cb,0x5201,
+ 0x6389,0x540a,0x9493,0x8c03,0x8dcc,0x7239,0x789f,0x8776,0x8fed,0x8c0d,
+ 0x53e0
+ },
+ { /* ku 36 */
+ 0x7993,0x7994,0x7995,0x7996,0x7997,0x7998,0x7999,0x799b,0x799c,0x799d,
+ 0x799e,0x799f,0x79a0,0x79a1,0x79a2,0x79a3,0x79a4,0x79a5,0x79a6,0x79a8,
+ 0x79a9,0x79aa,0x79ab,0x79ac,0x79ad,0x79ae,0x79af,0x79b0,0x79b1,0x79b2,
+ 0x79b4,0x79b5,0x79b6,0x79b7,0x79b8,0x79bc,0x79bf,0x79c2,0x79c4,0x79c5,
+ 0x79c7,0x79c8,0x79ca,0x79cc,0x79ce,0x79cf,0x79d0,0x79d3,0x79d4,0x79d6,
+ 0x79d7,0x79d9,0x79da,0x79db,0x79dc,0x79dd,0x79de,0x79e0,0x79e1,0x79e2,
+ 0x79e5,0x79e8,0x79ea,UBOGON,0x79ec,0x79ee,0x79f1,0x79f2,0x79f3,0x79f4,
+ 0x79f5,0x79f6,0x79f7,0x79f9,0x79fa,0x79fc,0x79fe,0x79ff,0x7a01,0x7a04,
+ 0x7a05,0x7a07,0x7a08,0x7a09,0x7a0a,0x7a0c,0x7a0f,0x7a10,0x7a11,0x7a12,
+ 0x7a13,0x7a15,0x7a16,0x7a18,0x7a19,0x7a1b,0x7a1c,0x4e01,0x76ef,0x53ee,
+ 0x9489,0x9876,0x9f0e,0x952d,0x5b9a,0x8ba2,0x4e22,0x4e1c,0x51ac,0x8463,
+ 0x61c2,0x52a8,0x680b,0x4f97,0x606b,0x51bb,0x6d1e,0x515c,0x6296,0x6597,
+ 0x9661,0x8c46,0x9017,0x75d8,0x90fd,0x7763,0x6bd2,0x728a,0x72ec,0x8bfb,
+ 0x5835,0x7779,0x8d4c,0x675c,0x9540,0x809a,0x5ea6,0x6e21,0x5992,0x7aef,
+ 0x77ed,0x953b,0x6bb5,0x65ad,0x7f0e,0x5806,0x5151,0x961f,0x5bf9,0x58a9,
+ 0x5428,0x8e72,0x6566,0x987f,0x56e4,0x949d,0x76fe,0x9041,0x6387,0x54c6,
+ 0x591a,0x593a,0x579b,0x8eb2,0x6735,0x8dfa,0x8235,0x5241,0x60f0,0x5815,
+ 0x86fe,0x5ce8,0x9e45,0x4fc4,0x989d,0x8bb9,0x5a25,0x6076,0x5384,0x627c,
+ 0x904f,0x9102,0x997f,0x6069,0x800c,0x513f,0x8033,0x5c14,0x9975,0x6d31,
+ 0x4e8c
+ },
+ { /* ku 37 */
+ 0x7a1d,0x7a1f,0x7a21,0x7a22,0x7a24,0x7a25,0x7a26,0x7a27,0x7a28,0x7a29,
+ 0x7a2a,0x7a2b,0x7a2c,0x7a2d,0x7a2e,0x7a2f,0x7a30,0x7a31,0x7a32,0x7a34,
+ 0x7a35,0x7a36,0x7a38,0x7a3a,0x7a3e,0x7a40,0x7a41,0x7a42,0x7a43,0x7a44,
+ 0x7a45,0x7a47,0x7a48,0x7a49,0x7a4a,0x7a4b,0x7a4c,0x7a4d,0x7a4e,0x7a4f,
+ 0x7a50,0x7a52,0x7a53,0x7a54,0x7a55,0x7a56,0x7a58,0x7a59,0x7a5a,0x7a5b,
+ 0x7a5c,0x7a5d,0x7a5e,0x7a5f,0x7a60,0x7a61,0x7a62,0x7a63,0x7a64,0x7a65,
+ 0x7a66,0x7a67,0x7a68,UBOGON,0x7a69,0x7a6a,0x7a6b,0x7a6c,0x7a6d,0x7a6e,
+ 0x7a6f,0x7a71,0x7a72,0x7a73,0x7a75,0x7a7b,0x7a7c,0x7a7d,0x7a7e,0x7a82,
+ 0x7a85,0x7a87,0x7a89,0x7a8a,0x7a8b,0x7a8c,0x7a8e,0x7a8f,0x7a90,0x7a93,
+ 0x7a94,0x7a99,0x7a9a,0x7a9b,0x7a9e,0x7aa1,0x7aa2,0x8d30,0x53d1,0x7f5a,
+ 0x7b4f,0x4f10,0x4e4f,0x9600,0x6cd5,0x73d0,0x85e9,0x5e06,0x756a,0x7ffb,
+ 0x6a0a,0x77fe,0x9492,0x7e41,0x51e1,0x70e6,0x53cd,0x8fd4,0x8303,0x8d29,
+ 0x72af,0x996d,0x6cdb,0x574a,0x82b3,0x65b9,0x80aa,0x623f,0x9632,0x59a8,
+ 0x4eff,0x8bbf,0x7eba,0x653e,0x83f2,0x975e,0x5561,0x98de,0x80a5,0x532a,
+ 0x8bfd,0x5420,0x80ba,0x5e9f,0x6cb8,0x8d39,0x82ac,0x915a,0x5429,0x6c1b,
+ 0x5206,0x7eb7,0x575f,0x711a,0x6c7e,0x7c89,0x594b,0x4efd,0x5fff,0x6124,
+ 0x7caa,0x4e30,0x5c01,0x67ab,0x8702,0x5cf0,0x950b,0x98ce,0x75af,0x70fd,
+ 0x9022,0x51af,0x7f1d,0x8bbd,0x5949,0x51e4,0x4f5b,0x5426,0x592b,0x6577,
+ 0x80a4,0x5b75,0x6276,0x62c2,0x8f90,0x5e45,0x6c1f,0x7b26,0x4f0f,0x4fd8,
+ 0x670d
+ },
+ { /* ku 38 */
+ 0x7aa3,0x7aa4,0x7aa7,0x7aa9,0x7aaa,0x7aab,0x7aae,0x7aaf,0x7ab0,0x7ab1,
+ 0x7ab2,0x7ab4,0x7ab5,0x7ab6,0x7ab7,0x7ab8,0x7ab9,0x7aba,0x7abb,0x7abc,
+ 0x7abd,0x7abe,0x7ac0,0x7ac1,0x7ac2,0x7ac3,0x7ac4,0x7ac5,0x7ac6,0x7ac7,
+ 0x7ac8,0x7ac9,0x7aca,0x7acc,0x7acd,0x7ace,0x7acf,0x7ad0,0x7ad1,0x7ad2,
+ 0x7ad3,0x7ad4,0x7ad5,0x7ad7,0x7ad8,0x7ada,0x7adb,0x7adc,0x7add,0x7ae1,
+ 0x7ae2,0x7ae4,0x7ae7,0x7ae8,0x7ae9,0x7aea,0x7aeb,0x7aec,0x7aee,0x7af0,
+ 0x7af1,0x7af2,0x7af3,UBOGON,0x7af4,0x7af5,0x7af6,0x7af7,0x7af8,0x7afb,
+ 0x7afc,0x7afe,0x7b00,0x7b01,0x7b02,0x7b05,0x7b07,0x7b09,0x7b0c,0x7b0d,
+ 0x7b0e,0x7b10,0x7b12,0x7b13,0x7b16,0x7b17,0x7b18,0x7b1a,0x7b1c,0x7b1d,
+ 0x7b1f,0x7b21,0x7b22,0x7b23,0x7b27,0x7b29,0x7b2d,0x6d6e,0x6daa,0x798f,
+ 0x88b1,0x5f17,0x752b,0x629a,0x8f85,0x4fef,0x91dc,0x65a7,0x812f,0x8151,
+ 0x5e9c,0x8150,0x8d74,0x526f,0x8986,0x8d4b,0x590d,0x5085,0x4ed8,0x961c,
+ 0x7236,0x8179,0x8d1f,0x5bcc,0x8ba3,0x9644,0x5987,0x7f1a,0x5490,0x5676,
+ 0x560e,0x8be5,0x6539,0x6982,0x9499,0x76d6,0x6e89,0x5e72,0x7518,0x6746,
+ 0x67d1,0x7aff,0x809d,0x8d76,0x611f,0x79c6,0x6562,0x8d63,0x5188,0x521a,
+ 0x94a2,0x7f38,0x809b,0x7eb2,0x5c97,0x6e2f,0x6760,0x7bd9,0x768b,0x9ad8,
+ 0x818f,0x7f94,0x7cd5,0x641e,0x9550,0x7a3f,0x544a,0x54e5,0x6b4c,0x6401,
+ 0x6208,0x9e3d,0x80f3,0x7599,0x5272,0x9769,0x845b,0x683c,0x86e4,0x9601,
+ 0x9694,0x94ec,0x4e2a,0x5404,0x7ed9,0x6839,0x8ddf,0x8015,0x66f4,0x5e9a,
+ 0x7fb9
+ },
+ { /* ku 39 */
+ 0x7b2f,0x7b30,0x7b32,0x7b34,0x7b35,0x7b36,0x7b37,0x7b39,0x7b3b,0x7b3d,
+ 0x7b3f,0x7b40,0x7b41,0x7b42,0x7b43,0x7b44,0x7b46,0x7b48,0x7b4a,0x7b4d,
+ 0x7b4e,0x7b53,0x7b55,0x7b57,0x7b59,0x7b5c,0x7b5e,0x7b5f,0x7b61,0x7b63,
+ 0x7b64,0x7b65,0x7b66,0x7b67,0x7b68,0x7b69,0x7b6a,0x7b6b,0x7b6c,0x7b6d,
+ 0x7b6f,0x7b70,0x7b73,0x7b74,0x7b76,0x7b78,0x7b7a,0x7b7c,0x7b7d,0x7b7f,
+ 0x7b81,0x7b82,0x7b83,0x7b84,0x7b86,0x7b87,0x7b88,0x7b89,0x7b8a,0x7b8b,
+ 0x7b8c,0x7b8e,0x7b8f,UBOGON,0x7b91,0x7b92,0x7b93,0x7b96,0x7b98,0x7b99,
+ 0x7b9a,0x7b9b,0x7b9e,0x7b9f,0x7ba0,0x7ba3,0x7ba4,0x7ba5,0x7bae,0x7baf,
+ 0x7bb0,0x7bb2,0x7bb3,0x7bb5,0x7bb6,0x7bb7,0x7bb9,0x7bba,0x7bbb,0x7bbc,
+ 0x7bbd,0x7bbe,0x7bbf,0x7bc0,0x7bc2,0x7bc3,0x7bc4,0x57c2,0x803f,0x6897,
+ 0x5de5,0x653b,0x529f,0x606d,0x9f9a,0x4f9b,0x8eac,0x516c,0x5bab,0x5f13,
+ 0x5de9,0x6c5e,0x62f1,0x8d21,0x5171,0x94a9,0x52fe,0x6c9f,0x82df,0x72d7,
+ 0x57a2,0x6784,0x8d2d,0x591f,0x8f9c,0x83c7,0x5495,0x7b8d,0x4f30,0x6cbd,
+ 0x5b64,0x59d1,0x9f13,0x53e4,0x86ca,0x9aa8,0x8c37,0x80a1,0x6545,0x987e,
+ 0x56fa,0x96c7,0x522e,0x74dc,0x5250,0x5be1,0x6302,0x8902,0x4e56,0x62d0,
+ 0x602a,0x68fa,0x5173,0x5b98,0x51a0,0x89c2,0x7ba1,0x9986,0x7f50,0x60ef,
+ 0x704c,0x8d2f,0x5149,0x5e7f,0x901b,0x7470,0x89c4,0x572d,0x7845,0x5f52,
+ 0x9f9f,0x95fa,0x8f68,0x9b3c,0x8be1,0x7678,0x6842,0x67dc,0x8dea,0x8d35,
+ 0x523d,0x8f8a,0x6eda,0x68cd,0x9505,0x90ed,0x56fd,0x679c,0x88f9,0x8fc7,
+ 0x54c8
+ },
+ { /* ku 3a */
+ 0x7bc5,0x7bc8,0x7bc9,0x7bca,0x7bcb,0x7bcd,0x7bce,0x7bcf,0x7bd0,0x7bd2,
+ 0x7bd4,0x7bd5,0x7bd6,0x7bd7,0x7bd8,0x7bdb,0x7bdc,0x7bde,0x7bdf,0x7be0,
+ 0x7be2,0x7be3,0x7be4,0x7be7,0x7be8,0x7be9,0x7beb,0x7bec,0x7bed,0x7bef,
+ 0x7bf0,0x7bf2,0x7bf3,0x7bf4,0x7bf5,0x7bf6,0x7bf8,0x7bf9,0x7bfa,0x7bfb,
+ 0x7bfd,0x7bff,0x7c00,0x7c01,0x7c02,0x7c03,0x7c04,0x7c05,0x7c06,0x7c08,
+ 0x7c09,0x7c0a,0x7c0d,0x7c0e,0x7c10,0x7c11,0x7c12,0x7c13,0x7c14,0x7c15,
+ 0x7c17,0x7c18,0x7c19,UBOGON,0x7c1a,0x7c1b,0x7c1c,0x7c1d,0x7c1e,0x7c20,
+ 0x7c21,0x7c22,0x7c23,0x7c24,0x7c25,0x7c28,0x7c29,0x7c2b,0x7c2c,0x7c2d,
+ 0x7c2e,0x7c2f,0x7c30,0x7c31,0x7c32,0x7c33,0x7c34,0x7c35,0x7c36,0x7c37,
+ 0x7c39,0x7c3a,0x7c3b,0x7c3c,0x7c3d,0x7c3e,0x7c42,0x9ab8,0x5b69,0x6d77,
+ 0x6c26,0x4ea5,0x5bb3,0x9a87,0x9163,0x61a8,0x90af,0x97e9,0x542b,0x6db5,
+ 0x5bd2,0x51fd,0x558a,0x7f55,0x7ff0,0x64bc,0x634d,0x65f1,0x61be,0x608d,
+ 0x710a,0x6c57,0x6c49,0x592f,0x676d,0x822a,0x58d5,0x568e,0x8c6a,0x6beb,
+ 0x90dd,0x597d,0x8017,0x53f7,0x6d69,0x5475,0x559d,0x8377,0x83cf,0x6838,
+ 0x79be,0x548c,0x4f55,0x5408,0x76d2,0x8c89,0x9602,0x6cb3,0x6db8,0x8d6b,
+ 0x8910,0x9e64,0x8d3a,0x563f,0x9ed1,0x75d5,0x5f88,0x72e0,0x6068,0x54fc,
+ 0x4ea8,0x6a2a,0x8861,0x6052,0x8f70,0x54c4,0x70d8,0x8679,0x9e3f,0x6d2a,
+ 0x5b8f,0x5f18,0x7ea2,0x5589,0x4faf,0x7334,0x543c,0x539a,0x5019,0x540e,
+ 0x547c,0x4e4e,0x5ffd,0x745a,0x58f6,0x846b,0x80e1,0x8774,0x72d0,0x7cca,
+ 0x6e56
+ },
+ { /* ku 3b */
+ 0x7c43,0x7c44,0x7c45,0x7c46,0x7c47,0x7c48,0x7c49,0x7c4a,0x7c4b,0x7c4c,
+ 0x7c4e,0x7c4f,0x7c50,0x7c51,0x7c52,0x7c53,0x7c54,0x7c55,0x7c56,0x7c57,
+ 0x7c58,0x7c59,0x7c5a,0x7c5b,0x7c5c,0x7c5d,0x7c5e,0x7c5f,0x7c60,0x7c61,
+ 0x7c62,0x7c63,0x7c64,0x7c65,0x7c66,0x7c67,0x7c68,0x7c69,0x7c6a,0x7c6b,
+ 0x7c6c,0x7c6d,0x7c6e,0x7c6f,0x7c70,0x7c71,0x7c72,0x7c75,0x7c76,0x7c77,
+ 0x7c78,0x7c79,0x7c7a,0x7c7e,0x7c7f,0x7c80,0x7c81,0x7c82,0x7c83,0x7c84,
+ 0x7c85,0x7c86,0x7c87,UBOGON,0x7c88,0x7c8a,0x7c8b,0x7c8c,0x7c8d,0x7c8e,
+ 0x7c8f,0x7c90,0x7c93,0x7c94,0x7c96,0x7c99,0x7c9a,0x7c9b,0x7ca0,0x7ca1,
+ 0x7ca3,0x7ca6,0x7ca7,0x7ca8,0x7ca9,0x7cab,0x7cac,0x7cad,0x7caf,0x7cb0,
+ 0x7cb4,0x7cb5,0x7cb6,0x7cb7,0x7cb8,0x7cba,0x7cbb,0x5f27,0x864e,0x552c,
+ 0x62a4,0x4e92,0x6caa,0x6237,0x82b1,0x54d7,0x534e,0x733e,0x6ed1,0x753b,
+ 0x5212,0x5316,0x8bdd,0x69d0,0x5f8a,0x6000,0x6dee,0x574f,0x6b22,0x73af,
+ 0x6853,0x8fd8,0x7f13,0x6362,0x60a3,0x5524,0x75ea,0x8c62,0x7115,0x6da3,
+ 0x5ba6,0x5e7b,0x8352,0x614c,0x9ec4,0x78fa,0x8757,0x7c27,0x7687,0x51f0,
+ 0x60f6,0x714c,0x6643,0x5e4c,0x604d,0x8c0e,0x7070,0x6325,0x8f89,0x5fbd,
+ 0x6062,0x86d4,0x56de,0x6bc1,0x6094,0x6167,0x5349,0x60e0,0x6666,0x8d3f,
+ 0x79fd,0x4f1a,0x70e9,0x6c47,0x8bb3,0x8bf2,0x7ed8,0x8364,0x660f,0x5a5a,
+ 0x9b42,0x6d51,0x6df7,0x8c41,0x6d3b,0x4f19,0x706b,0x83b7,0x6216,0x60d1,
+ 0x970d,0x8d27,0x7978,0x51fb,0x573e,0x57fa,0x673a,0x7578,0x7a3d,0x79ef,
+ 0x7b95
+ },
+ { /* ku 3c */
+ 0x7cbf,0x7cc0,0x7cc2,0x7cc3,0x7cc4,0x7cc6,0x7cc9,0x7ccb,0x7cce,0x7ccf,
+ 0x7cd0,0x7cd1,0x7cd2,0x7cd3,0x7cd4,0x7cd8,0x7cda,0x7cdb,0x7cdd,0x7cde,
+ 0x7ce1,0x7ce2,0x7ce3,0x7ce4,0x7ce5,0x7ce6,0x7ce7,0x7ce9,0x7cea,0x7ceb,
+ 0x7cec,0x7ced,0x7cee,0x7cf0,0x7cf1,0x7cf2,0x7cf3,0x7cf4,0x7cf5,0x7cf6,
+ 0x7cf7,0x7cf9,0x7cfa,0x7cfc,0x7cfd,0x7cfe,0x7cff,0x7d00,0x7d01,0x7d02,
+ 0x7d03,0x7d04,0x7d05,0x7d06,0x7d07,0x7d08,0x7d09,0x7d0b,0x7d0c,0x7d0d,
+ 0x7d0e,0x7d0f,0x7d10,UBOGON,0x7d11,0x7d12,0x7d13,0x7d14,0x7d15,0x7d16,
+ 0x7d17,0x7d18,0x7d19,0x7d1a,0x7d1b,0x7d1c,0x7d1d,0x7d1e,0x7d1f,0x7d21,
+ 0x7d23,0x7d24,0x7d25,0x7d26,0x7d28,0x7d29,0x7d2a,0x7d2c,0x7d2d,0x7d2e,
+ 0x7d30,0x7d31,0x7d32,0x7d33,0x7d34,0x7d35,0x7d36,0x808c,0x9965,0x8ff9,
+ 0x6fc0,0x8ba5,0x9e21,0x59ec,0x7ee9,0x7f09,0x5409,0x6781,0x68d8,0x8f91,
+ 0x7c4d,0x96c6,0x53ca,0x6025,0x75be,0x6c72,0x5373,0x5ac9,0x7ea7,0x6324,
+ 0x51e0,0x810a,0x5df1,0x84df,0x6280,0x5180,0x5b63,0x4f0e,0x796d,0x5242,
+ 0x60b8,0x6d4e,0x5bc4,0x5bc2,0x8ba1,0x8bb0,0x65e2,0x5fcc,0x9645,0x5993,
+ 0x7ee7,0x7eaa,0x5609,0x67b7,0x5939,0x4f73,0x5bb6,0x52a0,0x835a,0x988a,
+ 0x8d3e,0x7532,0x94be,0x5047,0x7a3c,0x4ef7,0x67b6,0x9a7e,0x5ac1,0x6b7c,
+ 0x76d1,0x575a,0x5c16,0x7b3a,0x95f4,0x714e,0x517c,0x80a9,0x8270,0x5978,
+ 0x7f04,0x8327,0x68c0,0x67ec,0x78b1,0x7877,0x62e3,0x6361,0x7b80,0x4fed,
+ 0x526a,0x51cf,0x8350,0x69db,0x9274,0x8df5,0x8d31,0x89c1,0x952e,0x7bad,
+ 0x4ef6
+ },
+ { /* ku 3d */
+ 0x7d37,0x7d38,0x7d39,0x7d3a,0x7d3b,0x7d3c,0x7d3d,0x7d3e,0x7d3f,0x7d40,
+ 0x7d41,0x7d42,0x7d43,0x7d44,0x7d45,0x7d46,0x7d47,0x7d48,0x7d49,0x7d4a,
+ 0x7d4b,0x7d4c,0x7d4d,0x7d4e,0x7d4f,0x7d50,0x7d51,0x7d52,0x7d53,0x7d54,
+ 0x7d55,0x7d56,0x7d57,0x7d58,0x7d59,0x7d5a,0x7d5b,0x7d5c,0x7d5d,0x7d5e,
+ 0x7d5f,0x7d60,0x7d61,0x7d62,0x7d63,0x7d64,0x7d65,0x7d66,0x7d67,0x7d68,
+ 0x7d69,0x7d6a,0x7d6b,0x7d6c,0x7d6d,0x7d6f,0x7d70,0x7d71,0x7d72,0x7d73,
+ 0x7d74,0x7d75,0x7d76,UBOGON,0x7d78,0x7d79,0x7d7a,0x7d7b,0x7d7c,0x7d7d,
+ 0x7d7e,0x7d7f,0x7d80,0x7d81,0x7d82,0x7d83,0x7d84,0x7d85,0x7d86,0x7d87,
+ 0x7d88,0x7d89,0x7d8a,0x7d8b,0x7d8c,0x7d8d,0x7d8e,0x7d8f,0x7d90,0x7d91,
+ 0x7d92,0x7d93,0x7d94,0x7d95,0x7d96,0x7d97,0x7d98,0x5065,0x8230,0x5251,
+ 0x996f,0x6e10,0x6e85,0x6da7,0x5efa,0x50f5,0x59dc,0x5c06,0x6d46,0x6c5f,
+ 0x7586,0x848b,0x6868,0x5956,0x8bb2,0x5320,0x9171,0x964d,0x8549,0x6912,
+ 0x7901,0x7126,0x80f6,0x4ea4,0x90ca,0x6d47,0x9a84,0x5a07,0x56bc,0x6405,
+ 0x94f0,0x77eb,0x4fa5,0x811a,0x72e1,0x89d2,0x997a,0x7f34,0x7ede,0x527f,
+ 0x6559,0x9175,0x8f7f,0x8f83,0x53eb,0x7a96,0x63ed,0x63a5,0x7686,0x79f8,
+ 0x8857,0x9636,0x622a,0x52ab,0x8282,0x6854,0x6770,0x6377,0x776b,0x7aed,
+ 0x6d01,0x7ed3,0x89e3,0x59d0,0x6212,0x85c9,0x82a5,0x754c,0x501f,0x4ecb,
+ 0x75a5,0x8beb,0x5c4a,0x5dfe,0x7b4b,0x65a4,0x91d1,0x4eca,0x6d25,0x895f,
+ 0x7d27,0x9526,0x4ec5,0x8c28,0x8fdb,0x9773,0x664b,0x7981,0x8fd1,0x70ec,
+ 0x6d78
+ },
+ { /* ku 3e */
+ 0x7d99,0x7d9a,0x7d9b,0x7d9c,0x7d9d,0x7d9e,0x7d9f,0x7da0,0x7da1,0x7da2,
+ 0x7da3,0x7da4,0x7da5,0x7da7,0x7da8,0x7da9,0x7daa,0x7dab,0x7dac,0x7dad,
+ 0x7daf,0x7db0,0x7db1,0x7db2,0x7db3,0x7db4,0x7db5,0x7db6,0x7db7,0x7db8,
+ 0x7db9,0x7dba,0x7dbb,0x7dbc,0x7dbd,0x7dbe,0x7dbf,0x7dc0,0x7dc1,0x7dc2,
+ 0x7dc3,0x7dc4,0x7dc5,0x7dc6,0x7dc7,0x7dc8,0x7dc9,0x7dca,0x7dcb,0x7dcc,
+ 0x7dcd,0x7dce,0x7dcf,0x7dd0,0x7dd1,0x7dd2,0x7dd3,0x7dd4,0x7dd5,0x7dd6,
+ 0x7dd7,0x7dd8,0x7dd9,UBOGON,0x7dda,0x7ddb,0x7ddc,0x7ddd,0x7dde,0x7ddf,
+ 0x7de0,0x7de1,0x7de2,0x7de3,0x7de4,0x7de5,0x7de6,0x7de7,0x7de8,0x7de9,
+ 0x7dea,0x7deb,0x7dec,0x7ded,0x7dee,0x7def,0x7df0,0x7df1,0x7df2,0x7df3,
+ 0x7df4,0x7df5,0x7df6,0x7df7,0x7df8,0x7df9,0x7dfa,0x5c3d,0x52b2,0x8346,
+ 0x5162,0x830e,0x775b,0x6676,0x9cb8,0x4eac,0x60ca,0x7cbe,0x7cb3,0x7ecf,
+ 0x4e95,0x8b66,0x666f,0x9888,0x9759,0x5883,0x656c,0x955c,0x5f84,0x75c9,
+ 0x9756,0x7adf,0x7ade,0x51c0,0x70af,0x7a98,0x63ea,0x7a76,0x7ea0,0x7396,
+ 0x97ed,0x4e45,0x7078,0x4e5d,0x9152,0x53a9,0x6551,0x65e7,0x81fc,0x8205,
+ 0x548e,0x5c31,0x759a,0x97a0,0x62d8,0x72d9,0x75bd,0x5c45,0x9a79,0x83ca,
+ 0x5c40,0x5480,0x77e9,0x4e3e,0x6cae,0x805a,0x62d2,0x636e,0x5de8,0x5177,
+ 0x8ddd,0x8e1e,0x952f,0x4ff1,0x53e5,0x60e7,0x70ac,0x5267,0x6350,0x9e43,
+ 0x5a1f,0x5026,0x7737,0x5377,0x7ee2,0x6485,0x652b,0x6289,0x6398,0x5014,
+ 0x7235,0x89c9,0x51b3,0x8bc0,0x7edd,0x5747,0x83cc,0x94a7,0x519b,0x541b,
+ 0x5cfb
+ },
+ { /* ku 3f */
+ 0x7dfb,0x7dfc,0x7dfd,0x7dfe,0x7dff,0x7e00,0x7e01,0x7e02,0x7e03,0x7e04,
+ 0x7e05,0x7e06,0x7e07,0x7e08,0x7e09,0x7e0a,0x7e0b,0x7e0c,0x7e0d,0x7e0e,
+ 0x7e0f,0x7e10,0x7e11,0x7e12,0x7e13,0x7e14,0x7e15,0x7e16,0x7e17,0x7e18,
+ 0x7e19,0x7e1a,0x7e1b,0x7e1c,0x7e1d,0x7e1e,0x7e1f,0x7e20,0x7e21,0x7e22,
+ 0x7e23,0x7e24,0x7e25,0x7e26,0x7e27,0x7e28,0x7e29,0x7e2a,0x7e2b,0x7e2c,
+ 0x7e2d,0x7e2e,0x7e2f,0x7e30,0x7e31,0x7e32,0x7e33,0x7e34,0x7e35,0x7e36,
+ 0x7e37,0x7e38,0x7e39,UBOGON,0x7e3a,0x7e3c,0x7e3d,0x7e3e,0x7e3f,0x7e40,
+ 0x7e42,0x7e43,0x7e44,0x7e45,0x7e46,0x7e48,0x7e49,0x7e4a,0x7e4b,0x7e4c,
+ 0x7e4d,0x7e4e,0x7e4f,0x7e50,0x7e51,0x7e52,0x7e53,0x7e54,0x7e55,0x7e56,
+ 0x7e57,0x7e58,0x7e59,0x7e5a,0x7e5b,0x7e5c,0x7e5d,0x4fca,0x7ae3,0x6d5a,
+ 0x90e1,0x9a8f,0x5580,0x5496,0x5361,0x54af,0x5f00,0x63e9,0x6977,0x51ef,
+ 0x6168,0x520a,0x582a,0x52d8,0x574e,0x780d,0x770b,0x5eb7,0x6177,0x7ce0,
+ 0x625b,0x6297,0x4ea2,0x7095,0x8003,0x62f7,0x70e4,0x9760,0x5777,0x82db,
+ 0x67ef,0x68f5,0x78d5,0x9897,0x79d1,0x58f3,0x54b3,0x53ef,0x6e34,0x514b,
+ 0x523b,0x5ba2,0x8bfe,0x80af,0x5543,0x57a6,0x6073,0x5751,0x542d,0x7a7a,
+ 0x6050,0x5b54,0x63a7,0x62a0,0x53e3,0x6263,0x5bc7,0x67af,0x54ed,0x7a9f,
+ 0x82e6,0x9177,0x5e93,0x88e4,0x5938,0x57ae,0x630e,0x8de8,0x80ef,0x5757,
+ 0x7b77,0x4fa9,0x5feb,0x5bbd,0x6b3e,0x5321,0x7b50,0x72c2,0x6846,0x77ff,
+ 0x7736,0x65f7,0x51b5,0x4e8f,0x76d4,0x5cbf,0x7aa5,0x8475,0x594e,0x9b41,
+ 0x5080
+ },
+ { /* ku 40 */
+ 0x7e5e,0x7e5f,0x7e60,0x7e61,0x7e62,0x7e63,0x7e64,0x7e65,0x7e66,0x7e67,
+ 0x7e68,0x7e69,0x7e6a,0x7e6b,0x7e6c,0x7e6d,0x7e6e,0x7e6f,0x7e70,0x7e71,
+ 0x7e72,0x7e73,0x7e74,0x7e75,0x7e76,0x7e77,0x7e78,0x7e79,0x7e7a,0x7e7b,
+ 0x7e7c,0x7e7d,0x7e7e,0x7e7f,0x7e80,0x7e81,0x7e83,0x7e84,0x7e85,0x7e86,
+ 0x7e87,0x7e88,0x7e89,0x7e8a,0x7e8b,0x7e8c,0x7e8d,0x7e8e,0x7e8f,0x7e90,
+ 0x7e91,0x7e92,0x7e93,0x7e94,0x7e95,0x7e96,0x7e97,0x7e98,0x7e99,0x7e9a,
+ 0x7e9c,0x7e9d,0x7e9e,UBOGON,0x7eae,0x7eb4,0x7ebb,0x7ebc,0x7ed6,0x7ee4,
+ 0x7eec,0x7ef9,0x7f0a,0x7f10,0x7f1e,0x7f37,0x7f39,0x7f3b,0x7f3c,0x7f3d,
+ 0x7f3e,0x7f3f,0x7f40,0x7f41,0x7f43,0x7f46,0x7f47,0x7f48,0x7f49,0x7f4a,
+ 0x7f4b,0x7f4c,0x7f4d,0x7f4e,0x7f4f,0x7f52,0x7f53,0x9988,0x6127,0x6e83,
+ 0x5764,0x6606,0x6346,0x56f0,0x62ec,0x6269,0x5ed3,0x9614,0x5783,0x62c9,
+ 0x5587,0x8721,0x814a,0x8fa3,0x5566,0x83b1,0x6765,0x8d56,0x84dd,0x5a6a,
+ 0x680f,0x62e6,0x7bee,0x9611,0x5170,0x6f9c,0x8c30,0x63fd,0x89c8,0x61d2,
+ 0x7f06,0x70c2,0x6ee5,0x7405,0x6994,0x72fc,0x5eca,0x90ce,0x6717,0x6d6a,
+ 0x635e,0x52b3,0x7262,0x8001,0x4f6c,0x59e5,0x916a,0x70d9,0x6d9d,0x52d2,
+ 0x4e50,0x96f7,0x956d,0x857e,0x78ca,0x7d2f,0x5121,0x5792,0x64c2,0x808b,
+ 0x7c7b,0x6cea,0x68f1,0x695e,0x51b7,0x5398,0x68a8,0x7281,0x9ece,0x7bf1,
+ 0x72f8,0x79bb,0x6f13,0x7406,0x674e,0x91cc,0x9ca4,0x793c,0x8389,0x8354,
+ 0x540f,0x6817,0x4e3d,0x5389,0x52b1,0x783e,0x5386,0x5229,0x5088,0x4f8b,
+ 0x4fd0
+ },
+ { /* ku 41 */
+ 0x7f56,0x7f59,0x7f5b,0x7f5c,0x7f5d,0x7f5e,0x7f60,0x7f63,0x7f64,0x7f65,
+ 0x7f66,0x7f67,0x7f6b,0x7f6c,0x7f6d,0x7f6f,0x7f70,0x7f73,0x7f75,0x7f76,
+ 0x7f77,0x7f78,0x7f7a,0x7f7b,0x7f7c,0x7f7d,0x7f7f,0x7f80,0x7f82,0x7f83,
+ 0x7f84,0x7f85,0x7f86,0x7f87,0x7f88,0x7f89,0x7f8b,0x7f8d,0x7f8f,0x7f90,
+ 0x7f91,0x7f92,0x7f93,0x7f95,0x7f96,0x7f97,0x7f98,0x7f99,0x7f9b,0x7f9c,
+ 0x7fa0,0x7fa2,0x7fa3,0x7fa5,0x7fa6,0x7fa8,0x7fa9,0x7faa,0x7fab,0x7fac,
+ 0x7fad,0x7fae,0x7fb1,UBOGON,0x7fb3,0x7fb4,0x7fb5,0x7fb6,0x7fb7,0x7fba,
+ 0x7fbb,0x7fbe,0x7fc0,0x7fc2,0x7fc3,0x7fc4,0x7fc6,0x7fc7,0x7fc8,0x7fc9,
+ 0x7fcb,0x7fcd,0x7fcf,0x7fd0,0x7fd1,0x7fd2,0x7fd3,0x7fd6,0x7fd7,0x7fd9,
+ 0x7fda,0x7fdb,0x7fdc,0x7fdd,0x7fde,0x7fe2,0x7fe3,0x75e2,0x7acb,0x7c92,
+ 0x6ca5,0x96b6,0x529b,0x7483,0x54e9,0x4fe9,0x8054,0x83b2,0x8fde,0x9570,
+ 0x5ec9,0x601c,0x6d9f,0x5e18,0x655b,0x8138,0x94fe,0x604b,0x70bc,0x7ec3,
+ 0x7cae,0x51c9,0x6881,0x7cb1,0x826f,0x4e24,0x8f86,0x91cf,0x667e,0x4eae,
+ 0x8c05,0x64a9,0x804a,0x50da,0x7597,0x71ce,0x5be5,0x8fbd,0x6f66,0x4e86,
+ 0x6482,0x9563,0x5ed6,0x6599,0x5217,0x88c2,0x70c8,0x52a3,0x730e,0x7433,
+ 0x6797,0x78f7,0x9716,0x4e34,0x90bb,0x9cde,0x6dcb,0x51db,0x8d41,0x541d,
+ 0x62ce,0x73b2,0x83f1,0x96f6,0x9f84,0x94c3,0x4f36,0x7f9a,0x51cc,0x7075,
+ 0x9675,0x5cad,0x9886,0x53e6,0x4ee4,0x6e9c,0x7409,0x69b4,0x786b,0x998f,
+ 0x7559,0x5218,0x7624,0x6d41,0x67f3,0x516d,0x9f99,0x804b,0x5499,0x7b3c,
+ 0x7abf
+ },
+ { /* ku 42 */
+ 0x7fe4,0x7fe7,0x7fe8,0x7fea,0x7feb,0x7fec,0x7fed,0x7fef,0x7ff2,0x7ff4,
+ 0x7ff5,0x7ff6,0x7ff7,0x7ff8,0x7ff9,0x7ffa,0x7ffd,0x7ffe,0x7fff,0x8002,
+ 0x8007,0x8008,0x8009,0x800a,0x800e,0x800f,0x8011,0x8013,0x801a,0x801b,
+ 0x801d,0x801e,0x801f,0x8021,0x8023,0x8024,0x802b,0x802c,0x802d,0x802e,
+ 0x802f,0x8030,0x8032,0x8034,0x8039,0x803a,0x803c,0x803e,0x8040,0x8041,
+ 0x8044,0x8045,0x8047,0x8048,0x8049,0x804e,0x804f,0x8050,0x8051,0x8053,
+ 0x8055,0x8056,0x8057,UBOGON,0x8059,0x805b,0x805c,0x805d,0x805e,0x805f,
+ 0x8060,0x8061,0x8062,0x8063,0x8064,0x8065,0x8066,0x8067,0x8068,0x806b,
+ 0x806c,0x806d,0x806e,0x806f,0x8070,0x8072,0x8073,0x8074,0x8075,0x8076,
+ 0x8077,0x8078,0x8079,0x807a,0x807b,0x807c,0x807d,0x9686,0x5784,0x62e2,
+ 0x9647,0x697c,0x5a04,0x6402,0x7bd3,0x6f0f,0x964b,0x82a6,0x5362,0x9885,
+ 0x5e90,0x7089,0x63b3,0x5364,0x864f,0x9c81,0x9e93,0x788c,0x9732,0x8def,
+ 0x8d42,0x9e7f,0x6f5e,0x7984,0x5f55,0x9646,0x622e,0x9a74,0x5415,0x94dd,
+ 0x4fa3,0x65c5,0x5c65,0x5c61,0x7f15,0x8651,0x6c2f,0x5f8b,0x7387,0x6ee4,
+ 0x7eff,0x5ce6,0x631b,0x5b6a,0x6ee6,0x5375,0x4e71,0x63a0,0x7565,0x62a1,
+ 0x8f6e,0x4f26,0x4ed1,0x6ca6,0x7eb6,0x8bba,0x841d,0x87ba,0x7f57,0x903b,
+ 0x9523,0x7ba9,0x9aa1,0x88f8,0x843d,0x6d1b,0x9a86,0x7edc,0x5988,0x9ebb,
+ 0x739b,0x7801,0x8682,0x9a6c,0x9a82,0x561b,0x5417,0x57cb,0x4e70,0x9ea6,
+ 0x5356,0x8fc8,0x8109,0x7792,0x9992,0x86ee,0x6ee1,0x8513,0x66fc,0x6162,
+ 0x6f2b
+ },
+ { /* ku 43 */
+ 0x807e,0x8081,0x8082,0x8085,0x8088,0x808a,0x808d,0x808e,0x808f,0x8090,
+ 0x8091,0x8092,0x8094,0x8095,0x8097,0x8099,0x809e,0x80a3,0x80a6,0x80a7,
+ 0x80a8,0x80ac,0x80b0,0x80b3,0x80b5,0x80b6,0x80b8,0x80b9,0x80bb,0x80c5,
+ 0x80c7,0x80c8,0x80c9,0x80ca,0x80cb,0x80cf,0x80d0,0x80d1,0x80d2,0x80d3,
+ 0x80d4,0x80d5,0x80d8,0x80df,0x80e0,0x80e2,0x80e3,0x80e6,0x80ee,0x80f5,
+ 0x80f7,0x80f9,0x80fb,0x80fe,0x80ff,0x8100,0x8101,0x8103,0x8104,0x8105,
+ 0x8107,0x8108,0x810b,UBOGON,0x810c,0x8115,0x8117,0x8119,0x811b,0x811c,
+ 0x811d,0x811f,0x8120,0x8121,0x8122,0x8123,0x8124,0x8125,0x8126,0x8127,
+ 0x8128,0x8129,0x812a,0x812b,0x812d,0x812e,0x8130,0x8133,0x8134,0x8135,
+ 0x8137,0x8139,0x813a,0x813b,0x813c,0x813d,0x813f,0x8c29,0x8292,0x832b,
+ 0x76f2,0x6c13,0x5fd9,0x83bd,0x732b,0x8305,0x951a,0x6bdb,0x77db,0x94c6,
+ 0x536f,0x8302,0x5192,0x5e3d,0x8c8c,0x8d38,0x4e48,0x73ab,0x679a,0x6885,
+ 0x9176,0x9709,0x7164,0x6ca1,0x7709,0x5a92,0x9541,0x6bcf,0x7f8e,0x6627,
+ 0x5bd0,0x59b9,0x5a9a,0x95e8,0x95f7,0x4eec,0x840c,0x8499,0x6aac,0x76df,
+ 0x9530,0x731b,0x68a6,0x5b5f,0x772f,0x919a,0x9761,0x7cdc,0x8ff7,0x8c1c,
+ 0x5f25,0x7c73,0x79d8,0x89c5,0x6ccc,0x871c,0x5bc6,0x5e42,0x68c9,0x7720,
+ 0x7ef5,0x5195,0x514d,0x52c9,0x5a29,0x7f05,0x9762,0x82d7,0x63cf,0x7784,
+ 0x85d0,0x79d2,0x6e3a,0x5e99,0x5999,0x8511,0x706d,0x6c11,0x62bf,0x76bf,
+ 0x654f,0x60af,0x95fd,0x660e,0x879f,0x9e23,0x94ed,0x540d,0x547d,0x8c2c,
+ 0x6478
+ },
+ { /* ku 44 */
+ 0x8140,0x8141,0x8142,0x8143,0x8144,0x8145,0x8147,0x8149,0x814d,0x814e,
+ 0x814f,0x8152,0x8156,0x8157,0x8158,0x815b,0x815c,0x815d,0x815e,0x815f,
+ 0x8161,0x8162,0x8163,0x8164,0x8166,0x8168,0x816a,0x816b,0x816c,0x816f,
+ 0x8172,0x8173,0x8175,0x8176,0x8177,0x8178,0x8181,0x8183,0x8184,0x8185,
+ 0x8186,0x8187,0x8189,0x818b,0x818c,0x818d,0x818e,0x8190,0x8192,0x8193,
+ 0x8194,0x8195,0x8196,0x8197,0x8199,0x819a,0x819e,0x819f,0x81a0,0x81a1,
+ 0x81a2,0x81a4,0x81a5,UBOGON,0x81a7,0x81a9,0x81ab,0x81ac,0x81ad,0x81ae,
+ 0x81af,0x81b0,0x81b1,0x81b2,0x81b4,0x81b5,0x81b6,0x81b7,0x81b8,0x81b9,
+ 0x81bc,0x81bd,0x81be,0x81bf,0x81c4,0x81c5,0x81c7,0x81c8,0x81c9,0x81cb,
+ 0x81cd,0x81ce,0x81cf,0x81d0,0x81d1,0x81d2,0x81d3,0x6479,0x8611,0x6a21,
+ 0x819c,0x78e8,0x6469,0x9b54,0x62b9,0x672b,0x83ab,0x58a8,0x9ed8,0x6cab,
+ 0x6f20,0x5bde,0x964c,0x8c0b,0x725f,0x67d0,0x62c7,0x7261,0x4ea9,0x59c6,
+ 0x6bcd,0x5893,0x66ae,0x5e55,0x52df,0x6155,0x6728,0x76ee,0x7766,0x7267,
+ 0x7a46,0x62ff,0x54ea,0x5450,0x94a0,0x90a3,0x5a1c,0x7eb3,0x6c16,0x4e43,
+ 0x5976,0x8010,0x5948,0x5357,0x7537,0x96be,0x56ca,0x6320,0x8111,0x607c,
+ 0x95f9,0x6dd6,0x5462,0x9981,0x5185,0x5ae9,0x80fd,0x59ae,0x9713,0x502a,
+ 0x6ce5,0x5c3c,0x62df,0x4f60,0x533f,0x817b,0x9006,0x6eba,0x852b,0x62c8,
+ 0x5e74,0x78be,0x64b5,0x637b,0x5ff5,0x5a18,0x917f,0x9e1f,0x5c3f,0x634f,
+ 0x8042,0x5b7d,0x556e,0x954a,0x954d,0x6d85,0x60a8,0x67e0,0x72de,0x51dd,
+ 0x5b81
+ },
+ { /* ku 45 */
+ 0x81d4,0x81d5,0x81d6,0x81d7,0x81d8,0x81d9,0x81da,0x81db,0x81dc,0x81dd,
+ 0x81de,0x81df,0x81e0,0x81e1,0x81e2,0x81e4,0x81e5,0x81e6,0x81e8,0x81e9,
+ 0x81eb,0x81ee,0x81ef,0x81f0,0x81f1,0x81f2,0x81f5,0x81f6,0x81f7,0x81f8,
+ 0x81f9,0x81fa,0x81fd,0x81ff,0x8203,0x8207,0x8208,0x8209,0x820a,0x820b,
+ 0x820e,0x820f,0x8211,0x8213,0x8215,0x8216,0x8217,0x8218,0x8219,0x821a,
+ 0x821d,0x8220,0x8224,0x8225,0x8226,0x8227,0x8229,0x822e,0x8232,0x823a,
+ 0x823c,0x823d,0x823f,UBOGON,0x8240,0x8241,0x8242,0x8243,0x8245,0x8246,
+ 0x8248,0x824a,0x824c,0x824d,0x824e,0x8250,0x8251,0x8252,0x8253,0x8254,
+ 0x8255,0x8256,0x8257,0x8259,0x825b,0x825c,0x825d,0x825e,0x8260,0x8261,
+ 0x8262,0x8263,0x8264,0x8265,0x8266,0x8267,0x8269,0x62e7,0x6cde,0x725b,
+ 0x626d,0x94ae,0x7ebd,0x8113,0x6d53,0x519c,0x5f04,0x5974,0x52aa,0x6012,
+ 0x5973,0x6696,0x8650,0x759f,0x632a,0x61e6,0x7cef,0x8bfa,0x54e6,0x6b27,
+ 0x9e25,0x6bb4,0x85d5,0x5455,0x5076,0x6ca4,0x556a,0x8db4,0x722c,0x5e15,
+ 0x6015,0x7436,0x62cd,0x6392,0x724c,0x5f98,0x6e43,0x6d3e,0x6500,0x6f58,
+ 0x76d8,0x78d0,0x76fc,0x7554,0x5224,0x53db,0x4e53,0x5e9e,0x65c1,0x802a,
+ 0x80d6,0x629b,0x5486,0x5228,0x70ae,0x888d,0x8dd1,0x6ce1,0x5478,0x80da,
+ 0x57f9,0x88f4,0x8d54,0x966a,0x914d,0x4f69,0x6c9b,0x55b7,0x76c6,0x7830,
+ 0x62a8,0x70f9,0x6f8e,0x5f6d,0x84ec,0x68da,0x787c,0x7bf7,0x81a8,0x670b,
+ 0x9e4f,0x6367,0x78b0,0x576f,0x7812,0x9739,0x6279,0x62ab,0x5288,0x7435,
+ 0x6bd7
+ },
+ { /* ku 46 */
+ 0x826a,0x826b,0x826c,0x826d,0x8271,0x8275,0x8276,0x8277,0x8278,0x827b,
+ 0x827c,0x8280,0x8281,0x8283,0x8285,0x8286,0x8287,0x8289,0x828c,0x8290,
+ 0x8293,0x8294,0x8295,0x8296,0x829a,0x829b,0x829e,0x82a0,0x82a2,0x82a3,
+ 0x82a7,0x82b2,0x82b5,0x82b6,0x82ba,0x82bb,0x82bc,0x82bf,0x82c0,0x82c2,
+ 0x82c3,0x82c5,0x82c6,0x82c9,0x82d0,0x82d6,0x82d9,0x82da,0x82dd,0x82e2,
+ 0x82e7,0x82e8,0x82e9,0x82ea,0x82ec,0x82ed,0x82ee,0x82f0,0x82f2,0x82f3,
+ 0x82f5,0x82f6,0x82f8,UBOGON,0x82fa,0x82fc,0x82fd,0x82fe,0x82ff,0x8300,
+ 0x830a,0x830b,0x830d,0x8310,0x8312,0x8313,0x8316,0x8318,0x8319,0x831d,
+ 0x831e,0x831f,0x8320,0x8321,0x8322,0x8323,0x8324,0x8325,0x8326,0x8329,
+ 0x832a,0x832e,0x8330,0x8332,0x8337,0x833b,0x833d,0x5564,0x813e,0x75b2,
+ 0x76ae,0x5339,0x75de,0x50fb,0x5c41,0x8b6c,0x7bc7,0x504f,0x7247,0x9a97,
+ 0x98d8,0x6f02,0x74e2,0x7968,0x6487,0x77a5,0x62fc,0x9891,0x8d2b,0x54c1,
+ 0x8058,0x4e52,0x576a,0x82f9,0x840d,0x5e73,0x51ed,0x74f6,0x8bc4,0x5c4f,
+ 0x5761,0x6cfc,0x9887,0x5a46,0x7834,0x9b44,0x8feb,0x7c95,0x5256,0x6251,
+ 0x94fa,0x4ec6,0x8386,0x8461,0x83e9,0x84b2,0x57d4,0x6734,0x5703,0x666e,
+ 0x6d66,0x8c31,0x66dd,0x7011,0x671f,0x6b3a,0x6816,0x621a,0x59bb,0x4e03,
+ 0x51c4,0x6f06,0x67d2,0x6c8f,0x5176,0x68cb,0x5947,0x6b67,0x7566,0x5d0e,
+ 0x8110,0x9f50,0x65d7,0x7948,0x7941,0x9a91,0x8d77,0x5c82,0x4e5e,0x4f01,
+ 0x542f,0x5951,0x780c,0x5668,0x6c14,0x8fc4,0x5f03,0x6c7d,0x6ce3,0x8bab,
+ 0x6390
+ },
+ { /* ku 47 */
+ 0x833e,0x833f,0x8341,0x8342,0x8344,0x8345,0x8348,0x834a,0x834b,0x834c,
+ 0x834d,0x834e,0x8353,0x8355,0x8356,0x8357,0x8358,0x8359,0x835d,0x8362,
+ 0x8370,0x8371,0x8372,0x8373,0x8374,0x8375,0x8376,0x8379,0x837a,0x837e,
+ 0x837f,0x8380,0x8381,0x8382,0x8383,0x8384,0x8387,0x8388,0x838a,0x838b,
+ 0x838c,0x838d,0x838f,0x8390,0x8391,0x8394,0x8395,0x8396,0x8397,0x8399,
+ 0x839a,0x839d,0x839f,0x83a1,0x83a2,0x83a3,0x83a4,0x83a5,0x83a6,0x83a7,
+ 0x83ac,0x83ad,0x83ae,UBOGON,0x83af,0x83b5,0x83bb,0x83be,0x83bf,0x83c2,
+ 0x83c3,0x83c4,0x83c6,0x83c8,0x83c9,0x83cb,0x83cd,0x83ce,0x83d0,0x83d1,
+ 0x83d2,0x83d3,0x83d5,0x83d7,0x83d9,0x83da,0x83db,0x83de,0x83e2,0x83e3,
+ 0x83e4,0x83e6,0x83e7,0x83e8,0x83eb,0x83ec,0x83ed,0x6070,0x6d3d,0x7275,
+ 0x6266,0x948e,0x94c5,0x5343,0x8fc1,0x7b7e,0x4edf,0x8c26,0x4e7e,0x9ed4,
+ 0x94b1,0x94b3,0x524d,0x6f5c,0x9063,0x6d45,0x8c34,0x5811,0x5d4c,0x6b20,
+ 0x6b49,0x67aa,0x545b,0x8154,0x7f8c,0x5899,0x8537,0x5f3a,0x62a2,0x6a47,
+ 0x9539,0x6572,0x6084,0x6865,0x77a7,0x4e54,0x4fa8,0x5de7,0x9798,0x64ac,
+ 0x7fd8,0x5ced,0x4fcf,0x7a8d,0x5207,0x8304,0x4e14,0x602f,0x7a83,0x94a6,
+ 0x4fb5,0x4eb2,0x79e6,0x7434,0x52e4,0x82b9,0x64d2,0x79bd,0x5bdd,0x6c81,
+ 0x9752,0x8f7b,0x6c22,0x503e,0x537f,0x6e05,0x64ce,0x6674,0x6c30,0x60c5,
+ 0x9877,0x8bf7,0x5e86,0x743c,0x7a77,0x79cb,0x4e18,0x90b1,0x7403,0x6c42,
+ 0x56da,0x914b,0x6cc5,0x8d8b,0x533a,0x86c6,0x66f2,0x8eaf,0x5c48,0x9a71,
+ 0x6e20
+ },
+ { /* ku 48 */
+ 0x83ee,0x83ef,0x83f3,0x83f4,0x83f5,0x83f6,0x83f7,0x83fa,0x83fb,0x83fc,
+ 0x83fe,0x83ff,0x8400,0x8402,0x8405,0x8407,0x8408,0x8409,0x840a,0x8410,
+ 0x8412,0x8413,0x8414,0x8415,0x8416,0x8417,0x8419,0x841a,0x841b,0x841e,
+ 0x841f,0x8420,0x8421,0x8422,0x8423,0x8429,0x842a,0x842b,0x842c,0x842d,
+ 0x842e,0x842f,0x8430,0x8432,0x8433,0x8434,0x8435,0x8436,0x8437,0x8439,
+ 0x843a,0x843b,0x843e,0x843f,0x8440,0x8441,0x8442,0x8443,0x8444,0x8445,
+ 0x8447,0x8448,0x8449,UBOGON,0x844a,0x844b,0x844c,0x844d,0x844e,0x844f,
+ 0x8450,0x8452,0x8453,0x8454,0x8455,0x8456,0x8458,0x845d,0x845e,0x845f,
+ 0x8460,0x8462,0x8464,0x8465,0x8466,0x8467,0x8468,0x846a,0x846e,0x846f,
+ 0x8470,0x8472,0x8474,0x8477,0x8479,0x847b,0x847c,0x53d6,0x5a36,0x9f8b,
+ 0x8da3,0x53bb,0x5708,0x98a7,0x6743,0x919b,0x6cc9,0x5168,0x75ca,0x62f3,
+ 0x72ac,0x5238,0x529d,0x7f3a,0x7094,0x7638,0x5374,0x9e4a,0x69b7,0x786e,
+ 0x96c0,0x88d9,0x7fa4,0x7136,0x71c3,0x5189,0x67d3,0x74e4,0x58e4,0x6518,
+ 0x56b7,0x8ba9,0x9976,0x6270,0x7ed5,0x60f9,0x70ed,0x58ec,0x4ec1,0x4eba,
+ 0x5fcd,0x97e7,0x4efb,0x8ba4,0x5203,0x598a,0x7eab,0x6254,0x4ecd,0x65e5,
+ 0x620e,0x8338,0x84c9,0x8363,0x878d,0x7194,0x6eb6,0x5bb9,0x7ed2,0x5197,
+ 0x63c9,0x67d4,0x8089,0x8339,0x8815,0x5112,0x5b7a,0x5982,0x8fb1,0x4e73,
+ 0x6c5d,0x5165,0x8925,0x8f6f,0x962e,0x854a,0x745e,0x9510,0x95f0,0x6da6,
+ 0x82e5,0x5f31,0x6492,0x6d12,0x8428,0x816e,0x9cc3,0x585e,0x8d5b,0x4e09,
+ 0x53c1
+ },
+ { /* ku 49 */
+ 0x847d,0x847e,0x847f,0x8480,0x8481,0x8483,0x8484,0x8485,0x8486,0x848a,
+ 0x848d,0x848f,0x8490,0x8491,0x8492,0x8493,0x8494,0x8495,0x8496,0x8498,
+ 0x849a,0x849b,0x849d,0x849e,0x849f,0x84a0,0x84a2,0x84a3,0x84a4,0x84a5,
+ 0x84a6,0x84a7,0x84a8,0x84a9,0x84aa,0x84ab,0x84ac,0x84ad,0x84ae,0x84b0,
+ 0x84b1,0x84b3,0x84b5,0x84b6,0x84b7,0x84bb,0x84bc,0x84be,0x84c0,0x84c2,
+ 0x84c3,0x84c5,0x84c6,0x84c7,0x84c8,0x84cb,0x84cc,0x84ce,0x84cf,0x84d2,
+ 0x84d4,0x84d5,0x84d7,UBOGON,0x84d8,0x84d9,0x84da,0x84db,0x84dc,0x84de,
+ 0x84e1,0x84e2,0x84e4,0x84e7,0x84e8,0x84e9,0x84ea,0x84eb,0x84ed,0x84ee,
+ 0x84ef,0x84f1,0x84f2,0x84f3,0x84f4,0x84f5,0x84f6,0x84f7,0x84f8,0x84f9,
+ 0x84fa,0x84fb,0x84fd,0x84fe,0x8500,0x8501,0x8502,0x4f1e,0x6563,0x6851,
+ 0x55d3,0x4e27,0x6414,0x9a9a,0x626b,0x5ac2,0x745f,0x8272,0x6da9,0x68ee,
+ 0x50e7,0x838e,0x7802,0x6740,0x5239,0x6c99,0x7eb1,0x50bb,0x5565,0x715e,
+ 0x7b5b,0x6652,0x73ca,0x82eb,0x6749,0x5c71,0x5220,0x717d,0x886b,0x95ea,
+ 0x9655,0x64c5,0x8d61,0x81b3,0x5584,0x6c55,0x6247,0x7f2e,0x5892,0x4f24,
+ 0x5546,0x8d4f,0x664c,0x4e0a,0x5c1a,0x88f3,0x68a2,0x634e,0x7a0d,0x70e7,
+ 0x828d,0x52fa,0x97f6,0x5c11,0x54e8,0x90b5,0x7ecd,0x5962,0x8d4a,0x86c7,
+ 0x820c,0x820d,0x8d66,0x6444,0x5c04,0x6151,0x6d89,0x793e,0x8bbe,0x7837,
+ 0x7533,0x547b,0x4f38,0x8eab,0x6df1,0x5a20,0x7ec5,0x795e,0x6c88,0x5ba1,
+ 0x5a76,0x751a,0x80be,0x614e,0x6e17,0x58f0,0x751f,0x7525,0x7272,0x5347,
+ 0x7ef3
+ },
+ { /* ku 4a */
+ 0x8503,0x8504,0x8505,0x8506,0x8507,0x8508,0x8509,0x850a,0x850b,0x850d,
+ 0x850e,0x850f,0x8510,0x8512,0x8514,0x8515,0x8516,0x8518,0x8519,0x851b,
+ 0x851c,0x851d,0x851e,0x8520,0x8522,0x8523,0x8524,0x8525,0x8526,0x8527,
+ 0x8528,0x8529,0x852a,0x852d,0x852e,0x852f,0x8530,0x8531,0x8532,0x8533,
+ 0x8534,0x8535,0x8536,0x853e,0x853f,0x8540,0x8541,0x8542,0x8544,0x8545,
+ 0x8546,0x8547,0x854b,0x854c,0x854d,0x854e,0x854f,0x8550,0x8551,0x8552,
+ 0x8553,0x8554,0x8555,UBOGON,0x8557,0x8558,0x855a,0x855b,0x855c,0x855d,
+ 0x855f,0x8560,0x8561,0x8562,0x8563,0x8565,0x8566,0x8567,0x8569,0x856a,
+ 0x856b,0x856c,0x856d,0x856e,0x856f,0x8570,0x8571,0x8573,0x8575,0x8576,
+ 0x8577,0x8578,0x857c,0x857d,0x857f,0x8580,0x8581,0x7701,0x76db,0x5269,
+ 0x80dc,0x5723,0x5e08,0x5931,0x72ee,0x65bd,0x6e7f,0x8bd7,0x5c38,0x8671,
+ 0x5341,0x77f3,0x62fe,0x65f6,0x4ec0,0x98df,0x8680,0x5b9e,0x8bc6,0x53f2,
+ 0x77e2,0x4f7f,0x5c4e,0x9a76,0x59cb,0x5f0f,0x793a,0x58eb,0x4e16,0x67ff,
+ 0x4e8b,0x62ed,0x8a93,0x901d,0x52bf,0x662f,0x55dc,0x566c,0x9002,0x4ed5,
+ 0x4f8d,0x91ca,0x9970,0x6c0f,0x5e02,0x6043,0x5ba4,0x89c6,0x8bd5,0x6536,
+ 0x624b,0x9996,0x5b88,0x5bff,0x6388,0x552e,0x53d7,0x7626,0x517d,0x852c,
+ 0x67a2,0x68b3,0x6b8a,0x6292,0x8f93,0x53d4,0x8212,0x6dd1,0x758f,0x4e66,
+ 0x8d4e,0x5b70,0x719f,0x85af,0x6691,0x66d9,0x7f72,0x8700,0x9ecd,0x9f20,
+ 0x5c5e,0x672f,0x8ff0,0x6811,0x675f,0x620d,0x7ad6,0x5885,0x5eb6,0x6570,
+ 0x6f31
+ },
+ { /* ku 4b */
+ 0x8582,0x8583,0x8586,0x8588,0x8589,0x858a,0x858b,0x858c,0x858d,0x858e,
+ 0x8590,0x8591,0x8592,0x8593,0x8594,0x8595,0x8596,0x8597,0x8598,0x8599,
+ 0x859a,0x859d,0x859e,0x859f,0x85a0,0x85a1,0x85a2,0x85a3,0x85a5,0x85a6,
+ 0x85a7,0x85a9,0x85ab,0x85ac,0x85ad,0x85b1,0x85b2,0x85b3,0x85b4,0x85b5,
+ 0x85b6,0x85b8,0x85ba,0x85bb,0x85bc,0x85bd,0x85be,0x85bf,0x85c0,0x85c2,
+ 0x85c3,0x85c4,0x85c5,0x85c6,0x85c7,0x85c8,0x85ca,0x85cb,0x85cc,0x85cd,
+ 0x85ce,0x85d1,0x85d2,UBOGON,0x85d4,0x85d6,0x85d7,0x85d8,0x85d9,0x85da,
+ 0x85db,0x85dd,0x85de,0x85df,0x85e0,0x85e1,0x85e2,0x85e3,0x85e5,0x85e6,
+ 0x85e7,0x85e8,0x85ea,0x85eb,0x85ec,0x85ed,0x85ee,0x85ef,0x85f0,0x85f1,
+ 0x85f2,0x85f3,0x85f4,0x85f5,0x85f6,0x85f7,0x85f8,0x6055,0x5237,0x800d,
+ 0x6454,0x8870,0x7529,0x5e05,0x6813,0x62f4,0x971c,0x53cc,0x723d,0x8c01,
+ 0x6c34,0x7761,0x7a0e,0x542e,0x77ac,0x987a,0x821c,0x8bf4,0x7855,0x6714,
+ 0x70c1,0x65af,0x6495,0x5636,0x601d,0x79c1,0x53f8,0x4e1d,0x6b7b,0x8086,
+ 0x5bfa,0x55e3,0x56db,0x4f3a,0x4f3c,0x9972,0x5df3,0x677e,0x8038,0x6002,
+ 0x9882,0x9001,0x5b8b,0x8bbc,0x8bf5,0x641c,0x8258,0x64de,0x55fd,0x82cf,
+ 0x9165,0x4fd7,0x7d20,0x901f,0x7c9f,0x50f3,0x5851,0x6eaf,0x5bbf,0x8bc9,
+ 0x8083,0x9178,0x849c,0x7b97,0x867d,0x968b,0x968f,0x7ee5,0x9ad3,0x788e,
+ 0x5c81,0x7a57,0x9042,0x96a7,0x795f,0x5b59,0x635f,0x7b0b,0x84d1,0x68ad,
+ 0x5506,0x7f29,0x7410,0x7d22,0x9501,0x6240,0x584c,0x4ed6,0x5b83,0x5979,
+ 0x5854
+ },
+ { /* ku 4c */
+ 0x85f9,0x85fa,0x85fc,0x85fd,0x85fe,0x8600,0x8601,0x8602,0x8603,0x8604,
+ 0x8606,0x8607,0x8608,0x8609,0x860a,0x860b,0x860c,0x860d,0x860e,0x860f,
+ 0x8610,0x8612,0x8613,0x8614,0x8615,0x8617,0x8618,0x8619,0x861a,0x861b,
+ 0x861c,0x861d,0x861e,0x861f,0x8620,0x8621,0x8622,0x8623,0x8624,0x8625,
+ 0x8626,0x8628,0x862a,0x862b,0x862c,0x862d,0x862e,0x862f,0x8630,0x8631,
+ 0x8632,0x8633,0x8634,0x8635,0x8636,0x8637,0x8639,0x863a,0x863b,0x863d,
+ 0x863e,0x863f,0x8640,UBOGON,0x8641,0x8642,0x8643,0x8644,0x8645,0x8646,
+ 0x8647,0x8648,0x8649,0x864a,0x864b,0x864c,0x8652,0x8653,0x8655,0x8656,
+ 0x8657,0x8658,0x8659,0x865b,0x865c,0x865d,0x865f,0x8660,0x8661,0x8663,
+ 0x8664,0x8665,0x8666,0x8667,0x8668,0x8669,0x866a,0x736d,0x631e,0x8e4b,
+ 0x8e0f,0x80ce,0x82d4,0x62ac,0x53f0,0x6cf0,0x915e,0x592a,0x6001,0x6c70,
+ 0x574d,0x644a,0x8d2a,0x762b,0x6ee9,0x575b,0x6a80,0x75f0,0x6f6d,0x8c2d,
+ 0x8c08,0x5766,0x6bef,0x8892,0x78b3,0x63a2,0x53f9,0x70ad,0x6c64,0x5858,
+ 0x642a,0x5802,0x68e0,0x819b,0x5510,0x7cd6,0x5018,0x8eba,0x6dcc,0x8d9f,
+ 0x70eb,0x638f,0x6d9b,0x6ed4,0x7ee6,0x8404,0x6843,0x9003,0x6dd8,0x9676,
+ 0x8ba8,0x5957,0x7279,0x85e4,0x817e,0x75bc,0x8a8a,0x68af,0x5254,0x8e22,
+ 0x9511,0x63d0,0x9898,0x8e44,0x557c,0x4f53,0x66ff,0x568f,0x60d5,0x6d95,
+ 0x5243,0x5c49,0x5929,0x6dfb,0x586b,0x7530,0x751c,0x606c,0x8214,0x8146,
+ 0x6311,0x6761,0x8fe2,0x773a,0x8df3,0x8d34,0x94c1,0x5e16,0x5385,0x542c,
+ 0x70c3
+ },
+ { /* ku 4d */
+ 0x866d,0x866f,0x8670,0x8672,0x8673,0x8674,0x8675,0x8676,0x8677,0x8678,
+ 0x8683,0x8684,0x8685,0x8686,0x8687,0x8688,0x8689,0x868e,0x868f,0x8690,
+ 0x8691,0x8692,0x8694,0x8696,0x8697,0x8698,0x8699,0x869a,0x869b,0x869e,
+ 0x869f,0x86a0,0x86a1,0x86a2,0x86a5,0x86a6,0x86ab,0x86ad,0x86ae,0x86b2,
+ 0x86b3,0x86b7,0x86b8,0x86b9,0x86bb,0x86bc,0x86bd,0x86be,0x86bf,0x86c1,
+ 0x86c2,0x86c3,0x86c5,0x86c8,0x86cc,0x86cd,0x86d2,0x86d3,0x86d5,0x86d6,
+ 0x86d7,0x86da,0x86dc,UBOGON,0x86dd,0x86e0,0x86e1,0x86e2,0x86e3,0x86e5,
+ 0x86e6,0x86e7,0x86e8,0x86ea,0x86eb,0x86ec,0x86ef,0x86f5,0x86f6,0x86f7,
+ 0x86fa,0x86fb,0x86fc,0x86fd,0x86ff,0x8701,0x8704,0x8705,0x8706,0x870b,
+ 0x870c,0x870e,0x870f,0x8710,0x8711,0x8714,0x8716,0x6c40,0x5ef7,0x505c,
+ 0x4ead,0x5ead,0x633a,0x8247,0x901a,0x6850,0x916e,0x77b3,0x540c,0x94dc,
+ 0x5f64,0x7ae5,0x6876,0x6345,0x7b52,0x7edf,0x75db,0x5077,0x6295,0x5934,
+ 0x900f,0x51f8,0x79c3,0x7a81,0x56fe,0x5f92,0x9014,0x6d82,0x5c60,0x571f,
+ 0x5410,0x5154,0x6e4d,0x56e2,0x63a8,0x9893,0x817f,0x8715,0x892a,0x9000,
+ 0x541e,0x5c6f,0x81c0,0x62d6,0x6258,0x8131,0x9e35,0x9640,0x9a6e,0x9a7c,
+ 0x692d,0x59a5,0x62d3,0x553e,0x6316,0x54c7,0x86d9,0x6d3c,0x5a03,0x74e6,
+ 0x889c,0x6b6a,0x5916,0x8c4c,0x5f2f,0x6e7e,0x73a9,0x987d,0x4e38,0x70f7,
+ 0x5b8c,0x7897,0x633d,0x665a,0x7696,0x60cb,0x5b9b,0x5a49,0x4e07,0x8155,
+ 0x6c6a,0x738b,0x4ea1,0x6789,0x7f51,0x5f80,0x65fa,0x671b,0x5fd8,0x5984,
+ 0x5a01
+ },
+ { /* ku 4e */
+ 0x8719,0x871b,0x871d,0x871f,0x8720,0x8724,0x8726,0x8727,0x8728,0x872a,
+ 0x872b,0x872c,0x872d,0x872f,0x8730,0x8732,0x8733,0x8735,0x8736,0x8738,
+ 0x8739,0x873a,0x873c,0x873d,0x8740,0x8741,0x8742,0x8743,0x8744,0x8745,
+ 0x8746,0x874a,0x874b,0x874d,0x874f,0x8750,0x8751,0x8752,0x8754,0x8755,
+ 0x8756,0x8758,0x875a,0x875b,0x875c,0x875d,0x875e,0x875f,0x8761,0x8762,
+ 0x8766,0x8767,0x8768,0x8769,0x876a,0x876b,0x876c,0x876d,0x876f,0x8771,
+ 0x8772,0x8773,0x8775,UBOGON,0x8777,0x8778,0x8779,0x877a,0x877f,0x8780,
+ 0x8781,0x8784,0x8786,0x8787,0x8789,0x878a,0x878c,0x878e,0x878f,0x8790,
+ 0x8791,0x8792,0x8794,0x8795,0x8796,0x8798,0x8799,0x879a,0x879b,0x879c,
+ 0x879d,0x879e,0x87a0,0x87a1,0x87a2,0x87a3,0x87a4,0x5dcd,0x5fae,0x5371,
+ 0x97e6,0x8fdd,0x6845,0x56f4,0x552f,0x60df,0x4e3a,0x6f4d,0x7ef4,0x82c7,
+ 0x840e,0x59d4,0x4f1f,0x4f2a,0x5c3e,0x7eac,0x672a,0x851a,0x5473,0x754f,
+ 0x80c3,0x5582,0x9b4f,0x4f4d,0x6e2d,0x8c13,0x5c09,0x6170,0x536b,0x761f,
+ 0x6e29,0x868a,0x6587,0x95fb,0x7eb9,0x543b,0x7a33,0x7d0a,0x95ee,0x55e1,
+ 0x7fc1,0x74ee,0x631d,0x8717,0x6da1,0x7a9d,0x6211,0x65a1,0x5367,0x63e1,
+ 0x6c83,0x5deb,0x545c,0x94a8,0x4e4c,0x6c61,0x8bec,0x5c4b,0x65e0,0x829c,
+ 0x68a7,0x543e,0x5434,0x6bcb,0x6b66,0x4e94,0x6342,0x5348,0x821e,0x4f0d,
+ 0x4fae,0x575e,0x620a,0x96fe,0x6664,0x7269,0x52ff,0x52a1,0x609f,0x8bef,
+ 0x6614,0x7199,0x6790,0x897f,0x7852,0x77fd,0x6670,0x563b,0x5438,0x9521,
+ 0x727a
+ },
+ { /* ku 4f */
+ 0x87a5,0x87a6,0x87a7,0x87a9,0x87aa,0x87ae,0x87b0,0x87b1,0x87b2,0x87b4,
+ 0x87b6,0x87b7,0x87b8,0x87b9,0x87bb,0x87bc,0x87be,0x87bf,0x87c1,0x87c2,
+ 0x87c3,0x87c4,0x87c5,0x87c7,0x87c8,0x87c9,0x87cc,0x87cd,0x87ce,0x87cf,
+ 0x87d0,0x87d4,0x87d5,0x87d6,0x87d7,0x87d8,0x87d9,0x87da,0x87dc,0x87dd,
+ 0x87de,0x87df,0x87e1,0x87e2,0x87e3,0x87e4,0x87e6,0x87e7,0x87e8,0x87e9,
+ 0x87eb,0x87ec,0x87ed,0x87ef,0x87f0,0x87f1,0x87f2,0x87f3,0x87f4,0x87f5,
+ 0x87f6,0x87f7,0x87f8,UBOGON,0x87fa,0x87fb,0x87fc,0x87fd,0x87ff,0x8800,
+ 0x8801,0x8802,0x8804,0x8805,0x8806,0x8807,0x8808,0x8809,0x880b,0x880c,
+ 0x880d,0x880e,0x880f,0x8810,0x8811,0x8812,0x8814,0x8817,0x8818,0x8819,
+ 0x881a,0x881c,0x881d,0x881e,0x881f,0x8820,0x8823,0x7a00,0x606f,0x5e0c,
+ 0x6089,0x819d,0x5915,0x60dc,0x7184,0x70ef,0x6eaa,0x6c50,0x7280,0x6a84,
+ 0x88ad,0x5e2d,0x4e60,0x5ab3,0x559c,0x94e3,0x6d17,0x7cfb,0x9699,0x620f,
+ 0x7ec6,0x778e,0x867e,0x5323,0x971e,0x8f96,0x6687,0x5ce1,0x4fa0,0x72ed,
+ 0x4e0b,0x53a6,0x590f,0x5413,0x6380,0x9528,0x5148,0x4ed9,0x9c9c,0x7ea4,
+ 0x54b8,0x8d24,0x8854,0x8237,0x95f2,0x6d8e,0x5f26,0x5acc,0x663e,0x9669,
+ 0x73b0,0x732e,0x53bf,0x817a,0x9985,0x7fa1,0x5baa,0x9677,0x9650,0x7ebf,
+ 0x76f8,0x53a2,0x9576,0x9999,0x7bb1,0x8944,0x6e58,0x4e61,0x7fd4,0x7965,
+ 0x8be6,0x60f3,0x54cd,0x4eab,0x9879,0x5df7,0x6a61,0x50cf,0x5411,0x8c61,
+ 0x8427,0x785d,0x9704,0x524a,0x54ee,0x56a3,0x9500,0x6d88,0x5bb5,0x6dc6,
+ 0x6653
+ },
+ { /* ku 50 */
+ 0x8824,0x8825,0x8826,0x8827,0x8828,0x8829,0x882a,0x882b,0x882c,0x882d,
+ 0x882e,0x882f,0x8830,0x8831,0x8833,0x8834,0x8835,0x8836,0x8837,0x8838,
+ 0x883a,0x883b,0x883d,0x883e,0x883f,0x8841,0x8842,0x8843,0x8846,0x8847,
+ 0x8848,0x8849,0x884a,0x884b,0x884e,0x884f,0x8850,0x8851,0x8852,0x8853,
+ 0x8855,0x8856,0x8858,0x885a,0x885b,0x885c,0x885d,0x885e,0x885f,0x8860,
+ 0x8866,0x8867,0x886a,0x886d,0x886f,0x8871,0x8873,0x8874,0x8875,0x8876,
+ 0x8878,0x8879,0x887a,UBOGON,0x887b,0x887c,0x8880,0x8883,0x8886,0x8887,
+ 0x8889,0x888a,0x888c,0x888e,0x888f,0x8890,0x8891,0x8893,0x8894,0x8895,
+ 0x8897,0x8898,0x8899,0x889a,0x889b,0x889d,0x889e,0x889f,0x88a0,0x88a1,
+ 0x88a3,0x88a5,0x88a6,0x88a7,0x88a8,0x88a9,0x88aa,0x5c0f,0x5b5d,0x6821,
+ 0x8096,0x5578,0x7b11,0x6548,0x6954,0x4e9b,0x6b47,0x874e,0x978b,0x534f,
+ 0x631f,0x643a,0x90aa,0x659c,0x80c1,0x8c10,0x5199,0x68b0,0x5378,0x87f9,
+ 0x61c8,0x6cc4,0x6cfb,0x8c22,0x5c51,0x85aa,0x82af,0x950c,0x6b23,0x8f9b,
+ 0x65b0,0x5ffb,0x5fc3,0x4fe1,0x8845,0x661f,0x8165,0x7329,0x60fa,0x5174,
+ 0x5211,0x578b,0x5f62,0x90a2,0x884c,0x9192,0x5e78,0x674f,0x6027,0x59d3,
+ 0x5144,0x51f6,0x80f8,0x5308,0x6c79,0x96c4,0x718a,0x4f11,0x4fee,0x7f9e,
+ 0x673d,0x55c5,0x9508,0x79c0,0x8896,0x7ee3,0x589f,0x620c,0x9700,0x865a,
+ 0x5618,0x987b,0x5f90,0x8bb8,0x84c4,0x9157,0x53d9,0x65ed,0x5e8f,0x755c,
+ 0x6064,0x7d6e,0x5a7f,0x7eea,0x7eed,0x8f69,0x55a7,0x5ba3,0x60ac,0x65cb,
+ 0x7384
+ },
+ { /* ku 51 */
+ 0x88ac,0x88ae,0x88af,0x88b0,0x88b2,0x88b3,0x88b4,0x88b5,0x88b6,0x88b8,
+ 0x88b9,0x88ba,0x88bb,0x88bd,0x88be,0x88bf,0x88c0,0x88c3,0x88c4,0x88c7,
+ 0x88c8,0x88ca,0x88cb,0x88cc,0x88cd,0x88cf,0x88d0,0x88d1,0x88d3,0x88d6,
+ 0x88d7,0x88da,0x88db,0x88dc,0x88dd,0x88de,0x88e0,0x88e1,0x88e6,0x88e7,
+ 0x88e9,0x88ea,0x88eb,0x88ec,0x88ed,0x88ee,0x88ef,0x88f2,0x88f5,0x88f6,
+ 0x88f7,0x88fa,0x88fb,0x88fd,0x88ff,0x8900,0x8901,0x8903,0x8904,0x8905,
+ 0x8906,0x8907,0x8908,UBOGON,0x8909,0x890b,0x890c,0x890d,0x890e,0x890f,
+ 0x8911,0x8914,0x8915,0x8916,0x8917,0x8918,0x891c,0x891d,0x891e,0x891f,
+ 0x8920,0x8922,0x8923,0x8924,0x8926,0x8927,0x8928,0x8929,0x892c,0x892d,
+ 0x892e,0x892f,0x8931,0x8932,0x8933,0x8935,0x8937,0x9009,0x7663,0x7729,
+ 0x7eda,0x9774,0x859b,0x5b66,0x7a74,0x96ea,0x8840,0x52cb,0x718f,0x5faa,
+ 0x65ec,0x8be2,0x5bfb,0x9a6f,0x5de1,0x6b89,0x6c5b,0x8bad,0x8baf,0x900a,
+ 0x8fc5,0x538b,0x62bc,0x9e26,0x9e2d,0x5440,0x4e2b,0x82bd,0x7259,0x869c,
+ 0x5d16,0x8859,0x6daf,0x96c5,0x54d1,0x4e9a,0x8bb6,0x7109,0x54bd,0x9609,
+ 0x70df,0x6df9,0x76d0,0x4e25,0x7814,0x8712,0x5ca9,0x5ef6,0x8a00,0x989c,
+ 0x960e,0x708e,0x6cbf,0x5944,0x63a9,0x773c,0x884d,0x6f14,0x8273,0x5830,
+ 0x71d5,0x538c,0x781a,0x96c1,0x5501,0x5f66,0x7130,0x5bb4,0x8c1a,0x9a8c,
+ 0x6b83,0x592e,0x9e2f,0x79e7,0x6768,0x626c,0x4f6f,0x75a1,0x7f8a,0x6d0b,
+ 0x9633,0x6c27,0x4ef0,0x75d2,0x517b,0x6837,0x6f3e,0x9080,0x8170,0x5996,
+ 0x7476
+ },
+ { /* ku 52 */
+ 0x8938,0x8939,0x893a,0x893b,0x893c,0x893d,0x893e,0x893f,0x8940,0x8942,
+ 0x8943,0x8945,0x8946,0x8947,0x8948,0x8949,0x894a,0x894b,0x894c,0x894d,
+ 0x894e,0x894f,0x8950,0x8951,0x8952,0x8953,0x8954,0x8955,0x8956,0x8957,
+ 0x8958,0x8959,0x895a,0x895b,0x895c,0x895d,0x8960,0x8961,0x8962,0x8963,
+ 0x8964,0x8965,0x8967,0x8968,0x8969,0x896a,0x896b,0x896c,0x896d,0x896e,
+ 0x896f,0x8970,0x8971,0x8972,0x8973,0x8974,0x8975,0x8976,0x8977,0x8978,
+ 0x8979,0x897a,0x897c,UBOGON,0x897d,0x897e,0x8980,0x8982,0x8984,0x8985,
+ 0x8987,0x8988,0x8989,0x898a,0x898b,0x898c,0x898d,0x898e,0x898f,0x8990,
+ 0x8991,0x8992,0x8993,0x8994,0x8995,0x8996,0x8997,0x8998,0x8999,0x899a,
+ 0x899b,0x899c,0x899d,0x899e,0x899f,0x89a0,0x89a1,0x6447,0x5c27,0x9065,
+ 0x7a91,0x8c23,0x59da,0x54ac,0x8200,0x836f,0x8981,0x8000,0x6930,0x564e,
+ 0x8036,0x7237,0x91ce,0x51b6,0x4e5f,0x9875,0x6396,0x4e1a,0x53f6,0x66f3,
+ 0x814b,0x591c,0x6db2,0x4e00,0x58f9,0x533b,0x63d6,0x94f1,0x4f9d,0x4f0a,
+ 0x8863,0x9890,0x5937,0x9057,0x79fb,0x4eea,0x80f0,0x7591,0x6c82,0x5b9c,
+ 0x59e8,0x5f5d,0x6905,0x8681,0x501a,0x5df2,0x4e59,0x77e3,0x4ee5,0x827a,
+ 0x6291,0x6613,0x9091,0x5c79,0x4ebf,0x5f79,0x81c6,0x9038,0x8084,0x75ab,
+ 0x4ea6,0x88d4,0x610f,0x6bc5,0x5fc6,0x4e49,0x76ca,0x6ea2,0x8be3,0x8bae,
+ 0x8c0a,0x8bd1,0x5f02,0x7ffc,0x7fcc,0x7ece,0x8335,0x836b,0x56e0,0x6bb7,
+ 0x97f3,0x9634,0x59fb,0x541f,0x94f6,0x6deb,0x5bc5,0x996e,0x5c39,0x5f15,
+ 0x9690
+ },
+ { /* ku 53 */
+ 0x89a2,0x89a3,0x89a4,0x89a5,0x89a6,0x89a7,0x89a8,0x89a9,0x89aa,0x89ab,
+ 0x89ac,0x89ad,0x89ae,0x89af,0x89b0,0x89b1,0x89b2,0x89b3,0x89b4,0x89b5,
+ 0x89b6,0x89b7,0x89b8,0x89b9,0x89ba,0x89bb,0x89bc,0x89bd,0x89be,0x89bf,
+ 0x89c0,0x89c3,0x89cd,0x89d3,0x89d4,0x89d5,0x89d7,0x89d8,0x89d9,0x89db,
+ 0x89dd,0x89df,0x89e0,0x89e1,0x89e2,0x89e4,0x89e7,0x89e8,0x89e9,0x89ea,
+ 0x89ec,0x89ed,0x89ee,0x89f0,0x89f1,0x89f2,0x89f4,0x89f5,0x89f6,0x89f7,
+ 0x89f8,0x89f9,0x89fa,UBOGON,0x89fb,0x89fc,0x89fd,0x89fe,0x89ff,0x8a01,
+ 0x8a02,0x8a03,0x8a04,0x8a05,0x8a06,0x8a08,0x8a09,0x8a0a,0x8a0b,0x8a0c,
+ 0x8a0d,0x8a0e,0x8a0f,0x8a10,0x8a11,0x8a12,0x8a13,0x8a14,0x8a15,0x8a16,
+ 0x8a17,0x8a18,0x8a19,0x8a1a,0x8a1b,0x8a1c,0x8a1d,0x5370,0x82f1,0x6a31,
+ 0x5a74,0x9e70,0x5e94,0x7f28,0x83b9,0x8424,0x8425,0x8367,0x8747,0x8fce,
+ 0x8d62,0x76c8,0x5f71,0x9896,0x786c,0x6620,0x54df,0x62e5,0x4f63,0x81c3,
+ 0x75c8,0x5eb8,0x96cd,0x8e0a,0x86f9,0x548f,0x6cf3,0x6d8c,0x6c38,0x607f,
+ 0x52c7,0x7528,0x5e7d,0x4f18,0x60a0,0x5fe7,0x5c24,0x7531,0x90ae,0x94c0,
+ 0x72b9,0x6cb9,0x6e38,0x9149,0x6709,0x53cb,0x53f3,0x4f51,0x91c9,0x8bf1,
+ 0x53c8,0x5e7c,0x8fc2,0x6de4,0x4e8e,0x76c2,0x6986,0x865e,0x611a,0x8206,
+ 0x4f59,0x4fde,0x903e,0x9c7c,0x6109,0x6e1d,0x6e14,0x9685,0x4e88,0x5a31,
+ 0x96e8,0x4e0e,0x5c7f,0x79b9,0x5b87,0x8bed,0x7fbd,0x7389,0x57df,0x828b,
+ 0x90c1,0x5401,0x9047,0x55bb,0x5cea,0x5fa1,0x6108,0x6b32,0x72f1,0x80b2,
+ 0x8a89
+ },
+ { /* ku 54 */
+ 0x8a1e,0x8a1f,0x8a20,0x8a21,0x8a22,0x8a23,0x8a24,0x8a25,0x8a26,0x8a27,
+ 0x8a28,0x8a29,0x8a2a,0x8a2b,0x8a2c,0x8a2d,0x8a2e,0x8a2f,0x8a30,0x8a31,
+ 0x8a32,0x8a33,0x8a34,0x8a35,0x8a36,0x8a37,0x8a38,0x8a39,0x8a3a,0x8a3b,
+ 0x8a3c,0x8a3d,0x8a3f,0x8a40,0x8a41,0x8a42,0x8a43,0x8a44,0x8a45,0x8a46,
+ 0x8a47,0x8a49,0x8a4a,0x8a4b,0x8a4c,0x8a4d,0x8a4e,0x8a4f,0x8a50,0x8a51,
+ 0x8a52,0x8a53,0x8a54,0x8a55,0x8a56,0x8a57,0x8a58,0x8a59,0x8a5a,0x8a5b,
+ 0x8a5c,0x8a5d,0x8a5e,UBOGON,0x8a5f,0x8a60,0x8a61,0x8a62,0x8a63,0x8a64,
+ 0x8a65,0x8a66,0x8a67,0x8a68,0x8a69,0x8a6a,0x8a6b,0x8a6c,0x8a6d,0x8a6e,
+ 0x8a6f,0x8a70,0x8a71,0x8a72,0x8a73,0x8a74,0x8a75,0x8a76,0x8a77,0x8a78,
+ 0x8a7a,0x8a7b,0x8a7c,0x8a7d,0x8a7e,0x8a7f,0x8a80,0x6d74,0x5bd3,0x88d5,
+ 0x9884,0x8c6b,0x9a6d,0x9e33,0x6e0a,0x51a4,0x5143,0x57a3,0x8881,0x539f,
+ 0x63f4,0x8f95,0x56ed,0x5458,0x5706,0x733f,0x6e90,0x7f18,0x8fdc,0x82d1,
+ 0x613f,0x6028,0x9662,0x66f0,0x7ea6,0x8d8a,0x8dc3,0x94a5,0x5cb3,0x7ca4,
+ 0x6708,0x60a6,0x9605,0x8018,0x4e91,0x90e7,0x5300,0x9668,0x5141,0x8fd0,
+ 0x8574,0x915d,0x6655,0x97f5,0x5b55,0x531d,0x7838,0x6742,0x683d,0x54c9,
+ 0x707e,0x5bb0,0x8f7d,0x518d,0x5728,0x54b1,0x6512,0x6682,0x8d5e,0x8d43,
+ 0x810f,0x846c,0x906d,0x7cdf,0x51ff,0x85fb,0x67a3,0x65e9,0x6fa1,0x86a4,
+ 0x8e81,0x566a,0x9020,0x7682,0x7076,0x71e5,0x8d23,0x62e9,0x5219,0x6cfd,
+ 0x8d3c,0x600e,0x589e,0x618e,0x66fe,0x8d60,0x624e,0x55b3,0x6e23,0x672d,
+ 0x8f67
+ },
+ { /* ku 55 */
+ 0x8a81,0x8a82,0x8a83,0x8a84,0x8a85,0x8a86,0x8a87,0x8a88,0x8a8b,0x8a8c,
+ 0x8a8d,0x8a8e,0x8a8f,0x8a90,0x8a91,0x8a92,0x8a94,0x8a95,0x8a96,0x8a97,
+ 0x8a98,0x8a99,0x8a9a,0x8a9b,0x8a9c,0x8a9d,0x8a9e,0x8a9f,0x8aa0,0x8aa1,
+ 0x8aa2,0x8aa3,0x8aa4,0x8aa5,0x8aa6,0x8aa7,0x8aa8,0x8aa9,0x8aaa,0x8aab,
+ 0x8aac,0x8aad,0x8aae,0x8aaf,0x8ab0,0x8ab1,0x8ab2,0x8ab3,0x8ab4,0x8ab5,
+ 0x8ab6,0x8ab7,0x8ab8,0x8ab9,0x8aba,0x8abb,0x8abc,0x8abd,0x8abe,0x8abf,
+ 0x8ac0,0x8ac1,0x8ac2,UBOGON,0x8ac3,0x8ac4,0x8ac5,0x8ac6,0x8ac7,0x8ac8,
+ 0x8ac9,0x8aca,0x8acb,0x8acc,0x8acd,0x8ace,0x8acf,0x8ad0,0x8ad1,0x8ad2,
+ 0x8ad3,0x8ad4,0x8ad5,0x8ad6,0x8ad7,0x8ad8,0x8ad9,0x8ada,0x8adb,0x8adc,
+ 0x8add,0x8ade,0x8adf,0x8ae0,0x8ae1,0x8ae2,0x8ae3,0x94e1,0x95f8,0x7728,
+ 0x6805,0x69a8,0x548b,0x4e4d,0x70b8,0x8bc8,0x6458,0x658b,0x5b85,0x7a84,
+ 0x503a,0x5be8,0x77bb,0x6be1,0x8a79,0x7c98,0x6cbe,0x76cf,0x65a9,0x8f97,
+ 0x5d2d,0x5c55,0x8638,0x6808,0x5360,0x6218,0x7ad9,0x6e5b,0x7efd,0x6a1f,
+ 0x7ae0,0x5f70,0x6f33,0x5f20,0x638c,0x6da8,0x6756,0x4e08,0x5e10,0x8d26,
+ 0x4ed7,0x80c0,0x7634,0x969c,0x62db,0x662d,0x627e,0x6cbc,0x8d75,0x7167,
+ 0x7f69,0x5146,0x8087,0x53ec,0x906e,0x6298,0x54f2,0x86f0,0x8f99,0x8005,
+ 0x9517,0x8517,0x8fd9,0x6d59,0x73cd,0x659f,0x771f,0x7504,0x7827,0x81fb,
+ 0x8d1e,0x9488,0x4fa6,0x6795,0x75b9,0x8bca,0x9707,0x632f,0x9547,0x9635,
+ 0x84b8,0x6323,0x7741,0x5f81,0x72f0,0x4e89,0x6014,0x6574,0x62ef,0x6b63,
+ 0x653f
+ },
+ { /* ku 56 */
+ 0x8ae4,0x8ae5,0x8ae6,0x8ae7,0x8ae8,0x8ae9,0x8aea,0x8aeb,0x8aec,0x8aed,
+ 0x8aee,0x8aef,0x8af0,0x8af1,0x8af2,0x8af3,0x8af4,0x8af5,0x8af6,0x8af7,
+ 0x8af8,0x8af9,0x8afa,0x8afb,0x8afc,0x8afd,0x8afe,0x8aff,0x8b00,0x8b01,
+ 0x8b02,0x8b03,0x8b04,0x8b05,0x8b06,0x8b08,0x8b09,0x8b0a,0x8b0b,0x8b0c,
+ 0x8b0d,0x8b0e,0x8b0f,0x8b10,0x8b11,0x8b12,0x8b13,0x8b14,0x8b15,0x8b16,
+ 0x8b17,0x8b18,0x8b19,0x8b1a,0x8b1b,0x8b1c,0x8b1d,0x8b1e,0x8b1f,0x8b20,
+ 0x8b21,0x8b22,0x8b23,UBOGON,0x8b24,0x8b25,0x8b27,0x8b28,0x8b29,0x8b2a,
+ 0x8b2b,0x8b2c,0x8b2d,0x8b2e,0x8b2f,0x8b30,0x8b31,0x8b32,0x8b33,0x8b34,
+ 0x8b35,0x8b36,0x8b37,0x8b38,0x8b39,0x8b3a,0x8b3b,0x8b3c,0x8b3d,0x8b3e,
+ 0x8b3f,0x8b40,0x8b41,0x8b42,0x8b43,0x8b44,0x8b45,0x5e27,0x75c7,0x90d1,
+ 0x8bc1,0x829d,0x679d,0x652f,0x5431,0x8718,0x77e5,0x80a2,0x8102,0x6c41,
+ 0x4e4b,0x7ec7,0x804c,0x76f4,0x690d,0x6b96,0x6267,0x503c,0x4f84,0x5740,
+ 0x6307,0x6b62,0x8dbe,0x53ea,0x65e8,0x7eb8,0x5fd7,0x631a,0x63b7,0x81f3,
+ 0x81f4,0x7f6e,0x5e1c,0x5cd9,0x5236,0x667a,0x79e9,0x7a1a,0x8d28,0x7099,
+ 0x75d4,0x6ede,0x6cbb,0x7a92,0x4e2d,0x76c5,0x5fe0,0x949f,0x8877,0x7ec8,
+ 0x79cd,0x80bf,0x91cd,0x4ef2,0x4f17,0x821f,0x5468,0x5dde,0x6d32,0x8bcc,
+ 0x7ca5,0x8f74,0x8098,0x5e1a,0x5492,0x76b1,0x5b99,0x663c,0x9aa4,0x73e0,
+ 0x682a,0x86db,0x6731,0x732a,0x8bf8,0x8bdb,0x9010,0x7af9,0x70db,0x716e,
+ 0x62c4,0x77a9,0x5631,0x4e3b,0x8457,0x67f1,0x52a9,0x86c0,0x8d2e,0x94f8,
+ 0x7b51
+ },
+ { /* ku 57 */
+ 0x8b46,0x8b47,0x8b48,0x8b49,0x8b4a,0x8b4b,0x8b4c,0x8b4d,0x8b4e,0x8b4f,
+ 0x8b50,0x8b51,0x8b52,0x8b53,0x8b54,0x8b55,0x8b56,0x8b57,0x8b58,0x8b59,
+ 0x8b5a,0x8b5b,0x8b5c,0x8b5d,0x8b5e,0x8b5f,0x8b60,0x8b61,0x8b62,0x8b63,
+ 0x8b64,0x8b65,0x8b67,0x8b68,0x8b69,0x8b6a,0x8b6b,0x8b6d,0x8b6e,0x8b6f,
+ 0x8b70,0x8b71,0x8b72,0x8b73,0x8b74,0x8b75,0x8b76,0x8b77,0x8b78,0x8b79,
+ 0x8b7a,0x8b7b,0x8b7c,0x8b7d,0x8b7e,0x8b7f,0x8b80,0x8b81,0x8b82,0x8b83,
+ 0x8b84,0x8b85,0x8b86,UBOGON,0x8b87,0x8b88,0x8b89,0x8b8a,0x8b8b,0x8b8c,
+ 0x8b8d,0x8b8e,0x8b8f,0x8b90,0x8b91,0x8b92,0x8b93,0x8b94,0x8b95,0x8b96,
+ 0x8b97,0x8b98,0x8b99,0x8b9a,0x8b9b,0x8b9c,0x8b9d,0x8b9e,0x8b9f,0x8bac,
+ 0x8bb1,0x8bbb,0x8bc7,0x8bd0,0x8bea,0x8c09,0x8c1e,0x4f4f,0x6ce8,0x795d,
+ 0x9a7b,0x6293,0x722a,0x62fd,0x4e13,0x7816,0x8f6c,0x64b0,0x8d5a,0x7bc6,
+ 0x6869,0x5e84,0x88c5,0x5986,0x649e,0x58ee,0x72b6,0x690e,0x9525,0x8ffd,
+ 0x8d58,0x5760,0x7f00,0x8c06,0x51c6,0x6349,0x62d9,0x5353,0x684c,0x7422,
+ 0x8301,0x914c,0x5544,0x7740,0x707c,0x6d4a,0x5179,0x54a8,0x8d44,0x59ff,
+ 0x6ecb,0x6dc4,0x5b5c,0x7d2b,0x4ed4,0x7c7d,0x6ed3,0x5b50,0x81ea,0x6e0d,
+ 0x5b57,0x9b03,0x68d5,0x8e2a,0x5b97,0x7efc,0x603b,0x7eb5,0x90b9,0x8d70,
+ 0x594f,0x63cd,0x79df,0x8db3,0x5352,0x65cf,0x7956,0x8bc5,0x963b,0x7ec4,
+ 0x94bb,0x7e82,0x5634,0x9189,0x6700,0x7f6a,0x5c0a,0x9075,0x6628,0x5de6,
+ 0x4f50,0x67de,0x505a,0x4f5c,0x5750,0x5ea7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 58 */
+ 0x8c38,0x8c39,0x8c3a,0x8c3b,0x8c3c,0x8c3d,0x8c3e,0x8c3f,0x8c40,0x8c42,
+ 0x8c43,0x8c44,0x8c45,0x8c48,0x8c4a,0x8c4b,0x8c4d,0x8c4e,0x8c4f,0x8c50,
+ 0x8c51,0x8c52,0x8c53,0x8c54,0x8c56,0x8c57,0x8c58,0x8c59,0x8c5b,0x8c5c,
+ 0x8c5d,0x8c5e,0x8c5f,0x8c60,0x8c63,0x8c64,0x8c65,0x8c66,0x8c67,0x8c68,
+ 0x8c69,0x8c6c,0x8c6d,0x8c6e,0x8c6f,0x8c70,0x8c71,0x8c72,0x8c74,0x8c75,
+ 0x8c76,0x8c77,0x8c7b,0x8c7c,0x8c7d,0x8c7e,0x8c7f,0x8c80,0x8c81,0x8c83,
+ 0x8c84,0x8c86,0x8c87,UBOGON,0x8c88,0x8c8b,0x8c8d,0x8c8e,0x8c8f,0x8c90,
+ 0x8c91,0x8c92,0x8c93,0x8c95,0x8c96,0x8c97,0x8c99,0x8c9a,0x8c9b,0x8c9c,
+ 0x8c9d,0x8c9e,0x8c9f,0x8ca0,0x8ca1,0x8ca2,0x8ca3,0x8ca4,0x8ca5,0x8ca6,
+ 0x8ca7,0x8ca8,0x8ca9,0x8caa,0x8cab,0x8cac,0x8cad,0x4e8d,0x4e0c,0x5140,
+ 0x4e10,0x5eff,0x5345,0x4e15,0x4e98,0x4e1e,0x9b32,0x5b6c,0x5669,0x4e28,
+ 0x79ba,0x4e3f,0x5315,0x4e47,0x592d,0x723b,0x536e,0x6c10,0x56df,0x80e4,
+ 0x9997,0x6bd3,0x777e,0x9f17,0x4e36,0x4e9f,0x9f10,0x4e5c,0x4e69,0x4e93,
+ 0x8288,0x5b5b,0x556c,0x560f,0x4ec4,0x538d,0x539d,0x53a3,0x53a5,0x53ae,
+ 0x9765,0x8d5d,0x531a,0x53f5,0x5326,0x532e,0x533e,0x8d5c,0x5366,0x5363,
+ 0x5202,0x5208,0x520e,0x522d,0x5233,0x523f,0x5240,0x524c,0x525e,0x5261,
+ 0x525c,0x84af,0x527d,0x5282,0x5281,0x5290,0x5293,0x5182,0x7f54,0x4ebb,
+ 0x4ec3,0x4ec9,0x4ec2,0x4ee8,0x4ee1,0x4eeb,0x4ede,0x4f1b,0x4ef3,0x4f22,
+ 0x4f64,0x4ef5,0x4f25,0x4f27,0x4f09,0x4f2b,0x4f5e,0x4f67,0x6538,0x4f5a,
+ 0x4f5d
+ },
+ { /* ku 59 */
+ 0x8cae,0x8caf,0x8cb0,0x8cb1,0x8cb2,0x8cb3,0x8cb4,0x8cb5,0x8cb6,0x8cb7,
+ 0x8cb8,0x8cb9,0x8cba,0x8cbb,0x8cbc,0x8cbd,0x8cbe,0x8cbf,0x8cc0,0x8cc1,
+ 0x8cc2,0x8cc3,0x8cc4,0x8cc5,0x8cc6,0x8cc7,0x8cc8,0x8cc9,0x8cca,0x8ccb,
+ 0x8ccc,0x8ccd,0x8cce,0x8ccf,0x8cd0,0x8cd1,0x8cd2,0x8cd3,0x8cd4,0x8cd5,
+ 0x8cd6,0x8cd7,0x8cd8,0x8cd9,0x8cda,0x8cdb,0x8cdc,0x8cdd,0x8cde,0x8cdf,
+ 0x8ce0,0x8ce1,0x8ce2,0x8ce3,0x8ce4,0x8ce5,0x8ce6,0x8ce7,0x8ce8,0x8ce9,
+ 0x8cea,0x8ceb,0x8cec,UBOGON,0x8ced,0x8cee,0x8cef,0x8cf0,0x8cf1,0x8cf2,
+ 0x8cf3,0x8cf4,0x8cf5,0x8cf6,0x8cf7,0x8cf8,0x8cf9,0x8cfa,0x8cfb,0x8cfc,
+ 0x8cfd,0x8cfe,0x8cff,0x8d00,0x8d01,0x8d02,0x8d03,0x8d04,0x8d05,0x8d06,
+ 0x8d07,0x8d08,0x8d09,0x8d0a,0x8d0b,0x8d0c,0x8d0d,0x4f5f,0x4f57,0x4f32,
+ 0x4f3d,0x4f76,0x4f74,0x4f91,0x4f89,0x4f83,0x4f8f,0x4f7e,0x4f7b,0x4faa,
+ 0x4f7c,0x4fac,0x4f94,0x4fe6,0x4fe8,0x4fea,0x4fc5,0x4fda,0x4fe3,0x4fdc,
+ 0x4fd1,0x4fdf,0x4ff8,0x5029,0x504c,0x4ff3,0x502c,0x500f,0x502e,0x502d,
+ 0x4ffe,0x501c,0x500c,0x5025,0x5028,0x507e,0x5043,0x5055,0x5048,0x504e,
+ 0x506c,0x507b,0x50a5,0x50a7,0x50a9,0x50ba,0x50d6,0x5106,0x50ed,0x50ec,
+ 0x50e6,0x50ee,0x5107,0x510b,0x4edd,0x6c3d,0x4f58,0x4f65,0x4fce,0x9fa0,
+ 0x6c46,0x7c74,0x516e,0x5dfd,0x9ec9,0x9998,0x5181,0x5914,0x52f9,0x530d,
+ 0x8a07,0x5310,0x51eb,0x5919,0x5155,0x4ea0,0x5156,0x4eb3,0x886e,0x88a4,
+ 0x4eb5,0x8114,0x88d2,0x7980,0x5b34,0x8803,0x7fb8,0x51ab,0x51b1,0x51bd,
+ 0x51bc
+ },
+ { /* ku 5a */
+ 0x8d0e,0x8d0f,0x8d10,0x8d11,0x8d12,0x8d13,0x8d14,0x8d15,0x8d16,0x8d17,
+ 0x8d18,0x8d19,0x8d1a,0x8d1b,0x8d1c,0x8d20,0x8d51,0x8d52,0x8d57,0x8d5f,
+ 0x8d65,0x8d68,0x8d69,0x8d6a,0x8d6c,0x8d6e,0x8d6f,0x8d71,0x8d72,0x8d78,
+ 0x8d79,0x8d7a,0x8d7b,0x8d7c,0x8d7d,0x8d7e,0x8d7f,0x8d80,0x8d82,0x8d83,
+ 0x8d86,0x8d87,0x8d88,0x8d89,0x8d8c,0x8d8d,0x8d8e,0x8d8f,0x8d90,0x8d92,
+ 0x8d93,0x8d95,0x8d96,0x8d97,0x8d98,0x8d99,0x8d9a,0x8d9b,0x8d9c,0x8d9d,
+ 0x8d9e,0x8da0,0x8da1,UBOGON,0x8da2,0x8da4,0x8da5,0x8da6,0x8da7,0x8da8,
+ 0x8da9,0x8daa,0x8dab,0x8dac,0x8dad,0x8dae,0x8daf,0x8db0,0x8db2,0x8db6,
+ 0x8db7,0x8db9,0x8dbb,0x8dbd,0x8dc0,0x8dc1,0x8dc2,0x8dc5,0x8dc7,0x8dc8,
+ 0x8dc9,0x8dca,0x8dcd,0x8dd0,0x8dd2,0x8dd3,0x8dd4,0x51c7,0x5196,0x51a2,
+ 0x51a5,0x8ba0,0x8ba6,0x8ba7,0x8baa,0x8bb4,0x8bb5,0x8bb7,0x8bc2,0x8bc3,
+ 0x8bcb,0x8bcf,0x8bce,0x8bd2,0x8bd3,0x8bd4,0x8bd6,0x8bd8,0x8bd9,0x8bdc,
+ 0x8bdf,0x8be0,0x8be4,0x8be8,0x8be9,0x8bee,0x8bf0,0x8bf3,0x8bf6,0x8bf9,
+ 0x8bfc,0x8bff,0x8c00,0x8c02,0x8c04,0x8c07,0x8c0c,0x8c0f,0x8c11,0x8c12,
+ 0x8c14,0x8c15,0x8c16,0x8c19,0x8c1b,0x8c18,0x8c1d,0x8c1f,0x8c20,0x8c21,
+ 0x8c25,0x8c27,0x8c2a,0x8c2b,0x8c2e,0x8c2f,0x8c32,0x8c33,0x8c35,0x8c36,
+ 0x5369,0x537a,0x961d,0x9622,0x9621,0x9631,0x962a,0x963d,0x963c,0x9642,
+ 0x9649,0x9654,0x965f,0x9667,0x966c,0x9672,0x9674,0x9688,0x968d,0x9697,
+ 0x96b0,0x9097,0x909b,0x909d,0x9099,0x90ac,0x90a1,0x90b4,0x90b3,0x90b6,
+ 0x90ba
+ },
+ { /* ku 5b */
+ 0x8dd5,0x8dd8,0x8dd9,0x8ddc,0x8de0,0x8de1,0x8de2,0x8de5,0x8de6,0x8de7,
+ 0x8de9,0x8ded,0x8dee,0x8df0,0x8df1,0x8df2,0x8df4,0x8df6,0x8dfc,0x8dfe,
+ 0x8dff,0x8e00,0x8e01,0x8e02,0x8e03,0x8e04,0x8e06,0x8e07,0x8e08,0x8e0b,
+ 0x8e0d,0x8e0e,0x8e10,0x8e11,0x8e12,0x8e13,0x8e15,0x8e16,0x8e17,0x8e18,
+ 0x8e19,0x8e1a,0x8e1b,0x8e1c,0x8e20,0x8e21,0x8e24,0x8e25,0x8e26,0x8e27,
+ 0x8e28,0x8e2b,0x8e2d,0x8e30,0x8e32,0x8e33,0x8e34,0x8e36,0x8e37,0x8e38,
+ 0x8e3b,0x8e3c,0x8e3e,UBOGON,0x8e3f,0x8e43,0x8e45,0x8e46,0x8e4c,0x8e4d,
+ 0x8e4e,0x8e4f,0x8e50,0x8e53,0x8e54,0x8e55,0x8e56,0x8e57,0x8e58,0x8e5a,
+ 0x8e5b,0x8e5c,0x8e5d,0x8e5e,0x8e5f,0x8e60,0x8e61,0x8e62,0x8e63,0x8e64,
+ 0x8e65,0x8e67,0x8e68,0x8e6a,0x8e6b,0x8e6e,0x8e71,0x90b8,0x90b0,0x90cf,
+ 0x90c5,0x90be,0x90d0,0x90c4,0x90c7,0x90d3,0x90e6,0x90e2,0x90dc,0x90d7,
+ 0x90db,0x90eb,0x90ef,0x90fe,0x9104,0x9122,0x911e,0x9123,0x9131,0x912f,
+ 0x9139,0x9143,0x9146,0x520d,0x5942,0x52a2,0x52ac,0x52ad,0x52be,0x54ff,
+ 0x52d0,0x52d6,0x52f0,0x53df,0x71ee,0x77cd,0x5ef4,0x51f5,0x51fc,0x9b2f,
+ 0x53b6,0x5f01,0x755a,0x5def,0x574c,0x57a9,0x57a1,0x587e,0x58bc,0x58c5,
+ 0x58d1,0x5729,0x572c,0x572a,0x5733,0x5739,0x572e,0x572f,0x575c,0x573b,
+ 0x5742,0x5769,0x5785,0x576b,0x5786,0x577c,0x577b,0x5768,0x576d,0x5776,
+ 0x5773,0x57ad,0x57a4,0x578c,0x57b2,0x57cf,0x57a7,0x57b4,0x5793,0x57a0,
+ 0x57d5,0x57d8,0x57da,0x57d9,0x57d2,0x57b8,0x57f4,0x57ef,0x57f8,0x57e4,
+ 0x57dd
+ },
+ { /* ku 5c */
+ 0x8e73,0x8e75,0x8e77,0x8e78,0x8e79,0x8e7a,0x8e7b,0x8e7d,0x8e7e,0x8e80,
+ 0x8e82,0x8e83,0x8e84,0x8e86,0x8e88,0x8e89,0x8e8a,0x8e8b,0x8e8c,0x8e8d,
+ 0x8e8e,0x8e91,0x8e92,0x8e93,0x8e95,0x8e96,0x8e97,0x8e98,0x8e99,0x8e9a,
+ 0x8e9b,0x8e9d,0x8e9f,0x8ea0,0x8ea1,0x8ea2,0x8ea3,0x8ea4,0x8ea5,0x8ea6,
+ 0x8ea7,0x8ea8,0x8ea9,0x8eaa,0x8ead,0x8eae,0x8eb0,0x8eb1,0x8eb3,0x8eb4,
+ 0x8eb5,0x8eb6,0x8eb7,0x8eb8,0x8eb9,0x8ebb,0x8ebc,0x8ebd,0x8ebe,0x8ebf,
+ 0x8ec0,0x8ec1,0x8ec2,UBOGON,0x8ec3,0x8ec4,0x8ec5,0x8ec6,0x8ec7,0x8ec8,
+ 0x8ec9,0x8eca,0x8ecb,0x8ecc,0x8ecd,0x8ecf,0x8ed0,0x8ed1,0x8ed2,0x8ed3,
+ 0x8ed4,0x8ed5,0x8ed6,0x8ed7,0x8ed8,0x8ed9,0x8eda,0x8edb,0x8edc,0x8edd,
+ 0x8ede,0x8edf,0x8ee0,0x8ee1,0x8ee2,0x8ee3,0x8ee4,0x580b,0x580d,0x57fd,
+ 0x57ed,0x5800,0x581e,0x5819,0x5844,0x5820,0x5865,0x586c,0x5881,0x5889,
+ 0x589a,0x5880,0x99a8,0x9f19,0x61ff,0x8279,0x827d,0x827f,0x828f,0x828a,
+ 0x82a8,0x8284,0x828e,0x8291,0x8297,0x8299,0x82ab,0x82b8,0x82be,0x82b0,
+ 0x82c8,0x82ca,0x82e3,0x8298,0x82b7,0x82ae,0x82cb,0x82cc,0x82c1,0x82a9,
+ 0x82b4,0x82a1,0x82aa,0x829f,0x82c4,0x82ce,0x82a4,0x82e1,0x8309,0x82f7,
+ 0x82e4,0x830f,0x8307,0x82dc,0x82f4,0x82d2,0x82d8,0x830c,0x82fb,0x82d3,
+ 0x8311,0x831a,0x8306,0x8314,0x8315,0x82e0,0x82d5,0x831c,0x8351,0x835b,
+ 0x835c,0x8308,0x8392,0x833c,0x8334,0x8331,0x839b,0x835e,0x832f,0x834f,
+ 0x8347,0x8343,0x835f,0x8340,0x8317,0x8360,0x832d,0x833a,0x8333,0x8366,
+ 0x8365
+ },
+ { /* ku 5d */
+ 0x8ee5,0x8ee6,0x8ee7,0x8ee8,0x8ee9,0x8eea,0x8eeb,0x8eec,0x8eed,0x8eee,
+ 0x8eef,0x8ef0,0x8ef1,0x8ef2,0x8ef3,0x8ef4,0x8ef5,0x8ef6,0x8ef7,0x8ef8,
+ 0x8ef9,0x8efa,0x8efb,0x8efc,0x8efd,0x8efe,0x8eff,0x8f00,0x8f01,0x8f02,
+ 0x8f03,0x8f04,0x8f05,0x8f06,0x8f07,0x8f08,0x8f09,0x8f0a,0x8f0b,0x8f0c,
+ 0x8f0d,0x8f0e,0x8f0f,0x8f10,0x8f11,0x8f12,0x8f13,0x8f14,0x8f15,0x8f16,
+ 0x8f17,0x8f18,0x8f19,0x8f1a,0x8f1b,0x8f1c,0x8f1d,0x8f1e,0x8f1f,0x8f20,
+ 0x8f21,0x8f22,0x8f23,UBOGON,0x8f24,0x8f25,0x8f26,0x8f27,0x8f28,0x8f29,
+ 0x8f2a,0x8f2b,0x8f2c,0x8f2d,0x8f2e,0x8f2f,0x8f30,0x8f31,0x8f32,0x8f33,
+ 0x8f34,0x8f35,0x8f36,0x8f37,0x8f38,0x8f39,0x8f3a,0x8f3b,0x8f3c,0x8f3d,
+ 0x8f3e,0x8f3f,0x8f40,0x8f41,0x8f42,0x8f43,0x8f44,0x8368,0x831b,0x8369,
+ 0x836c,0x836a,0x836d,0x836e,0x83b0,0x8378,0x83b3,0x83b4,0x83a0,0x83aa,
+ 0x8393,0x839c,0x8385,0x837c,0x83b6,0x83a9,0x837d,0x83b8,0x837b,0x8398,
+ 0x839e,0x83a8,0x83ba,0x83bc,0x83c1,0x8401,0x83e5,0x83d8,0x5807,0x8418,
+ 0x840b,0x83dd,0x83fd,0x83d6,0x841c,0x8438,0x8411,0x8406,0x83d4,0x83df,
+ 0x840f,0x8403,0x83f8,0x83f9,0x83ea,0x83c5,0x83c0,0x8426,0x83f0,0x83e1,
+ 0x845c,0x8451,0x845a,0x8459,0x8473,0x8487,0x8488,0x847a,0x8489,0x8478,
+ 0x843c,0x8446,0x8469,0x8476,0x848c,0x848e,0x8431,0x846d,0x84c1,0x84cd,
+ 0x84d0,0x84e6,0x84bd,0x84d3,0x84ca,0x84bf,0x84ba,0x84e0,0x84a1,0x84b9,
+ 0x84b4,0x8497,0x84e5,0x84e3,0x850c,0x750d,0x8538,0x84f0,0x8539,0x851f,
+ 0x853a
+ },
+ { /* ku 5e */
+ 0x8f45,0x8f46,0x8f47,0x8f48,0x8f49,0x8f4a,0x8f4b,0x8f4c,0x8f4d,0x8f4e,
+ 0x8f4f,0x8f50,0x8f51,0x8f52,0x8f53,0x8f54,0x8f55,0x8f56,0x8f57,0x8f58,
+ 0x8f59,0x8f5a,0x8f5b,0x8f5c,0x8f5d,0x8f5e,0x8f5f,0x8f60,0x8f61,0x8f62,
+ 0x8f63,0x8f64,0x8f65,0x8f6a,0x8f80,0x8f8c,0x8f92,0x8f9d,0x8fa0,0x8fa1,
+ 0x8fa2,0x8fa4,0x8fa5,0x8fa6,0x8fa7,0x8faa,0x8fac,0x8fad,0x8fae,0x8faf,
+ 0x8fb2,0x8fb3,0x8fb4,0x8fb5,0x8fb7,0x8fb8,0x8fba,0x8fbb,0x8fbc,0x8fbf,
+ 0x8fc0,0x8fc3,0x8fc6,UBOGON,0x8fc9,0x8fca,0x8fcb,0x8fcc,0x8fcd,0x8fcf,
+ 0x8fd2,0x8fd6,0x8fd7,0x8fda,0x8fe0,0x8fe1,0x8fe3,0x8fe7,0x8fec,0x8fef,
+ 0x8ff1,0x8ff2,0x8ff4,0x8ff5,0x8ff6,0x8ffa,0x8ffb,0x8ffc,0x8ffe,0x8fff,
+ 0x9007,0x9008,0x900c,0x900e,0x9013,0x9015,0x9018,0x8556,0x853b,0x84ff,
+ 0x84fc,0x8559,0x8548,0x8568,0x8564,0x855e,0x857a,0x77a2,0x8543,0x8572,
+ 0x857b,0x85a4,0x85a8,0x8587,0x858f,0x8579,0x85ae,0x859c,0x8585,0x85b9,
+ 0x85b7,0x85b0,0x85d3,0x85c1,0x85dc,0x85ff,0x8627,0x8605,0x8629,0x8616,
+ 0x863c,0x5efe,0x5f08,0x593c,0x5941,0x8037,0x5955,0x595a,0x5958,0x530f,
+ 0x5c22,0x5c25,0x5c2c,0x5c34,0x624c,0x626a,0x629f,0x62bb,0x62ca,0x62da,
+ 0x62d7,0x62ee,0x6322,0x62f6,0x6339,0x634b,0x6343,0x63ad,0x63f6,0x6371,
+ 0x637a,0x638e,0x63b4,0x636d,0x63ac,0x638a,0x6369,0x63ae,0x63bc,0x63f2,
+ 0x63f8,0x63e0,0x63ff,0x63c4,0x63de,0x63ce,0x6452,0x63c6,0x63be,0x6445,
+ 0x6441,0x640b,0x641b,0x6420,0x640c,0x6426,0x6421,0x645e,0x6484,0x646d,
+ 0x6496
+ },
+ { /* ku 5f */
+ 0x9019,0x901c,0x9023,0x9024,0x9025,0x9027,0x9028,0x9029,0x902a,0x902b,
+ 0x902c,0x9030,0x9031,0x9032,0x9033,0x9034,0x9037,0x9039,0x903a,0x903d,
+ 0x903f,0x9040,0x9043,0x9045,0x9046,0x9048,0x9049,0x904a,0x904b,0x904c,
+ 0x904e,0x9054,0x9055,0x9056,0x9059,0x905a,0x905c,0x905d,0x905e,0x905f,
+ 0x9060,0x9061,0x9064,0x9066,0x9067,0x9069,0x906a,0x906b,0x906c,0x906f,
+ 0x9070,0x9071,0x9072,0x9073,0x9076,0x9077,0x9078,0x9079,0x907a,0x907b,
+ 0x907c,0x907e,0x9081,UBOGON,0x9084,0x9085,0x9086,0x9087,0x9089,0x908a,
+ 0x908c,0x908d,0x908e,0x908f,0x9090,0x9092,0x9094,0x9096,0x9098,0x909a,
+ 0x909c,0x909e,0x909f,0x90a0,0x90a4,0x90a5,0x90a7,0x90a8,0x90a9,0x90ab,
+ 0x90ad,0x90b2,0x90b7,0x90bc,0x90bd,0x90bf,0x90c0,0x647a,0x64b7,0x64b8,
+ 0x6499,0x64ba,0x64c0,0x64d0,0x64d7,0x64e4,0x64e2,0x6509,0x6525,0x652e,
+ 0x5f0b,0x5fd2,0x7519,0x5f11,0x535f,0x53f1,0x53fd,0x53e9,0x53e8,0x53fb,
+ 0x5412,0x5416,0x5406,0x544b,0x5452,0x5453,0x5454,0x5456,0x5443,0x5421,
+ 0x5457,0x5459,0x5423,0x5432,0x5482,0x5494,0x5477,0x5471,0x5464,0x549a,
+ 0x549b,0x5484,0x5476,0x5466,0x549d,0x54d0,0x54ad,0x54c2,0x54b4,0x54d2,
+ 0x54a7,0x54a6,0x54d3,0x54d4,0x5472,0x54a3,0x54d5,0x54bb,0x54bf,0x54cc,
+ 0x54d9,0x54da,0x54dc,0x54a9,0x54aa,0x54a4,0x54dd,0x54cf,0x54de,0x551b,
+ 0x54e7,0x5520,0x54fd,0x5514,0x54f3,0x5522,0x5523,0x550f,0x5511,0x5527,
+ 0x552a,0x5567,0x558f,0x55b5,0x5549,0x556d,0x5541,0x5555,0x553f,0x5550,
+ 0x553c
+ },
+ { /* ku 60 */
+ 0x90c2,0x90c3,0x90c6,0x90c8,0x90c9,0x90cb,0x90cc,0x90cd,0x90d2,0x90d4,
+ 0x90d5,0x90d6,0x90d8,0x90d9,0x90da,0x90de,0x90df,0x90e0,0x90e3,0x90e4,
+ 0x90e5,0x90e9,0x90ea,0x90ec,0x90ee,0x90f0,0x90f1,0x90f2,0x90f3,0x90f5,
+ 0x90f6,0x90f7,0x90f9,0x90fa,0x90fb,0x90fc,0x90ff,0x9100,0x9101,0x9103,
+ 0x9105,0x9106,0x9107,0x9108,0x9109,0x910a,0x910b,0x910c,0x910d,0x910e,
+ 0x910f,0x9110,0x9111,0x9112,0x9113,0x9114,0x9115,0x9116,0x9117,0x9118,
+ 0x911a,0x911b,0x911c,UBOGON,0x911d,0x911f,0x9120,0x9121,0x9124,0x9125,
+ 0x9126,0x9127,0x9128,0x9129,0x912a,0x912b,0x912c,0x912d,0x912e,0x9130,
+ 0x9132,0x9133,0x9134,0x9135,0x9136,0x9137,0x9138,0x913a,0x913b,0x913c,
+ 0x913d,0x913e,0x913f,0x9140,0x9141,0x9142,0x9144,0x5537,0x5556,0x5575,
+ 0x5576,0x5577,0x5533,0x5530,0x555c,0x558b,0x55d2,0x5583,0x55b1,0x55b9,
+ 0x5588,0x5581,0x559f,0x557e,0x55d6,0x5591,0x557b,0x55df,0x55bd,0x55be,
+ 0x5594,0x5599,0x55ea,0x55f7,0x55c9,0x561f,0x55d1,0x55eb,0x55ec,0x55d4,
+ 0x55e6,0x55dd,0x55c4,0x55ef,0x55e5,0x55f2,0x55f3,0x55cc,0x55cd,0x55e8,
+ 0x55f5,0x55e4,0x8f94,0x561e,0x5608,0x560c,0x5601,0x5624,0x5623,0x55fe,
+ 0x5600,0x5627,0x562d,0x5658,0x5639,0x5657,0x562c,0x564d,0x5662,0x5659,
+ 0x565c,0x564c,0x5654,0x5686,0x5664,0x5671,0x566b,0x567b,0x567c,0x5685,
+ 0x5693,0x56af,0x56d4,0x56d7,0x56dd,0x56e1,0x56f5,0x56eb,0x56f9,0x56ff,
+ 0x5704,0x570a,0x5709,0x571c,0x5e0f,0x5e19,0x5e14,0x5e11,0x5e31,0x5e3b,
+ 0x5e3c
+ },
+ { /* ku 61 */
+ 0x9145,0x9147,0x9148,0x9151,0x9153,0x9154,0x9155,0x9156,0x9158,0x9159,
+ 0x915b,0x915c,0x915f,0x9160,0x9166,0x9167,0x9168,0x916b,0x916d,0x9173,
+ 0x917a,0x917b,0x917c,0x9180,0x9181,0x9182,0x9183,0x9184,0x9186,0x9188,
+ 0x918a,0x918e,0x918f,0x9193,0x9194,0x9195,0x9196,0x9197,0x9198,0x9199,
+ 0x919c,0x919d,0x919e,0x919f,0x91a0,0x91a1,0x91a4,0x91a5,0x91a6,0x91a7,
+ 0x91a8,0x91a9,0x91ab,0x91ac,0x91b0,0x91b1,0x91b2,0x91b3,0x91b6,0x91b7,
+ 0x91b8,0x91b9,0x91bb,UBOGON,0x91bc,0x91bd,0x91be,0x91bf,0x91c0,0x91c1,
+ 0x91c2,0x91c3,0x91c4,0x91c5,0x91c6,0x91c8,0x91cb,0x91d0,0x91d2,0x91d3,
+ 0x91d4,0x91d5,0x91d6,0x91d7,0x91d8,0x91d9,0x91da,0x91db,0x91dd,0x91de,
+ 0x91df,0x91e0,0x91e1,0x91e2,0x91e3,0x91e4,0x91e5,0x5e37,0x5e44,0x5e54,
+ 0x5e5b,0x5e5e,0x5e61,0x5c8c,0x5c7a,0x5c8d,0x5c90,0x5c96,0x5c88,0x5c98,
+ 0x5c99,0x5c91,0x5c9a,0x5c9c,0x5cb5,0x5ca2,0x5cbd,0x5cac,0x5cab,0x5cb1,
+ 0x5ca3,0x5cc1,0x5cb7,0x5cc4,0x5cd2,0x5ce4,0x5ccb,0x5ce5,0x5d02,0x5d03,
+ 0x5d27,0x5d26,0x5d2e,0x5d24,0x5d1e,0x5d06,0x5d1b,0x5d58,0x5d3e,0x5d34,
+ 0x5d3d,0x5d6c,0x5d5b,0x5d6f,0x5d5d,0x5d6b,0x5d4b,0x5d4a,0x5d69,0x5d74,
+ 0x5d82,0x5d99,0x5d9d,0x8c73,0x5db7,0x5dc5,0x5f73,0x5f77,0x5f82,0x5f87,
+ 0x5f89,0x5f8c,0x5f95,0x5f99,0x5f9c,0x5fa8,0x5fad,0x5fb5,0x5fbc,0x8862,
+ 0x5f61,0x72ad,0x72b0,0x72b4,0x72b7,0x72b8,0x72c3,0x72c1,0x72ce,0x72cd,
+ 0x72d2,0x72e8,0x72ef,0x72e9,0x72f2,0x72f4,0x72f7,0x7301,0x72f3,0x7303,
+ 0x72fa
+ },
+ { /* ku 62 */
+ 0x91e6,0x91e7,0x91e8,0x91e9,0x91ea,0x91eb,0x91ec,0x91ed,0x91ee,0x91ef,
+ 0x91f0,0x91f1,0x91f2,0x91f3,0x91f4,0x91f5,0x91f6,0x91f7,0x91f8,0x91f9,
+ 0x91fa,0x91fb,0x91fc,0x91fd,0x91fe,0x91ff,0x9200,0x9201,0x9202,0x9203,
+ 0x9204,0x9205,0x9206,0x9207,0x9208,0x9209,0x920a,0x920b,0x920c,0x920d,
+ 0x920e,0x920f,0x9210,0x9211,0x9212,0x9213,0x9214,0x9215,0x9216,0x9217,
+ 0x9218,0x9219,0x921a,0x921b,0x921c,0x921d,0x921e,0x921f,0x9220,0x9221,
+ 0x9222,0x9223,0x9224,UBOGON,0x9225,0x9226,0x9227,0x9228,0x9229,0x922a,
+ 0x922b,0x922c,0x922d,0x922e,0x922f,0x9230,0x9231,0x9232,0x9233,0x9234,
+ 0x9235,0x9236,0x9237,0x9238,0x9239,0x923a,0x923b,0x923c,0x923d,0x923e,
+ 0x923f,0x9240,0x9241,0x9242,0x9243,0x9244,0x9245,0x72fb,0x7317,0x7313,
+ 0x7321,0x730a,0x731e,0x731d,0x7315,0x7322,0x7339,0x7325,0x732c,0x7338,
+ 0x7331,0x7350,0x734d,0x7357,0x7360,0x736c,0x736f,0x737e,0x821b,0x5925,
+ 0x98e7,0x5924,0x5902,0x9963,0x9967,0x9968,0x9969,0x996a,0x996b,0x996c,
+ 0x9974,0x9977,0x997d,0x9980,0x9984,0x9987,0x998a,0x998d,0x9990,0x9991,
+ 0x9993,0x9994,0x9995,0x5e80,0x5e91,0x5e8b,0x5e96,0x5ea5,0x5ea0,0x5eb9,
+ 0x5eb5,0x5ebe,0x5eb3,0x8d53,0x5ed2,0x5ed1,0x5edb,0x5ee8,0x5eea,0x81ba,
+ 0x5fc4,0x5fc9,0x5fd6,0x5fcf,0x6003,0x5fee,0x6004,0x5fe1,0x5fe4,0x5ffe,
+ 0x6005,0x6006,0x5fea,0x5fed,0x5ff8,0x6019,0x6035,0x6026,0x601b,0x600f,
+ 0x600d,0x6029,0x602b,0x600a,0x603f,0x6021,0x6078,0x6079,0x607b,0x607a,
+ 0x6042
+ },
+ { /* ku 63 */
+ 0x9246,0x9247,0x9248,0x9249,0x924a,0x924b,0x924c,0x924d,0x924e,0x924f,
+ 0x9250,0x9251,0x9252,0x9253,0x9254,0x9255,0x9256,0x9257,0x9258,0x9259,
+ 0x925a,0x925b,0x925c,0x925d,0x925e,0x925f,0x9260,0x9261,0x9262,0x9263,
+ 0x9264,0x9265,0x9266,0x9267,0x9268,0x9269,0x926a,0x926b,0x926c,0x926d,
+ 0x926e,0x926f,0x9270,0x9271,0x9272,0x9273,0x9275,0x9276,0x9277,0x9278,
+ 0x9279,0x927a,0x927b,0x927c,0x927d,0x927e,0x927f,0x9280,0x9281,0x9282,
+ 0x9283,0x9284,0x9285,UBOGON,0x9286,0x9287,0x9288,0x9289,0x928a,0x928b,
+ 0x928c,0x928d,0x928f,0x9290,0x9291,0x9292,0x9293,0x9294,0x9295,0x9296,
+ 0x9297,0x9298,0x9299,0x929a,0x929b,0x929c,0x929d,0x929e,0x929f,0x92a0,
+ 0x92a1,0x92a2,0x92a3,0x92a4,0x92a5,0x92a6,0x92a7,0x606a,0x607d,0x6096,
+ 0x609a,0x60ad,0x609d,0x6083,0x6092,0x608c,0x609b,0x60ec,0x60bb,0x60b1,
+ 0x60dd,0x60d8,0x60c6,0x60da,0x60b4,0x6120,0x6126,0x6115,0x6123,0x60f4,
+ 0x6100,0x610e,0x612b,0x614a,0x6175,0x61ac,0x6194,0x61a7,0x61b7,0x61d4,
+ 0x61f5,0x5fdd,0x96b3,0x95e9,0x95eb,0x95f1,0x95f3,0x95f5,0x95f6,0x95fc,
+ 0x95fe,0x9603,0x9604,0x9606,0x9608,0x960a,0x960b,0x960c,0x960d,0x960f,
+ 0x9612,0x9615,0x9616,0x9617,0x9619,0x961a,0x4e2c,0x723f,0x6215,0x6c35,
+ 0x6c54,0x6c5c,0x6c4a,0x6ca3,0x6c85,0x6c90,0x6c94,0x6c8c,0x6c68,0x6c69,
+ 0x6c74,0x6c76,0x6c86,0x6ca9,0x6cd0,0x6cd4,0x6cad,0x6cf7,0x6cf8,0x6cf1,
+ 0x6cd7,0x6cb2,0x6ce0,0x6cd6,0x6cfa,0x6ceb,0x6cee,0x6cb1,0x6cd3,0x6cef,
+ 0x6cfe
+ },
+ { /* ku 64 */
+ 0x92a8,0x92a9,0x92aa,0x92ab,0x92ac,0x92ad,0x92af,0x92b0,0x92b1,0x92b2,
+ 0x92b3,0x92b4,0x92b5,0x92b6,0x92b7,0x92b8,0x92b9,0x92ba,0x92bb,0x92bc,
+ 0x92bd,0x92be,0x92bf,0x92c0,0x92c1,0x92c2,0x92c3,0x92c4,0x92c5,0x92c6,
+ 0x92c7,0x92c9,0x92ca,0x92cb,0x92cc,0x92cd,0x92ce,0x92cf,0x92d0,0x92d1,
+ 0x92d2,0x92d3,0x92d4,0x92d5,0x92d6,0x92d7,0x92d8,0x92d9,0x92da,0x92db,
+ 0x92dc,0x92dd,0x92de,0x92df,0x92e0,0x92e1,0x92e2,0x92e3,0x92e4,0x92e5,
+ 0x92e6,0x92e7,0x92e8,UBOGON,0x92e9,0x92ea,0x92eb,0x92ec,0x92ed,0x92ee,
+ 0x92ef,0x92f0,0x92f1,0x92f2,0x92f3,0x92f4,0x92f5,0x92f6,0x92f7,0x92f8,
+ 0x92f9,0x92fa,0x92fb,0x92fc,0x92fd,0x92fe,0x92ff,0x9300,0x9301,0x9302,
+ 0x9303,0x9304,0x9305,0x9306,0x9307,0x9308,0x9309,0x6d39,0x6d27,0x6d0c,
+ 0x6d43,0x6d48,0x6d07,0x6d04,0x6d19,0x6d0e,0x6d2b,0x6d4d,0x6d2e,0x6d35,
+ 0x6d1a,0x6d4f,0x6d52,0x6d54,0x6d33,0x6d91,0x6d6f,0x6d9e,0x6da0,0x6d5e,
+ 0x6d93,0x6d94,0x6d5c,0x6d60,0x6d7c,0x6d63,0x6e1a,0x6dc7,0x6dc5,0x6dde,
+ 0x6e0e,0x6dbf,0x6de0,0x6e11,0x6de6,0x6ddd,0x6dd9,0x6e16,0x6dab,0x6e0c,
+ 0x6dae,0x6e2b,0x6e6e,0x6e4e,0x6e6b,0x6eb2,0x6e5f,0x6e86,0x6e53,0x6e54,
+ 0x6e32,0x6e25,0x6e44,0x6edf,0x6eb1,0x6e98,0x6ee0,0x6f2d,0x6ee2,0x6ea5,
+ 0x6ea7,0x6ebd,0x6ebb,0x6eb7,0x6ed7,0x6eb4,0x6ecf,0x6e8f,0x6ec2,0x6e9f,
+ 0x6f62,0x6f46,0x6f47,0x6f24,0x6f15,0x6ef9,0x6f2f,0x6f36,0x6f4b,0x6f74,
+ 0x6f2a,0x6f09,0x6f29,0x6f89,0x6f8d,0x6f8c,0x6f78,0x6f72,0x6f7c,0x6f7a,
+ 0x6fd1
+ },
+ { /* ku 65 */
+ 0x930a,0x930b,0x930c,0x930d,0x930e,0x930f,0x9310,0x9311,0x9312,0x9313,
+ 0x9314,0x9315,0x9316,0x9317,0x9318,0x9319,0x931a,0x931b,0x931c,0x931d,
+ 0x931e,0x931f,0x9320,0x9321,0x9322,0x9323,0x9324,0x9325,0x9326,0x9327,
+ 0x9328,0x9329,0x932a,0x932b,0x932c,0x932d,0x932e,0x932f,0x9330,0x9331,
+ 0x9332,0x9333,0x9334,0x9335,0x9336,0x9337,0x9338,0x9339,0x933a,0x933b,
+ 0x933c,0x933d,0x933f,0x9340,0x9341,0x9342,0x9343,0x9344,0x9345,0x9346,
+ 0x9347,0x9348,0x9349,UBOGON,0x934a,0x934b,0x934c,0x934d,0x934e,0x934f,
+ 0x9350,0x9351,0x9352,0x9353,0x9354,0x9355,0x9356,0x9357,0x9358,0x9359,
+ 0x935a,0x935b,0x935c,0x935d,0x935e,0x935f,0x9360,0x9361,0x9362,0x9363,
+ 0x9364,0x9365,0x9366,0x9367,0x9368,0x9369,0x936b,0x6fc9,0x6fa7,0x6fb9,
+ 0x6fb6,0x6fc2,0x6fe1,0x6fee,0x6fde,0x6fe0,0x6fef,0x701a,0x7023,0x701b,
+ 0x7039,0x7035,0x704f,0x705e,0x5b80,0x5b84,0x5b95,0x5b93,0x5ba5,0x5bb8,
+ 0x752f,0x9a9e,0x6434,0x5be4,0x5bee,0x8930,0x5bf0,0x8e47,0x8b07,0x8fb6,
+ 0x8fd3,0x8fd5,0x8fe5,0x8fee,0x8fe4,0x8fe9,0x8fe6,0x8ff3,0x8fe8,0x9005,
+ 0x9004,0x900b,0x9026,0x9011,0x900d,0x9016,0x9021,0x9035,0x9036,0x902d,
+ 0x902f,0x9044,0x9051,0x9052,0x9050,0x9068,0x9058,0x9062,0x905b,0x66b9,
+ 0x9074,0x907d,0x9082,0x9088,0x9083,0x908b,0x5f50,0x5f57,0x5f56,0x5f58,
+ 0x5c3b,0x54ab,0x5c50,0x5c59,0x5b71,0x5c63,0x5c66,0x7fbc,0x5f2a,0x5f29,
+ 0x5f2d,0x8274,0x5f3c,0x9b3b,0x5c6e,0x5981,0x5983,0x598d,0x59a9,0x59aa,
+ 0x59a3
+ },
+ { /* ku 66 */
+ 0x936c,0x936d,0x936e,0x936f,0x9370,0x9371,0x9372,0x9373,0x9374,0x9375,
+ 0x9376,0x9377,0x9378,0x9379,0x937a,0x937b,0x937c,0x937d,0x937e,0x937f,
+ 0x9380,0x9381,0x9382,0x9383,0x9384,0x9385,0x9386,0x9387,0x9388,0x9389,
+ 0x938a,0x938b,0x938c,0x938d,0x938e,0x9390,0x9391,0x9392,0x9393,0x9394,
+ 0x9395,0x9396,0x9397,0x9398,0x9399,0x939a,0x939b,0x939c,0x939d,0x939e,
+ 0x939f,0x93a0,0x93a1,0x93a2,0x93a3,0x93a4,0x93a5,0x93a6,0x93a7,0x93a8,
+ 0x93a9,0x93aa,0x93ab,UBOGON,0x93ac,0x93ad,0x93ae,0x93af,0x93b0,0x93b1,
+ 0x93b2,0x93b3,0x93b4,0x93b5,0x93b6,0x93b7,0x93b8,0x93b9,0x93ba,0x93bb,
+ 0x93bc,0x93bd,0x93be,0x93bf,0x93c0,0x93c1,0x93c2,0x93c3,0x93c4,0x93c5,
+ 0x93c6,0x93c7,0x93c8,0x93c9,0x93cb,0x93cc,0x93cd,0x5997,0x59ca,0x59ab,
+ 0x599e,0x59a4,0x59d2,0x59b2,0x59af,0x59d7,0x59be,0x5a05,0x5a06,0x59dd,
+ 0x5a08,0x59e3,0x59d8,0x59f9,0x5a0c,0x5a09,0x5a32,0x5a34,0x5a11,0x5a23,
+ 0x5a13,0x5a40,0x5a67,0x5a4a,0x5a55,0x5a3c,0x5a62,0x5a75,0x80ec,0x5aaa,
+ 0x5a9b,0x5a77,0x5a7a,0x5abe,0x5aeb,0x5ab2,0x5ad2,0x5ad4,0x5ab8,0x5ae0,
+ 0x5ae3,0x5af1,0x5ad6,0x5ae6,0x5ad8,0x5adc,0x5b09,0x5b17,0x5b16,0x5b32,
+ 0x5b37,0x5b40,0x5c15,0x5c1c,0x5b5a,0x5b65,0x5b73,0x5b51,0x5b53,0x5b62,
+ 0x9a75,0x9a77,0x9a78,0x9a7a,0x9a7f,0x9a7d,0x9a80,0x9a81,0x9a85,0x9a88,
+ 0x9a8a,0x9a90,0x9a92,0x9a93,0x9a96,0x9a98,0x9a9b,0x9a9c,0x9a9d,0x9a9f,
+ 0x9aa0,0x9aa2,0x9aa3,0x9aa5,0x9aa7,0x7e9f,0x7ea1,0x7ea3,0x7ea5,0x7ea8,
+ 0x7ea9
+ },
+ { /* ku 67 */
+ 0x93ce,0x93cf,0x93d0,0x93d1,0x93d2,0x93d3,0x93d4,0x93d5,0x93d7,0x93d8,
+ 0x93d9,0x93da,0x93db,0x93dc,0x93dd,0x93de,0x93df,0x93e0,0x93e1,0x93e2,
+ 0x93e3,0x93e4,0x93e5,0x93e6,0x93e7,0x93e8,0x93e9,0x93ea,0x93eb,0x93ec,
+ 0x93ed,0x93ee,0x93ef,0x93f0,0x93f1,0x93f2,0x93f3,0x93f4,0x93f5,0x93f6,
+ 0x93f7,0x93f8,0x93f9,0x93fa,0x93fb,0x93fc,0x93fd,0x93fe,0x93ff,0x9400,
+ 0x9401,0x9402,0x9403,0x9404,0x9405,0x9406,0x9407,0x9408,0x9409,0x940a,
+ 0x940b,0x940c,0x940d,UBOGON,0x940e,0x940f,0x9410,0x9411,0x9412,0x9413,
+ 0x9414,0x9415,0x9416,0x9417,0x9418,0x9419,0x941a,0x941b,0x941c,0x941d,
+ 0x941e,0x941f,0x9420,0x9421,0x9422,0x9423,0x9424,0x9425,0x9426,0x9427,
+ 0x9428,0x9429,0x942a,0x942b,0x942c,0x942d,0x942e,0x7ead,0x7eb0,0x7ebe,
+ 0x7ec0,0x7ec1,0x7ec2,0x7ec9,0x7ecb,0x7ecc,0x7ed0,0x7ed4,0x7ed7,0x7edb,
+ 0x7ee0,0x7ee1,0x7ee8,0x7eeb,0x7eee,0x7eef,0x7ef1,0x7ef2,0x7f0d,0x7ef6,
+ 0x7efa,0x7efb,0x7efe,0x7f01,0x7f02,0x7f03,0x7f07,0x7f08,0x7f0b,0x7f0c,
+ 0x7f0f,0x7f11,0x7f12,0x7f17,0x7f19,0x7f1c,0x7f1b,0x7f1f,0x7f21,0x7f22,
+ 0x7f23,0x7f24,0x7f25,0x7f26,0x7f27,0x7f2a,0x7f2b,0x7f2c,0x7f2d,0x7f2f,
+ 0x7f30,0x7f31,0x7f32,0x7f33,0x7f35,0x5e7a,0x757f,0x5ddb,0x753e,0x9095,
+ 0x738e,0x7391,0x73ae,0x73a2,0x739f,0x73cf,0x73c2,0x73d1,0x73b7,0x73b3,
+ 0x73c0,0x73c9,0x73c8,0x73e5,0x73d9,0x987c,0x740a,0x73e9,0x73e7,0x73de,
+ 0x73ba,0x73f2,0x740f,0x742a,0x745b,0x7426,0x7425,0x7428,0x7430,0x742e,
+ 0x742c
+ },
+ { /* ku 68 */
+ 0x942f,0x9430,0x9431,0x9432,0x9433,0x9434,0x9435,0x9436,0x9437,0x9438,
+ 0x9439,0x943a,0x943b,0x943c,0x943d,0x943f,0x9440,0x9441,0x9442,0x9443,
+ 0x9444,0x9445,0x9446,0x9447,0x9448,0x9449,0x944a,0x944b,0x944c,0x944d,
+ 0x944e,0x944f,0x9450,0x9451,0x9452,0x9453,0x9454,0x9455,0x9456,0x9457,
+ 0x9458,0x9459,0x945a,0x945b,0x945c,0x945d,0x945e,0x945f,0x9460,0x9461,
+ 0x9462,0x9463,0x9464,0x9465,0x9466,0x9467,0x9468,0x9469,0x946a,0x946c,
+ 0x946d,0x946e,0x946f,UBOGON,0x9470,0x9471,0x9472,0x9473,0x9474,0x9475,
+ 0x9476,0x9477,0x9478,0x9479,0x947a,0x947b,0x947c,0x947d,0x947e,0x947f,
+ 0x9480,0x9481,0x9482,0x9483,0x9484,0x9491,0x9496,0x9498,0x94c7,0x94cf,
+ 0x94d3,0x94d4,0x94da,0x94e6,0x94fb,0x951c,0x9520,0x741b,0x741a,0x7441,
+ 0x745c,0x7457,0x7455,0x7459,0x7477,0x746d,0x747e,0x749c,0x748e,0x7480,
+ 0x7481,0x7487,0x748b,0x749e,0x74a8,0x74a9,0x7490,0x74a7,0x74d2,0x74ba,
+ 0x97ea,0x97eb,0x97ec,0x674c,0x6753,0x675e,0x6748,0x6769,0x67a5,0x6787,
+ 0x676a,0x6773,0x6798,0x67a7,0x6775,0x67a8,0x679e,0x67ad,0x678b,0x6777,
+ 0x677c,0x67f0,0x6809,0x67d8,0x680a,0x67e9,0x67b0,0x680c,0x67d9,0x67b5,
+ 0x67da,0x67b3,0x67dd,0x6800,0x67c3,0x67b8,0x67e2,0x680e,0x67c1,0x67fd,
+ 0x6832,0x6833,0x6860,0x6861,0x684e,0x6862,0x6844,0x6864,0x6883,0x681d,
+ 0x6855,0x6866,0x6841,0x6867,0x6840,0x683e,0x684a,0x6849,0x6829,0x68b5,
+ 0x688f,0x6874,0x6877,0x6893,0x686b,0x68c2,0x696e,0x68fc,0x691f,0x6920,
+ 0x68f9
+ },
+ { /* ku 69 */
+ 0x9527,0x9533,0x953d,0x9543,0x9548,0x954b,0x9555,0x955a,0x9560,0x956e,
+ 0x9574,0x9575,0x9577,0x9578,0x9579,0x957a,0x957b,0x957c,0x957d,0x957e,
+ 0x9580,0x9581,0x9582,0x9583,0x9584,0x9585,0x9586,0x9587,0x9588,0x9589,
+ 0x958a,0x958b,0x958c,0x958d,0x958e,0x958f,0x9590,0x9591,0x9592,0x9593,
+ 0x9594,0x9595,0x9596,0x9597,0x9598,0x9599,0x959a,0x959b,0x959c,0x959d,
+ 0x959e,0x959f,0x95a0,0x95a1,0x95a2,0x95a3,0x95a4,0x95a5,0x95a6,0x95a7,
+ 0x95a8,0x95a9,0x95aa,UBOGON,0x95ab,0x95ac,0x95ad,0x95ae,0x95af,0x95b0,
+ 0x95b1,0x95b2,0x95b3,0x95b4,0x95b5,0x95b6,0x95b7,0x95b8,0x95b9,0x95ba,
+ 0x95bb,0x95bc,0x95bd,0x95be,0x95bf,0x95c0,0x95c1,0x95c2,0x95c3,0x95c4,
+ 0x95c5,0x95c6,0x95c7,0x95c8,0x95c9,0x95ca,0x95cb,0x6924,0x68f0,0x690b,
+ 0x6901,0x6957,0x68e3,0x6910,0x6971,0x6939,0x6960,0x6942,0x695d,0x6984,
+ 0x696b,0x6980,0x6998,0x6978,0x6934,0x69cc,0x6987,0x6988,0x69ce,0x6989,
+ 0x6966,0x6963,0x6979,0x699b,0x69a7,0x69bb,0x69ab,0x69ad,0x69d4,0x69b1,
+ 0x69c1,0x69ca,0x69df,0x6995,0x69e0,0x698d,0x69ff,0x6a2f,0x69ed,0x6a17,
+ 0x6a18,0x6a65,0x69f2,0x6a44,0x6a3e,0x6aa0,0x6a50,0x6a5b,0x6a35,0x6a8e,
+ 0x6a79,0x6a3d,0x6a28,0x6a58,0x6a7c,0x6a91,0x6a90,0x6aa9,0x6a97,0x6aab,
+ 0x7337,0x7352,0x6b81,0x6b82,0x6b87,0x6b84,0x6b92,0x6b93,0x6b8d,0x6b9a,
+ 0x6b9b,0x6ba1,0x6baa,0x8f6b,0x8f6d,0x8f71,0x8f72,0x8f73,0x8f75,0x8f76,
+ 0x8f78,0x8f77,0x8f79,0x8f7a,0x8f7c,0x8f7e,0x8f81,0x8f82,0x8f84,0x8f87,
+ 0x8f8b
+ },
+ { /* ku 6a */
+ 0x95cc,0x95cd,0x95ce,0x95cf,0x95d0,0x95d1,0x95d2,0x95d3,0x95d4,0x95d5,
+ 0x95d6,0x95d7,0x95d8,0x95d9,0x95da,0x95db,0x95dc,0x95dd,0x95de,0x95df,
+ 0x95e0,0x95e1,0x95e2,0x95e3,0x95e4,0x95e5,0x95e6,0x95e7,0x95ec,0x95ff,
+ 0x9607,0x9613,0x9618,0x961b,0x961e,0x9620,0x9623,0x9624,0x9625,0x9626,
+ 0x9627,0x9628,0x9629,0x962b,0x962c,0x962d,0x962f,0x9630,0x9637,0x9638,
+ 0x9639,0x963a,0x963e,0x9641,0x9643,0x964a,0x964e,0x964f,0x9651,0x9652,
+ 0x9653,0x9656,0x9657,UBOGON,0x9658,0x9659,0x965a,0x965c,0x965d,0x965e,
+ 0x9660,0x9663,0x9665,0x9666,0x966b,0x966d,0x966e,0x966f,0x9670,0x9671,
+ 0x9673,0x9678,0x9679,0x967a,0x967b,0x967c,0x967d,0x967e,0x967f,0x9680,
+ 0x9681,0x9682,0x9683,0x9684,0x9687,0x9689,0x968a,0x8f8d,0x8f8e,0x8f8f,
+ 0x8f98,0x8f9a,0x8ece,0x620b,0x6217,0x621b,0x621f,0x6222,0x6221,0x6225,
+ 0x6224,0x622c,0x81e7,0x74ef,0x74f4,0x74ff,0x750f,0x7511,0x7513,0x6534,
+ 0x65ee,0x65ef,0x65f0,0x660a,0x6619,0x6772,0x6603,0x6615,0x6600,0x7085,
+ 0x66f7,0x661d,0x6634,0x6631,0x6636,0x6635,0x8006,0x665f,0x6654,0x6641,
+ 0x664f,0x6656,0x6661,0x6657,0x6677,0x6684,0x668c,0x66a7,0x669d,0x66be,
+ 0x66db,0x66dc,0x66e6,0x66e9,0x8d32,0x8d33,0x8d36,0x8d3b,0x8d3d,0x8d40,
+ 0x8d45,0x8d46,0x8d48,0x8d49,0x8d47,0x8d4d,0x8d55,0x8d59,0x89c7,0x89ca,
+ 0x89cb,0x89cc,0x89ce,0x89cf,0x89d0,0x89d1,0x726e,0x729f,0x725d,0x7266,
+ 0x726f,0x727e,0x727f,0x7284,0x728b,0x728d,0x728f,0x7292,0x6308,0x6332,
+ 0x63b0
+ },
+ { /* ku 6b */
+ 0x968c,0x968e,0x9691,0x9692,0x9693,0x9695,0x9696,0x969a,0x969b,0x969d,
+ 0x969e,0x969f,0x96a0,0x96a1,0x96a2,0x96a3,0x96a4,0x96a5,0x96a6,0x96a8,
+ 0x96a9,0x96aa,0x96ab,0x96ac,0x96ad,0x96ae,0x96af,0x96b1,0x96b2,0x96b4,
+ 0x96b5,0x96b7,0x96b8,0x96ba,0x96bb,0x96bf,0x96c2,0x96c3,0x96c8,0x96ca,
+ 0x96cb,0x96d0,0x96d1,0x96d3,0x96d4,0x96d6,0x96d7,0x96d8,0x96d9,0x96da,
+ 0x96db,0x96dc,0x96dd,0x96de,0x96df,0x96e1,0x96e2,0x96e3,0x96e4,0x96e5,
+ 0x96e6,0x96e7,0x96eb,UBOGON,0x96ec,0x96ed,0x96ee,0x96f0,0x96f1,0x96f2,
+ 0x96f4,0x96f5,0x96f8,0x96fa,0x96fb,0x96fc,0x96fd,0x96ff,0x9702,0x9703,
+ 0x9705,0x970a,0x970b,0x970c,0x9710,0x9711,0x9712,0x9714,0x9715,0x9717,
+ 0x9718,0x9719,0x971a,0x971b,0x971d,0x971f,0x9720,0x643f,0x64d8,0x8004,
+ 0x6bea,0x6bf3,0x6bfd,0x6bf5,0x6bf9,0x6c05,0x6c07,0x6c06,0x6c0d,0x6c15,
+ 0x6c18,0x6c19,0x6c1a,0x6c21,0x6c29,0x6c24,0x6c2a,0x6c32,0x6535,0x6555,
+ 0x656b,0x724d,0x7252,0x7256,0x7230,0x8662,0x5216,0x809f,0x809c,0x8093,
+ 0x80bc,0x670a,0x80bd,0x80b1,0x80ab,0x80ad,0x80b4,0x80b7,0x80e7,0x80e8,
+ 0x80e9,0x80ea,0x80db,0x80c2,0x80c4,0x80d9,0x80cd,0x80d7,0x6710,0x80dd,
+ 0x80eb,0x80f1,0x80f4,0x80ed,0x810d,0x810e,0x80f2,0x80fc,0x6715,0x8112,
+ 0x8c5a,0x8136,0x811e,0x812c,0x8118,0x8132,0x8148,0x814c,0x8153,0x8174,
+ 0x8159,0x815a,0x8171,0x8160,0x8169,0x817c,0x817d,0x816d,0x8167,0x584d,
+ 0x5ab5,0x8188,0x8182,0x8191,0x6ed5,0x81a3,0x81aa,0x81cc,0x6726,0x81ca,
+ 0x81bb
+ },
+ { /* ku 6c */
+ 0x9721,0x9722,0x9723,0x9724,0x9725,0x9726,0x9727,0x9728,0x9729,0x972b,
+ 0x972c,0x972e,0x972f,0x9731,0x9733,0x9734,0x9735,0x9736,0x9737,0x973a,
+ 0x973b,0x973c,0x973d,0x973f,0x9740,0x9741,0x9742,0x9743,0x9744,0x9745,
+ 0x9746,0x9747,0x9748,0x9749,0x974a,0x974b,0x974c,0x974d,0x974e,0x974f,
+ 0x9750,0x9751,0x9754,0x9755,0x9757,0x9758,0x975a,0x975c,0x975d,0x975f,
+ 0x9763,0x9764,0x9766,0x9767,0x9768,0x976a,0x976b,0x976c,0x976d,0x976e,
+ 0x976f,0x9770,0x9771,UBOGON,0x9772,0x9775,0x9777,0x9778,0x9779,0x977a,
+ 0x977b,0x977d,0x977e,0x977f,0x9780,0x9781,0x9782,0x9783,0x9784,0x9786,
+ 0x9787,0x9788,0x9789,0x978a,0x978c,0x978e,0x978f,0x9790,0x9793,0x9795,
+ 0x9796,0x9797,0x9799,0x979a,0x979b,0x979c,0x979d,0x81c1,0x81a6,0x6b24,
+ 0x6b37,0x6b39,0x6b43,0x6b46,0x6b59,0x98d1,0x98d2,0x98d3,0x98d5,0x98d9,
+ 0x98da,0x6bb3,0x5f40,0x6bc2,0x89f3,0x6590,0x9f51,0x6593,0x65bc,0x65c6,
+ 0x65c4,0x65c3,0x65cc,0x65ce,0x65d2,0x65d6,0x7080,0x709c,0x7096,0x709d,
+ 0x70bb,0x70c0,0x70b7,0x70ab,0x70b1,0x70e8,0x70ca,0x7110,0x7113,0x7116,
+ 0x712f,0x7131,0x7173,0x715c,0x7168,0x7145,0x7172,0x714a,0x7178,0x717a,
+ 0x7198,0x71b3,0x71b5,0x71a8,0x71a0,0x71e0,0x71d4,0x71e7,0x71f9,0x721d,
+ 0x7228,0x706c,0x7118,0x7166,0x71b9,0x623e,0x623d,0x6243,0x6248,0x6249,
+ 0x793b,0x7940,0x7946,0x7949,0x795b,0x795c,0x7953,0x795a,0x7962,0x7957,
+ 0x7960,0x796f,0x7967,0x797a,0x7985,0x798a,0x799a,0x79a7,0x79b3,0x5fd1,
+ 0x5fd0
+ },
+ { /* ku 6d */
+ 0x979e,0x979f,0x97a1,0x97a2,0x97a4,0x97a5,0x97a6,0x97a7,0x97a8,0x97a9,
+ 0x97aa,0x97ac,0x97ae,0x97b0,0x97b1,0x97b3,0x97b5,0x97b6,0x97b7,0x97b8,
+ 0x97b9,0x97ba,0x97bb,0x97bc,0x97bd,0x97be,0x97bf,0x97c0,0x97c1,0x97c2,
+ 0x97c3,0x97c4,0x97c5,0x97c6,0x97c7,0x97c8,0x97c9,0x97ca,0x97cb,0x97cc,
+ 0x97cd,0x97ce,0x97cf,0x97d0,0x97d1,0x97d2,0x97d3,0x97d4,0x97d5,0x97d6,
+ 0x97d7,0x97d8,0x97d9,0x97da,0x97db,0x97dc,0x97dd,0x97de,0x97df,0x97e0,
+ 0x97e1,0x97e2,0x97e3,UBOGON,0x97e4,0x97e5,0x97e8,0x97ee,0x97ef,0x97f0,
+ 0x97f1,0x97f2,0x97f4,0x97f7,0x97f8,0x97f9,0x97fa,0x97fb,0x97fc,0x97fd,
+ 0x97fe,0x97ff,0x9800,0x9801,0x9802,0x9803,0x9804,0x9805,0x9806,0x9807,
+ 0x9808,0x9809,0x980a,0x980b,0x980c,0x980d,0x980e,0x603c,0x605d,0x605a,
+ 0x6067,0x6041,0x6059,0x6063,0x60ab,0x6106,0x610d,0x615d,0x61a9,0x619d,
+ 0x61cb,0x61d1,0x6206,0x8080,0x807f,0x6c93,0x6cf6,0x6dfc,0x77f6,0x77f8,
+ 0x7800,0x7809,0x7817,0x7818,0x7811,0x65ab,0x782d,0x781c,0x781d,0x7839,
+ 0x783a,0x783b,0x781f,0x783c,0x7825,0x782c,0x7823,0x7829,0x784e,0x786d,
+ 0x7856,0x7857,0x7826,0x7850,0x7847,0x784c,0x786a,0x789b,0x7893,0x789a,
+ 0x7887,0x789c,0x78a1,0x78a3,0x78b2,0x78b9,0x78a5,0x78d4,0x78d9,0x78c9,
+ 0x78ec,0x78f2,0x7905,0x78f4,0x7913,0x7924,0x791e,0x7934,0x9f9b,0x9ef9,
+ 0x9efb,0x9efc,0x76f1,0x7704,0x770d,0x76f9,0x7707,0x7708,0x771a,0x7722,
+ 0x7719,0x772d,0x7726,0x7735,0x7738,0x7750,0x7751,0x7747,0x7743,0x775a,
+ 0x7768
+ },
+ { /* ku 6e */
+ 0x980f,0x9810,0x9811,0x9812,0x9813,0x9814,0x9815,0x9816,0x9817,0x9818,
+ 0x9819,0x981a,0x981b,0x981c,0x981d,0x981e,0x981f,0x9820,0x9821,0x9822,
+ 0x9823,0x9824,0x9825,0x9826,0x9827,0x9828,0x9829,0x982a,0x982b,0x982c,
+ 0x982d,0x982e,0x982f,0x9830,0x9831,0x9832,0x9833,0x9834,0x9835,0x9836,
+ 0x9837,0x9838,0x9839,0x983a,0x983b,0x983c,0x983d,0x983e,0x983f,0x9840,
+ 0x9841,0x9842,0x9843,0x9844,0x9845,0x9846,0x9847,0x9848,0x9849,0x984a,
+ 0x984b,0x984c,0x984d,UBOGON,0x984e,0x984f,0x9850,0x9851,0x9852,0x9853,
+ 0x9854,0x9855,0x9856,0x9857,0x9858,0x9859,0x985a,0x985b,0x985c,0x985d,
+ 0x985e,0x985f,0x9860,0x9861,0x9862,0x9863,0x9864,0x9865,0x9866,0x9867,
+ 0x9868,0x9869,0x986a,0x986b,0x986c,0x986d,0x986e,0x7762,0x7765,0x777f,
+ 0x778d,0x777d,0x7780,0x778c,0x7791,0x779f,0x77a0,0x77b0,0x77b5,0x77bd,
+ 0x753a,0x7540,0x754e,0x754b,0x7548,0x755b,0x7572,0x7579,0x7583,0x7f58,
+ 0x7f61,0x7f5f,0x8a48,0x7f68,0x7f74,0x7f71,0x7f79,0x7f81,0x7f7e,0x76cd,
+ 0x76e5,0x8832,0x9485,0x9486,0x9487,0x948b,0x948a,0x948c,0x948d,0x948f,
+ 0x9490,0x9494,0x9497,0x9495,0x949a,0x949b,0x949c,0x94a3,0x94a4,0x94ab,
+ 0x94aa,0x94ad,0x94ac,0x94af,0x94b0,0x94b2,0x94b4,0x94b6,0x94b7,0x94b8,
+ 0x94b9,0x94ba,0x94bc,0x94bd,0x94bf,0x94c4,0x94c8,0x94c9,0x94ca,0x94cb,
+ 0x94cc,0x94cd,0x94ce,0x94d0,0x94d1,0x94d2,0x94d5,0x94d6,0x94d7,0x94d9,
+ 0x94d8,0x94db,0x94de,0x94df,0x94e0,0x94e2,0x94e4,0x94e5,0x94e7,0x94e8,
+ 0x94ea
+ },
+ { /* ku 6f */
+ 0x986f,0x9870,0x9871,0x9872,0x9873,0x9874,0x988b,0x988e,0x9892,0x9895,
+ 0x9899,0x98a3,0x98a8,0x98a9,0x98aa,0x98ab,0x98ac,0x98ad,0x98ae,0x98af,
+ 0x98b0,0x98b1,0x98b2,0x98b3,0x98b4,0x98b5,0x98b6,0x98b7,0x98b8,0x98b9,
+ 0x98ba,0x98bb,0x98bc,0x98bd,0x98be,0x98bf,0x98c0,0x98c1,0x98c2,0x98c3,
+ 0x98c4,0x98c5,0x98c6,0x98c7,0x98c8,0x98c9,0x98ca,0x98cb,0x98cc,0x98cd,
+ 0x98cf,0x98d0,0x98d4,0x98d6,0x98d7,0x98db,0x98dc,0x98dd,0x98e0,0x98e1,
+ 0x98e2,0x98e3,0x98e4,UBOGON,0x98e5,0x98e6,0x98e9,0x98ea,0x98eb,0x98ec,
+ 0x98ed,0x98ee,0x98ef,0x98f0,0x98f1,0x98f2,0x98f3,0x98f4,0x98f5,0x98f6,
+ 0x98f7,0x98f8,0x98f9,0x98fa,0x98fb,0x98fc,0x98fd,0x98fe,0x98ff,0x9900,
+ 0x9901,0x9902,0x9903,0x9904,0x9905,0x9906,0x9907,0x94e9,0x94eb,0x94ee,
+ 0x94ef,0x94f3,0x94f4,0x94f5,0x94f7,0x94f9,0x94fc,0x94fd,0x94ff,0x9503,
+ 0x9502,0x9506,0x9507,0x9509,0x950a,0x950d,0x950e,0x950f,0x9512,0x9513,
+ 0x9514,0x9515,0x9516,0x9518,0x951b,0x951d,0x951e,0x951f,0x9522,0x952a,
+ 0x952b,0x9529,0x952c,0x9531,0x9532,0x9534,0x9536,0x9537,0x9538,0x953c,
+ 0x953e,0x953f,0x9542,0x9535,0x9544,0x9545,0x9546,0x9549,0x954c,0x954e,
+ 0x954f,0x9552,0x9553,0x9554,0x9556,0x9557,0x9558,0x9559,0x955b,0x955e,
+ 0x955f,0x955d,0x9561,0x9562,0x9564,0x9565,0x9566,0x9567,0x9568,0x9569,
+ 0x956a,0x956b,0x956c,0x956f,0x9571,0x9572,0x9573,0x953a,0x77e7,0x77ec,
+ 0x96c9,0x79d5,0x79ed,0x79e3,0x79eb,0x7a06,0x5d47,0x7a03,0x7a02,0x7a1e,
+ 0x7a14
+ },
+ { /* ku 70 */
+ 0x9908,0x9909,0x990a,0x990b,0x990c,0x990e,0x990f,0x9911,0x9912,0x9913,
+ 0x9914,0x9915,0x9916,0x9917,0x9918,0x9919,0x991a,0x991b,0x991c,0x991d,
+ 0x991e,0x991f,0x9920,0x9921,0x9922,0x9923,0x9924,0x9925,0x9926,0x9927,
+ 0x9928,0x9929,0x992a,0x992b,0x992c,0x992d,0x992f,0x9930,0x9931,0x9932,
+ 0x9933,0x9934,0x9935,0x9936,0x9937,0x9938,0x9939,0x993a,0x993b,0x993c,
+ 0x993d,0x993e,0x993f,0x9940,0x9941,0x9942,0x9943,0x9944,0x9945,0x9946,
+ 0x9947,0x9948,0x9949,UBOGON,0x994a,0x994b,0x994c,0x994d,0x994e,0x994f,
+ 0x9950,0x9951,0x9952,0x9953,0x9956,0x9957,0x9958,0x9959,0x995a,0x995b,
+ 0x995c,0x995d,0x995e,0x995f,0x9960,0x9961,0x9962,0x9964,0x9966,0x9973,
+ 0x9978,0x9979,0x997b,0x997e,0x9982,0x9983,0x9989,0x7a39,0x7a37,0x7a51,
+ 0x9ecf,0x99a5,0x7a70,0x7688,0x768e,0x7693,0x7699,0x76a4,0x74de,0x74e0,
+ 0x752c,0x9e20,0x9e22,0x9e28,0x9e29,0x9e2a,0x9e2b,0x9e2c,0x9e32,0x9e31,
+ 0x9e36,0x9e38,0x9e37,0x9e39,0x9e3a,0x9e3e,0x9e41,0x9e42,0x9e44,0x9e46,
+ 0x9e47,0x9e48,0x9e49,0x9e4b,0x9e4c,0x9e4e,0x9e51,0x9e55,0x9e57,0x9e5a,
+ 0x9e5b,0x9e5c,0x9e5e,0x9e63,0x9e66,0x9e67,0x9e68,0x9e69,0x9e6a,0x9e6b,
+ 0x9e6c,0x9e71,0x9e6d,0x9e73,0x7592,0x7594,0x7596,0x75a0,0x759d,0x75ac,
+ 0x75a3,0x75b3,0x75b4,0x75b8,0x75c4,0x75b1,0x75b0,0x75c3,0x75c2,0x75d6,
+ 0x75cd,0x75e3,0x75e8,0x75e6,0x75e4,0x75eb,0x75e7,0x7603,0x75f1,0x75fc,
+ 0x75ff,0x7610,0x7600,0x7605,0x760c,0x7617,0x760a,0x7625,0x7618,0x7615,
+ 0x7619
+ },
+ { /* ku 71 */
+ 0x998c,0x998e,0x999a,0x999b,0x999c,0x999d,0x999e,0x999f,0x99a0,0x99a1,
+ 0x99a2,0x99a3,0x99a4,0x99a6,0x99a7,0x99a9,0x99aa,0x99ab,0x99ac,0x99ad,
+ 0x99ae,0x99af,0x99b0,0x99b1,0x99b2,0x99b3,0x99b4,0x99b5,0x99b6,0x99b7,
+ 0x99b8,0x99b9,0x99ba,0x99bb,0x99bc,0x99bd,0x99be,0x99bf,0x99c0,0x99c1,
+ 0x99c2,0x99c3,0x99c4,0x99c5,0x99c6,0x99c7,0x99c8,0x99c9,0x99ca,0x99cb,
+ 0x99cc,0x99cd,0x99ce,0x99cf,0x99d0,0x99d1,0x99d2,0x99d3,0x99d4,0x99d5,
+ 0x99d6,0x99d7,0x99d8,UBOGON,0x99d9,0x99da,0x99db,0x99dc,0x99dd,0x99de,
+ 0x99df,0x99e0,0x99e1,0x99e2,0x99e3,0x99e4,0x99e5,0x99e6,0x99e7,0x99e8,
+ 0x99e9,0x99ea,0x99eb,0x99ec,0x99ed,0x99ee,0x99ef,0x99f0,0x99f1,0x99f2,
+ 0x99f3,0x99f4,0x99f5,0x99f6,0x99f7,0x99f8,0x99f9,0x761b,0x763c,0x7622,
+ 0x7620,0x7640,0x762d,0x7630,0x763f,0x7635,0x7643,0x763e,0x7633,0x764d,
+ 0x765e,0x7654,0x765c,0x7656,0x766b,0x766f,0x7fca,0x7ae6,0x7a78,0x7a79,
+ 0x7a80,0x7a86,0x7a88,0x7a95,0x7aa6,0x7aa0,0x7aac,0x7aa8,0x7aad,0x7ab3,
+ 0x8864,0x8869,0x8872,0x887d,0x887f,0x8882,0x88a2,0x88c6,0x88b7,0x88bc,
+ 0x88c9,0x88e2,0x88ce,0x88e3,0x88e5,0x88f1,0x891a,0x88fc,0x88e8,0x88fe,
+ 0x88f0,0x8921,0x8919,0x8913,0x891b,0x890a,0x8934,0x892b,0x8936,0x8941,
+ 0x8966,0x897b,0x758b,0x80e5,0x76b2,0x76b4,0x77dc,0x8012,0x8014,0x8016,
+ 0x801c,0x8020,0x8022,0x8025,0x8026,0x8027,0x8029,0x8028,0x8031,0x800b,
+ 0x8035,0x8043,0x8046,0x804d,0x8052,0x8069,0x8071,0x8983,0x9878,0x9880,
+ 0x9883
+ },
+ { /* ku 72 */
+ 0x99fa,0x99fb,0x99fc,0x99fd,0x99fe,0x99ff,0x9a00,0x9a01,0x9a02,0x9a03,
+ 0x9a04,0x9a05,0x9a06,0x9a07,0x9a08,0x9a09,0x9a0a,0x9a0b,0x9a0c,0x9a0d,
+ 0x9a0e,0x9a0f,0x9a10,0x9a11,0x9a12,0x9a13,0x9a14,0x9a15,0x9a16,0x9a17,
+ 0x9a18,0x9a19,0x9a1a,0x9a1b,0x9a1c,0x9a1d,0x9a1e,0x9a1f,0x9a20,0x9a21,
+ 0x9a22,0x9a23,0x9a24,0x9a25,0x9a26,0x9a27,0x9a28,0x9a29,0x9a2a,0x9a2b,
+ 0x9a2c,0x9a2d,0x9a2e,0x9a2f,0x9a30,0x9a31,0x9a32,0x9a33,0x9a34,0x9a35,
+ 0x9a36,0x9a37,0x9a38,UBOGON,0x9a39,0x9a3a,0x9a3b,0x9a3c,0x9a3d,0x9a3e,
+ 0x9a3f,0x9a40,0x9a41,0x9a42,0x9a43,0x9a44,0x9a45,0x9a46,0x9a47,0x9a48,
+ 0x9a49,0x9a4a,0x9a4b,0x9a4c,0x9a4d,0x9a4e,0x9a4f,0x9a50,0x9a51,0x9a52,
+ 0x9a53,0x9a54,0x9a55,0x9a56,0x9a57,0x9a58,0x9a59,0x9889,0x988c,0x988d,
+ 0x988f,0x9894,0x989a,0x989b,0x989e,0x989f,0x98a1,0x98a2,0x98a5,0x98a6,
+ 0x864d,0x8654,0x866c,0x866e,0x867f,0x867a,0x867c,0x867b,0x86a8,0x868d,
+ 0x868b,0x86ac,0x869d,0x86a7,0x86a3,0x86aa,0x8693,0x86a9,0x86b6,0x86c4,
+ 0x86b5,0x86ce,0x86b0,0x86ba,0x86b1,0x86af,0x86c9,0x86cf,0x86b4,0x86e9,
+ 0x86f1,0x86f2,0x86ed,0x86f3,0x86d0,0x8713,0x86de,0x86f4,0x86df,0x86d8,
+ 0x86d1,0x8703,0x8707,0x86f8,0x8708,0x870a,0x870d,0x8709,0x8723,0x873b,
+ 0x871e,0x8725,0x872e,0x871a,0x873e,0x8748,0x8734,0x8731,0x8729,0x8737,
+ 0x873f,0x8782,0x8722,0x877d,0x877e,0x877b,0x8760,0x8770,0x874c,0x876e,
+ 0x878b,0x8753,0x8763,0x877c,0x8764,0x8759,0x8765,0x8793,0x87af,0x87a8,
+ 0x87d2
+ },
+ { /* ku 73 */
+ 0x9a5a,0x9a5b,0x9a5c,0x9a5d,0x9a5e,0x9a5f,0x9a60,0x9a61,0x9a62,0x9a63,
+ 0x9a64,0x9a65,0x9a66,0x9a67,0x9a68,0x9a69,0x9a6a,0x9a6b,0x9a72,0x9a83,
+ 0x9a89,0x9a8d,0x9a8e,0x9a94,0x9a95,0x9a99,0x9aa6,0x9aa9,0x9aaa,0x9aab,
+ 0x9aac,0x9aad,0x9aae,0x9aaf,0x9ab2,0x9ab3,0x9ab4,0x9ab5,0x9ab9,0x9abb,
+ 0x9abd,0x9abe,0x9abf,0x9ac3,0x9ac4,0x9ac6,0x9ac7,0x9ac8,0x9ac9,0x9aca,
+ 0x9acd,0x9ace,0x9acf,0x9ad0,0x9ad2,0x9ad4,0x9ad5,0x9ad6,0x9ad7,0x9ad9,
+ 0x9ada,0x9adb,0x9adc,UBOGON,0x9add,0x9ade,0x9ae0,0x9ae2,0x9ae3,0x9ae4,
+ 0x9ae5,0x9ae7,0x9ae8,0x9ae9,0x9aea,0x9aec,0x9aee,0x9af0,0x9af1,0x9af2,
+ 0x9af3,0x9af4,0x9af5,0x9af6,0x9af7,0x9af8,0x9afa,0x9afc,0x9afd,0x9afe,
+ 0x9aff,0x9b00,0x9b01,0x9b02,0x9b04,0x9b05,0x9b06,0x87c6,0x8788,0x8785,
+ 0x87ad,0x8797,0x8783,0x87ab,0x87e5,0x87ac,0x87b5,0x87b3,0x87cb,0x87d3,
+ 0x87bd,0x87d1,0x87c0,0x87ca,0x87db,0x87ea,0x87e0,0x87ee,0x8816,0x8813,
+ 0x87fe,0x880a,0x881b,0x8821,0x8839,0x883c,0x7f36,0x7f42,0x7f44,0x7f45,
+ 0x8210,0x7afa,0x7afd,0x7b08,0x7b03,0x7b04,0x7b15,0x7b0a,0x7b2b,0x7b0f,
+ 0x7b47,0x7b38,0x7b2a,0x7b19,0x7b2e,0x7b31,0x7b20,0x7b25,0x7b24,0x7b33,
+ 0x7b3e,0x7b1e,0x7b58,0x7b5a,0x7b45,0x7b75,0x7b4c,0x7b5d,0x7b60,0x7b6e,
+ 0x7b7b,0x7b62,0x7b72,0x7b71,0x7b90,0x7ba6,0x7ba7,0x7bb8,0x7bac,0x7b9d,
+ 0x7ba8,0x7b85,0x7baa,0x7b9c,0x7ba2,0x7bab,0x7bb4,0x7bd1,0x7bc1,0x7bcc,
+ 0x7bdd,0x7bda,0x7be5,0x7be6,0x7bea,0x7c0c,0x7bfe,0x7bfc,0x7c0f,0x7c16,
+ 0x7c0b
+ },
+ { /* ku 74 */
+ 0x9b07,0x9b09,0x9b0a,0x9b0b,0x9b0c,0x9b0d,0x9b0e,0x9b10,0x9b11,0x9b12,
+ 0x9b14,0x9b15,0x9b16,0x9b17,0x9b18,0x9b19,0x9b1a,0x9b1b,0x9b1c,0x9b1d,
+ 0x9b1e,0x9b20,0x9b21,0x9b22,0x9b24,0x9b25,0x9b26,0x9b27,0x9b28,0x9b29,
+ 0x9b2a,0x9b2b,0x9b2c,0x9b2d,0x9b2e,0x9b30,0x9b31,0x9b33,0x9b34,0x9b35,
+ 0x9b36,0x9b37,0x9b38,0x9b39,0x9b3a,0x9b3d,0x9b3e,0x9b3f,0x9b40,0x9b46,
+ 0x9b4a,0x9b4b,0x9b4c,0x9b4e,0x9b50,0x9b52,0x9b53,0x9b55,0x9b56,0x9b57,
+ 0x9b58,0x9b59,0x9b5a,UBOGON,0x9b5b,0x9b5c,0x9b5d,0x9b5e,0x9b5f,0x9b60,
+ 0x9b61,0x9b62,0x9b63,0x9b64,0x9b65,0x9b66,0x9b67,0x9b68,0x9b69,0x9b6a,
+ 0x9b6b,0x9b6c,0x9b6d,0x9b6e,0x9b6f,0x9b70,0x9b71,0x9b72,0x9b73,0x9b74,
+ 0x9b75,0x9b76,0x9b77,0x9b78,0x9b79,0x9b7a,0x9b7b,0x7c1f,0x7c2a,0x7c26,
+ 0x7c38,0x7c41,0x7c40,0x81fe,0x8201,0x8202,0x8204,0x81ec,0x8844,0x8221,
+ 0x8222,0x8223,0x822d,0x822f,0x8228,0x822b,0x8238,0x823b,0x8233,0x8234,
+ 0x823e,0x8244,0x8249,0x824b,0x824f,0x825a,0x825f,0x8268,0x887e,0x8885,
+ 0x8888,0x88d8,0x88df,0x895e,0x7f9d,0x7f9f,0x7fa7,0x7faf,0x7fb0,0x7fb2,
+ 0x7c7c,0x6549,0x7c91,0x7c9d,0x7c9c,0x7c9e,0x7ca2,0x7cb2,0x7cbc,0x7cbd,
+ 0x7cc1,0x7cc7,0x7ccc,0x7ccd,0x7cc8,0x7cc5,0x7cd7,0x7ce8,0x826e,0x66a8,
+ 0x7fbf,0x7fce,0x7fd5,0x7fe5,0x7fe1,0x7fe6,0x7fe9,0x7fee,0x7ff3,0x7cf8,
+ 0x7d77,0x7da6,0x7dae,0x7e47,0x7e9b,0x9eb8,0x9eb4,0x8d73,0x8d84,0x8d94,
+ 0x8d91,0x8db1,0x8d67,0x8d6d,0x8c47,0x8c49,0x914a,0x9150,0x914e,0x914f,
+ 0x9164
+ },
+ { /* ku 75 */
+ 0x9b7c,0x9b7d,0x9b7e,0x9b7f,0x9b80,0x9b81,0x9b82,0x9b83,0x9b84,0x9b85,
+ 0x9b86,0x9b87,0x9b88,0x9b89,0x9b8a,0x9b8b,0x9b8c,0x9b8d,0x9b8e,0x9b8f,
+ 0x9b90,0x9b91,0x9b92,0x9b93,0x9b94,0x9b95,0x9b96,0x9b97,0x9b98,0x9b99,
+ 0x9b9a,0x9b9b,0x9b9c,0x9b9d,0x9b9e,0x9b9f,0x9ba0,0x9ba1,0x9ba2,0x9ba3,
+ 0x9ba4,0x9ba5,0x9ba6,0x9ba7,0x9ba8,0x9ba9,0x9baa,0x9bab,0x9bac,0x9bad,
+ 0x9bae,0x9baf,0x9bb0,0x9bb1,0x9bb2,0x9bb3,0x9bb4,0x9bb5,0x9bb6,0x9bb7,
+ 0x9bb8,0x9bb9,0x9bba,UBOGON,0x9bbb,0x9bbc,0x9bbd,0x9bbe,0x9bbf,0x9bc0,
+ 0x9bc1,0x9bc2,0x9bc3,0x9bc4,0x9bc5,0x9bc6,0x9bc7,0x9bc8,0x9bc9,0x9bca,
+ 0x9bcb,0x9bcc,0x9bcd,0x9bce,0x9bcf,0x9bd0,0x9bd1,0x9bd2,0x9bd3,0x9bd4,
+ 0x9bd5,0x9bd6,0x9bd7,0x9bd8,0x9bd9,0x9bda,0x9bdb,0x9162,0x9161,0x9170,
+ 0x9169,0x916f,0x917d,0x917e,0x9172,0x9174,0x9179,0x918c,0x9185,0x9190,
+ 0x918d,0x9191,0x91a2,0x91a3,0x91aa,0x91ad,0x91ae,0x91af,0x91b5,0x91b4,
+ 0x91ba,0x8c55,0x9e7e,0x8db8,0x8deb,0x8e05,0x8e59,0x8e69,0x8db5,0x8dbf,
+ 0x8dbc,0x8dba,0x8dc4,0x8dd6,0x8dd7,0x8dda,0x8dde,0x8dce,0x8dcf,0x8ddb,
+ 0x8dc6,0x8dec,0x8df7,0x8df8,0x8de3,0x8df9,0x8dfb,0x8de4,0x8e09,0x8dfd,
+ 0x8e14,0x8e1d,0x8e1f,0x8e2c,0x8e2e,0x8e23,0x8e2f,0x8e3a,0x8e40,0x8e39,
+ 0x8e35,0x8e3d,0x8e31,0x8e49,0x8e41,0x8e42,0x8e51,0x8e52,0x8e4a,0x8e70,
+ 0x8e76,0x8e7c,0x8e6f,0x8e74,0x8e85,0x8e8f,0x8e94,0x8e90,0x8e9c,0x8e9e,
+ 0x8c78,0x8c82,0x8c8a,0x8c85,0x8c98,0x8c94,0x659b,0x89d6,0x89de,0x89da,
+ 0x89dc
+ },
+ { /* ku 76 */
+ 0x9bdc,0x9bdd,0x9bde,0x9bdf,0x9be0,0x9be1,0x9be2,0x9be3,0x9be4,0x9be5,
+ 0x9be6,0x9be7,0x9be8,0x9be9,0x9bea,0x9beb,0x9bec,0x9bed,0x9bee,0x9bef,
+ 0x9bf0,0x9bf1,0x9bf2,0x9bf3,0x9bf4,0x9bf5,0x9bf6,0x9bf7,0x9bf8,0x9bf9,
+ 0x9bfa,0x9bfb,0x9bfc,0x9bfd,0x9bfe,0x9bff,0x9c00,0x9c01,0x9c02,0x9c03,
+ 0x9c04,0x9c05,0x9c06,0x9c07,0x9c08,0x9c09,0x9c0a,0x9c0b,0x9c0c,0x9c0d,
+ 0x9c0e,0x9c0f,0x9c10,0x9c11,0x9c12,0x9c13,0x9c14,0x9c15,0x9c16,0x9c17,
+ 0x9c18,0x9c19,0x9c1a,UBOGON,0x9c1b,0x9c1c,0x9c1d,0x9c1e,0x9c1f,0x9c20,
+ 0x9c21,0x9c22,0x9c23,0x9c24,0x9c25,0x9c26,0x9c27,0x9c28,0x9c29,0x9c2a,
+ 0x9c2b,0x9c2c,0x9c2d,0x9c2e,0x9c2f,0x9c30,0x9c31,0x9c32,0x9c33,0x9c34,
+ 0x9c35,0x9c36,0x9c37,0x9c38,0x9c39,0x9c3a,0x9c3b,0x89e5,0x89eb,0x89ef,
+ 0x8a3e,0x8b26,0x9753,0x96e9,0x96f3,0x96ef,0x9706,0x9701,0x9708,0x970f,
+ 0x970e,0x972a,0x972d,0x9730,0x973e,0x9f80,0x9f83,0x9f85,0x9f86,0x9f87,
+ 0x9f88,0x9f89,0x9f8a,0x9f8c,0x9efe,0x9f0b,0x9f0d,0x96b9,0x96bc,0x96bd,
+ 0x96ce,0x96d2,0x77bf,0x96e0,0x928e,0x92ae,0x92c8,0x933e,0x936a,0x93ca,
+ 0x938f,0x943e,0x946b,0x9c7f,0x9c82,0x9c85,0x9c86,0x9c87,0x9c88,0x7a23,
+ 0x9c8b,0x9c8e,0x9c90,0x9c91,0x9c92,0x9c94,0x9c95,0x9c9a,0x9c9b,0x9c9e,
+ 0x9c9f,0x9ca0,0x9ca1,0x9ca2,0x9ca3,0x9ca5,0x9ca6,0x9ca7,0x9ca8,0x9ca9,
+ 0x9cab,0x9cad,0x9cae,0x9cb0,0x9cb1,0x9cb2,0x9cb3,0x9cb4,0x9cb5,0x9cb6,
+ 0x9cb7,0x9cba,0x9cbb,0x9cbc,0x9cbd,0x9cc4,0x9cc5,0x9cc6,0x9cc7,0x9cca,
+ 0x9ccb
+ },
+ { /* ku 77 */
+ 0x9c3c,0x9c3d,0x9c3e,0x9c3f,0x9c40,0x9c41,0x9c42,0x9c43,0x9c44,0x9c45,
+ 0x9c46,0x9c47,0x9c48,0x9c49,0x9c4a,0x9c4b,0x9c4c,0x9c4d,0x9c4e,0x9c4f,
+ 0x9c50,0x9c51,0x9c52,0x9c53,0x9c54,0x9c55,0x9c56,0x9c57,0x9c58,0x9c59,
+ 0x9c5a,0x9c5b,0x9c5c,0x9c5d,0x9c5e,0x9c5f,0x9c60,0x9c61,0x9c62,0x9c63,
+ 0x9c64,0x9c65,0x9c66,0x9c67,0x9c68,0x9c69,0x9c6a,0x9c6b,0x9c6c,0x9c6d,
+ 0x9c6e,0x9c6f,0x9c70,0x9c71,0x9c72,0x9c73,0x9c74,0x9c75,0x9c76,0x9c77,
+ 0x9c78,0x9c79,0x9c7a,UBOGON,0x9c7b,0x9c7d,0x9c7e,0x9c80,0x9c83,0x9c84,
+ 0x9c89,0x9c8a,0x9c8c,0x9c8f,0x9c93,0x9c96,0x9c97,0x9c98,0x9c99,0x9c9d,
+ 0x9caa,0x9cac,0x9caf,0x9cb9,0x9cbe,0x9cbf,0x9cc0,0x9cc1,0x9cc2,0x9cc8,
+ 0x9cc9,0x9cd1,0x9cd2,0x9cda,0x9cdb,0x9ce0,0x9ce1,0x9ccc,0x9ccd,0x9cce,
+ 0x9ccf,0x9cd0,0x9cd3,0x9cd4,0x9cd5,0x9cd7,0x9cd8,0x9cd9,0x9cdc,0x9cdd,
+ 0x9cdf,0x9ce2,0x977c,0x9785,0x9791,0x9792,0x9794,0x97af,0x97ab,0x97a3,
+ 0x97b2,0x97b4,0x9ab1,0x9ab0,0x9ab7,0x9e58,0x9ab6,0x9aba,0x9abc,0x9ac1,
+ 0x9ac0,0x9ac5,0x9ac2,0x9acb,0x9acc,0x9ad1,0x9b45,0x9b43,0x9b47,0x9b49,
+ 0x9b48,0x9b4d,0x9b51,0x98e8,0x990d,0x992e,0x9955,0x9954,0x9adf,0x9ae1,
+ 0x9ae6,0x9aef,0x9aeb,0x9afb,0x9aed,0x9af9,0x9b08,0x9b0f,0x9b13,0x9b1f,
+ 0x9b23,0x9ebd,0x9ebe,0x7e3b,0x9e82,0x9e87,0x9e88,0x9e8b,0x9e92,0x93d6,
+ 0x9e9d,0x9e9f,0x9edb,0x9edc,0x9edd,0x9ee0,0x9edf,0x9ee2,0x9ee9,0x9ee7,
+ 0x9ee5,0x9eea,0x9eef,0x9f22,0x9f2c,0x9f2f,0x9f39,0x9f37,0x9f3d,0x9f3e,
+ 0x9f44
+ },
+ { /* ku 78 */
+ 0x9ce3,0x9ce4,0x9ce5,0x9ce6,0x9ce7,0x9ce8,0x9ce9,0x9cea,0x9ceb,0x9cec,
+ 0x9ced,0x9cee,0x9cef,0x9cf0,0x9cf1,0x9cf2,0x9cf3,0x9cf4,0x9cf5,0x9cf6,
+ 0x9cf7,0x9cf8,0x9cf9,0x9cfa,0x9cfb,0x9cfc,0x9cfd,0x9cfe,0x9cff,0x9d00,
+ 0x9d01,0x9d02,0x9d03,0x9d04,0x9d05,0x9d06,0x9d07,0x9d08,0x9d09,0x9d0a,
+ 0x9d0b,0x9d0c,0x9d0d,0x9d0e,0x9d0f,0x9d10,0x9d11,0x9d12,0x9d13,0x9d14,
+ 0x9d15,0x9d16,0x9d17,0x9d18,0x9d19,0x9d1a,0x9d1b,0x9d1c,0x9d1d,0x9d1e,
+ 0x9d1f,0x9d20,0x9d21,UBOGON,0x9d22,0x9d23,0x9d24,0x9d25,0x9d26,0x9d27,
+ 0x9d28,0x9d29,0x9d2a,0x9d2b,0x9d2c,0x9d2d,0x9d2e,0x9d2f,0x9d30,0x9d31,
+ 0x9d32,0x9d33,0x9d34,0x9d35,0x9d36,0x9d37,0x9d38,0x9d39,0x9d3a,0x9d3b,
+ 0x9d3c,0x9d3d,0x9d3e,0x9d3f,0x9d40,0x9d41,0x9d42,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 79 */
+ 0x9d43,0x9d44,0x9d45,0x9d46,0x9d47,0x9d48,0x9d49,0x9d4a,0x9d4b,0x9d4c,
+ 0x9d4d,0x9d4e,0x9d4f,0x9d50,0x9d51,0x9d52,0x9d53,0x9d54,0x9d55,0x9d56,
+ 0x9d57,0x9d58,0x9d59,0x9d5a,0x9d5b,0x9d5c,0x9d5d,0x9d5e,0x9d5f,0x9d60,
+ 0x9d61,0x9d62,0x9d63,0x9d64,0x9d65,0x9d66,0x9d67,0x9d68,0x9d69,0x9d6a,
+ 0x9d6b,0x9d6c,0x9d6d,0x9d6e,0x9d6f,0x9d70,0x9d71,0x9d72,0x9d73,0x9d74,
+ 0x9d75,0x9d76,0x9d77,0x9d78,0x9d79,0x9d7a,0x9d7b,0x9d7c,0x9d7d,0x9d7e,
+ 0x9d7f,0x9d80,0x9d81,UBOGON,0x9d82,0x9d83,0x9d84,0x9d85,0x9d86,0x9d87,
+ 0x9d88,0x9d89,0x9d8a,0x9d8b,0x9d8c,0x9d8d,0x9d8e,0x9d8f,0x9d90,0x9d91,
+ 0x9d92,0x9d93,0x9d94,0x9d95,0x9d96,0x9d97,0x9d98,0x9d99,0x9d9a,0x9d9b,
+ 0x9d9c,0x9d9d,0x9d9e,0x9d9f,0x9da0,0x9da1,0x9da2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7a */
+ 0x9da3,0x9da4,0x9da5,0x9da6,0x9da7,0x9da8,0x9da9,0x9daa,0x9dab,0x9dac,
+ 0x9dad,0x9dae,0x9daf,0x9db0,0x9db1,0x9db2,0x9db3,0x9db4,0x9db5,0x9db6,
+ 0x9db7,0x9db8,0x9db9,0x9dba,0x9dbb,0x9dbc,0x9dbd,0x9dbe,0x9dbf,0x9dc0,
+ 0x9dc1,0x9dc2,0x9dc3,0x9dc4,0x9dc5,0x9dc6,0x9dc7,0x9dc8,0x9dc9,0x9dca,
+ 0x9dcb,0x9dcc,0x9dcd,0x9dce,0x9dcf,0x9dd0,0x9dd1,0x9dd2,0x9dd3,0x9dd4,
+ 0x9dd5,0x9dd6,0x9dd7,0x9dd8,0x9dd9,0x9dda,0x9ddb,0x9ddc,0x9ddd,0x9dde,
+ 0x9ddf,0x9de0,0x9de1,UBOGON,0x9de2,0x9de3,0x9de4,0x9de5,0x9de6,0x9de7,
+ 0x9de8,0x9de9,0x9dea,0x9deb,0x9dec,0x9ded,0x9dee,0x9def,0x9df0,0x9df1,
+ 0x9df2,0x9df3,0x9df4,0x9df5,0x9df6,0x9df7,0x9df8,0x9df9,0x9dfa,0x9dfb,
+ 0x9dfc,0x9dfd,0x9dfe,0x9dff,0x9e00,0x9e01,0x9e02,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7b */
+ 0x9e03,0x9e04,0x9e05,0x9e06,0x9e07,0x9e08,0x9e09,0x9e0a,0x9e0b,0x9e0c,
+ 0x9e0d,0x9e0e,0x9e0f,0x9e10,0x9e11,0x9e12,0x9e13,0x9e14,0x9e15,0x9e16,
+ 0x9e17,0x9e18,0x9e19,0x9e1a,0x9e1b,0x9e1c,0x9e1d,0x9e1e,0x9e24,0x9e27,
+ 0x9e2e,0x9e30,0x9e34,0x9e3b,0x9e3c,0x9e40,0x9e4d,0x9e50,0x9e52,0x9e53,
+ 0x9e54,0x9e56,0x9e59,0x9e5d,0x9e5f,0x9e60,0x9e61,0x9e62,0x9e65,0x9e6e,
+ 0x9e6f,0x9e72,0x9e74,0x9e75,0x9e76,0x9e77,0x9e78,0x9e79,0x9e7a,0x9e7b,
+ 0x9e7c,0x9e7d,0x9e80,UBOGON,0x9e81,0x9e83,0x9e84,0x9e85,0x9e86,0x9e89,
+ 0x9e8a,0x9e8c,0x9e8d,0x9e8e,0x9e8f,0x9e90,0x9e91,0x9e94,0x9e95,0x9e96,
+ 0x9e97,0x9e98,0x9e99,0x9e9a,0x9e9b,0x9e9c,0x9e9e,0x9ea0,0x9ea1,0x9ea2,
+ 0x9ea3,0x9ea4,0x9ea5,0x9ea7,0x9ea8,0x9ea9,0x9eaa,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7c */
+ 0x9eab,0x9eac,0x9ead,0x9eae,0x9eaf,0x9eb0,0x9eb1,0x9eb2,0x9eb3,0x9eb5,
+ 0x9eb6,0x9eb7,0x9eb9,0x9eba,0x9ebc,0x9ebf,0x9ec0,0x9ec1,0x9ec2,0x9ec3,
+ 0x9ec5,0x9ec6,0x9ec7,0x9ec8,0x9eca,0x9ecb,0x9ecc,0x9ed0,0x9ed2,0x9ed3,
+ 0x9ed5,0x9ed6,0x9ed7,0x9ed9,0x9eda,0x9ede,0x9ee1,0x9ee3,0x9ee4,0x9ee6,
+ 0x9ee8,0x9eeb,0x9eec,0x9eed,0x9eee,0x9ef0,0x9ef1,0x9ef2,0x9ef3,0x9ef4,
+ 0x9ef5,0x9ef6,0x9ef7,0x9ef8,0x9efa,0x9efd,0x9eff,0x9f00,0x9f01,0x9f02,
+ 0x9f03,0x9f04,0x9f05,UBOGON,0x9f06,0x9f07,0x9f08,0x9f09,0x9f0a,0x9f0c,
+ 0x9f0f,0x9f11,0x9f12,0x9f14,0x9f15,0x9f16,0x9f18,0x9f1a,0x9f1b,0x9f1c,
+ 0x9f1d,0x9f1e,0x9f1f,0x9f21,0x9f23,0x9f24,0x9f25,0x9f26,0x9f27,0x9f28,
+ 0x9f29,0x9f2a,0x9f2b,0x9f2d,0x9f2e,0x9f30,0x9f31,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7d */
+ 0x9f32,0x9f33,0x9f34,0x9f35,0x9f36,0x9f38,0x9f3a,0x9f3c,0x9f3f,0x9f40,
+ 0x9f41,0x9f42,0x9f43,0x9f45,0x9f46,0x9f47,0x9f48,0x9f49,0x9f4a,0x9f4b,
+ 0x9f4c,0x9f4d,0x9f4e,0x9f4f,0x9f52,0x9f53,0x9f54,0x9f55,0x9f56,0x9f57,
+ 0x9f58,0x9f59,0x9f5a,0x9f5b,0x9f5c,0x9f5d,0x9f5e,0x9f5f,0x9f60,0x9f61,
+ 0x9f62,0x9f63,0x9f64,0x9f65,0x9f66,0x9f67,0x9f68,0x9f69,0x9f6a,0x9f6b,
+ 0x9f6c,0x9f6d,0x9f6e,0x9f6f,0x9f70,0x9f71,0x9f72,0x9f73,0x9f74,0x9f75,
+ 0x9f76,0x9f77,0x9f78,UBOGON,0x9f79,0x9f7a,0x9f7b,0x9f7c,0x9f7d,0x9f7e,
+ 0x9f81,0x9f82,0x9f8d,0x9f8e,0x9f8f,0x9f90,0x9f91,0x9f92,0x9f93,0x9f94,
+ 0x9f95,0x9f96,0x9f97,0x9f98,0x9f9c,0x9f9d,0x9f9e,0x9fa1,0x9fa2,0x9fa3,
+ 0x9fa4,0x9fa5,0xf92c,0xf979,0xf995,0xf9e7,0xf9f1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ }
+};
diff --git a/imap/src/charset/ibm.c b/imap/src/charset/ibm.c
new file mode 100644
index 00000000..1a717329
--- /dev/null
+++ b/imap/src/charset/ibm.c
@@ -0,0 +1,347 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IBM conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 November 2002
+ * Last Edited: 30 August 2006
+ */
+
+ /* IBM Latin US */
+static const unsigned short ibm_437tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Greek */
+static const unsigned short ibm_737tab[128] = {
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c3,0x03c2,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03c9,0x03ac,0x03ad,0x03ae,0x03ca,0x03af,0x03cc,0x03cd,
+ 0x03cb,0x03ce,0x0386,0x0388,0x0389,0x038a,0x038c,0x038e,
+ 0x038f,0x00b1,0x2265,0x2264,0x03aa,0x03ab,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Baltic Rim */
+static const unsigned short ibm_775tab[128] = {
+ 0x0106,0x00fc,0x00e9,0x0101,0x00e4,0x0123,0x00e5,0x0107,
+ 0x0142,0x0113,0x0156,0x0157,0x012b,0x0179,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x014d,0x00f6,0x0122,0x00a2,0x015a,
+ 0x015b,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x00a4,
+ 0x0100,0x012a,0x00f3,0x017b,0x017c,0x017a,0x201d,0x00a6,
+ 0x00a9,0x00ae,0x00ac,0x00bd,0x00bc,0x0141,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x0104,0x010c,0x0118,
+ 0x0116,0x2563,0x2551,0x2557,0x255d,0x012e,0x0160,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0172,0x016a,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x017d,
+ 0x0105,0x010d,0x0119,0x0117,0x012f,0x0161,0x0173,0x016b,
+ 0x017e,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x00d3,0x00df,0x014c,0x0143,0x00f5,0x00d5,0x00b5,0x0144,
+ 0x0136,0x0137,0x013b,0x013c,0x0146,0x0112,0x0145,0x2019,
+ 0x00ad,0x00b1,0x201c,0x00be,0x00b6,0x00a7,0x00f7,0x201e,
+ 0x00b0,0x2219,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Latin 1 */
+static const unsigned short ibm_850tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,
+ 0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x00f0,0x00d0,0x00ca,0x00cb,0x00c8,0x0131,0x00cd,0x00ce,
+ 0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
+ 0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x00fe,
+ 0x00de,0x00da,0x00db,0x00d9,0x00fd,0x00dd,0x00af,0x00b4,
+ 0x00ad,0x00b1,0x2017,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Latin 2 */
+static const unsigned short ibm_852tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x016f,0x0107,0x00e7,
+ 0x0142,0x00eb,0x0150,0x0151,0x00ee,0x0179,0x00c4,0x0106,
+ 0x00c9,0x0139,0x013a,0x00f4,0x00f6,0x013d,0x013e,0x015a,
+ 0x015b,0x00d6,0x00dc,0x0164,0x0165,0x0141,0x00d7,0x010d,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x0104,0x0105,0x017d,0x017e,
+ 0x0118,0x0119,0x00ac,0x017a,0x010c,0x015f,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x011a,
+ 0x015e,0x2563,0x2551,0x2557,0x255d,0x017b,0x017c,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0102,0x0103,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x0111,0x0110,0x010e,0x00cb,0x010f,0x0147,0x00cd,0x00ce,
+ 0x011b,0x2518,0x250c,0x2588,0x2584,0x0162,0x016e,0x2580,
+ 0x00d3,0x00df,0x00d4,0x0143,0x0144,0x0148,0x0160,0x0161,
+ 0x0154,0x00da,0x0155,0x0170,0x00fd,0x00dd,0x0163,0x00b4,
+ 0x00ad,0x02dd,0x02db,0x02c7,0x02d8,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x02d9,0x0171,0x0158,0x0159,0x25a0,0x00a0
+};
+
+ /* IBM Cyrillic */
+static const unsigned short ibm_855tab[128] = {
+ 0x0452,0x0402,0x0453,0x0403,0x0451,0x0401,0x0454,0x0404,
+ 0x0455,0x0405,0x0456,0x0406,0x0457,0x0407,0x0458,0x0408,
+ 0x0459,0x0409,0x045a,0x040a,0x045b,0x040b,0x045c,0x040c,
+ 0x045e,0x040e,0x045f,0x040f,0x044e,0x042e,0x044a,0x042a,
+ 0x0430,0x0410,0x0431,0x0411,0x0446,0x0426,0x0434,0x0414,
+ 0x0435,0x0415,0x0444,0x0424,0x0433,0x0413,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x0445,0x0425,0x0438,
+ 0x0418,0x2563,0x2551,0x2557,0x255d,0x0439,0x0419,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x043a,0x041a,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x043b,0x041b,0x043c,0x041c,0x043d,0x041d,0x043e,0x041e,
+ 0x043f,0x2518,0x250c,0x2588,0x2584,0x041f,0x044f,0x2580,
+ 0x042f,0x0440,0x0420,0x0441,0x0421,0x0442,0x0422,0x0443,
+ 0x0423,0x0436,0x0416,0x0432,0x0412,0x044c,0x042c,0x2116,
+ 0x00ad,0x044b,0x042b,0x0437,0x0417,0x0448,0x0428,0x044d,
+ 0x042d,0x0449,0x0429,0x0447,0x0427,0x00a7,0x25a0,0x00a0
+};
+
+ /* IBM Turkish */
+static const unsigned short ibm_857tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x0131,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x0130,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x015e,0x015f,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x011e,0x011f,
+ 0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,
+ 0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x00ba,0x00aa,0x00ca,0x00cb,0x00c8,UBOGON,0x00cd,0x00ce,
+ 0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
+ 0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,UBOGON,
+ 0x00d7,0x00da,0x00db,0x00d9,0x00ec,0x00ff,0x00af,0x00b4,
+ 0x00ad,0x00b1,UBOGON,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Portuguese */
+static const unsigned short ibm_860tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e3,0x00e0,0x00c1,0x00e7,
+ 0x00ea,0x00ca,0x00e8,0x00cd,0x00d4,0x00ec,0x00c3,0x00c2,
+ 0x00c9,0x00c0,0x00c8,0x00f4,0x00f5,0x00f2,0x00da,0x00f9,
+ 0x00cc,0x00d5,0x00dc,0x00a2,0x00a3,0x00d9,0x20a7,0x00d3,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x00d2,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Icelandic */
+static const unsigned short ibm_861tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00d0,0x00f0,0x00de,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00fe,0x00fb,0x00dd,
+ 0x00fd,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00c1,0x00cd,0x00d3,0x00da,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Hebrew */
+static const unsigned short ibm_862tab[128] = {
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Canada/French */
+static const unsigned short ibm_863tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00c2,0x00e0,0x00b6,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x2017,0x00c0,0x00a7,
+ 0x00c9,0x00c8,0x00ca,0x00f4,0x00cb,0x00cf,0x00fb,0x00f9,
+ 0x00a4,0x00d4,0x00dc,0x00a2,0x00a3,0x00d9,0x00db,0x0192,
+ 0x00a6,0x00b4,0x00f3,0x00fa,0x00a8,0x00b8,0x00b3,0x00af,
+ 0x00ce,0x2310,0x00ac,0x00bd,0x00bc,0x00be,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Arabic */
+static const unsigned short ibm_864tab[128] = {
+ 0x00b0,0x00b7,0x2219,0x221a,0x2592,0x2500,0x2502,0x253c,
+ 0x2524,0x252c,0x251c,0x2534,0x2510,0x250c,0x2514,0x2518,
+ 0x03b2,0x221e,0x03c6,0x00b1,0x00bd,0x00bc,0x2248,0x00ab,
+ 0x00bb,0xfef7,0xfef8,UBOGON,UBOGON,0xfefb,0xfefc,UBOGON,
+ 0x00a0,0x00ad,0xfe82,0x00a3,0x00a4,0xfe84,UBOGON,UBOGON,
+ 0xfe8e,0xfe8f,0xfe95,0xfe99,0x060c,0xfe9d,0xfea1,0xfea5,
+ 0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,
+ 0x0668,0x0669,0xfed1,0x061b,0xfeb1,0xfeb5,0xfeb9,0x061f,
+ 0x00a2,0xfe80,0xfe81,0xfe83,0xfe85,0xfeca,0xfe8b,0xfe8d,
+ 0xfe91,0xfe93,0xfe97,0xfe9b,0xfe9f,0xfea3,0xfea7,0xfea9,
+ 0xfeab,0xfead,0xfeaf,0xfeb3,0xfeb7,0xfebb,0xfebf,0xfec1,
+ 0xfec5,0xfecb,0xfecf,0x00a6,0x00ac,0x00f7,0x00d7,0xfec9,
+ 0x0640,0xfed3,0xfed7,0xfedb,0xfedf,0xfee3,0xfee7,0xfeeb,
+ 0xfeed,0xfeef,0xfef3,0xfebd,0xfecc,0xfece,0xfecd,0xfee1,
+ 0xfe7d,0x0651,0xfee5,0xfee9,0xfeec,0xfef0,0xfef2,0xfed0,
+ 0xfed5,0xfef5,0xfef6,0xfedd,0xfed9,0xfef1,0x25a0,UBOGON
+};
+
+ /* IBM Nordic */
+static const unsigned short ibm_865tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00a4,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Cyrillic/Russian */
+static const unsigned short ibm_866tab[128] = {
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,
+ 0x0401,0x0451,0x0404,0x0454,0x0407,0x0457,0x040e,0x045e,
+ 0x00b0,0x2219,0x00b7,0x221a,0x2116,0x00a4,0x25a0,0x00a0
+};
+
+ /* IBM Greek 2 */
+static const unsigned short ibm_869tab[128] = {
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0386,UBOGON,
+ 0x00b7,0x00ac,0x00a6,0x2018,0x2019,0x0388,0x2015,0x0389,
+ 0x038a,0x03aa,0x038c,UBOGON,UBOGON,0x038e,0x03ab,0x00a9,
+ 0x038f,0x00b2,0x00b3,0x03ac,0x00a3,0x03ad,0x03ae,0x03af,
+ 0x03ca,0x0390,0x03cc,0x03cd,0x0391,0x0392,0x0393,0x0394,
+ 0x0395,0x0396,0x0397,0x00bd,0x0398,0x0399,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x039a,0x039b,0x039c,
+ 0x039d,0x2563,0x2551,0x2557,0x255d,0x039e,0x039f,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x03a0,0x03a1,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x03a3,
+ 0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,
+ 0x03b3,0x2518,0x250c,0x2588,0x2584,0x03b4,0x03b5,0x2580,
+ 0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,
+ 0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c2,0x03c4,0x0384,
+ 0x00ad,0x00b1,0x03c5,0x03c6,0x03c7,0x00a7,0x03c8,0x0385,
+ 0x00b0,0x00a8,0x03c9,0x03cb,0x03b0,0x03ce,0x25a0,0x00a0
+};
+
+ /* IBM Thai */
+static const unsigned short ibm_874tab[128] = {
+ 0x20ac,UBOGON,UBOGON,UBOGON,UBOGON,0x2026,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
diff --git a/imap/src/charset/iso_8859.c b/imap/src/charset/iso_8859.c
new file mode 100644
index 00000000..484c3ecc
--- /dev/null
+++ b/imap/src/charset/iso_8859.c
@@ -0,0 +1,308 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: ISO-8859 conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* ISO-8859 conversion tables */
+
+ /* Latin-2 (East European) */
+static const unsigned short iso8859_2tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x02d8,0x0141,0x00a4,0x013d,0x015a,0x00a7,
+ 0x00a8,0x0160,0x015e,0x0164,0x0179,0x00ad,0x017d,0x017b,
+ 0x00b0,0x0105,0x02db,0x0142,0x00b4,0x013e,0x015b,0x02c7,
+ 0x00b8,0x0161,0x015f,0x0165,0x017a,0x02dd,0x017e,0x017c,
+ 0x0154,0x00c1,0x00c2,0x0102,0x00c4,0x0139,0x0106,0x00c7,
+ 0x010c,0x00c9,0x0118,0x00cb,0x011a,0x00cd,0x00ce,0x010e,
+ 0x0110,0x0143,0x0147,0x00d3,0x00d4,0x0150,0x00d6,0x00d7,
+ 0x0158,0x016e,0x00da,0x0170,0x00dc,0x00dd,0x0162,0x00df,
+ 0x0155,0x00e1,0x00e2,0x0103,0x00e4,0x013a,0x0107,0x00e7,
+ 0x010d,0x00e9,0x0119,0x00eb,0x011b,0x00ed,0x00ee,0x010f,
+ 0x0111,0x0144,0x0148,0x00f3,0x00f4,0x0151,0x00f6,0x00f7,
+ 0x0159,0x016f,0x00fa,0x0171,0x00fc,0x00fd,0x0163,0x02d9
+};
+
+
+ /* Latin-3 (South European) */
+static const unsigned short iso8859_3tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0126,0x02d8,0x00a3,0x00a4,UBOGON,0x0124,0x00a7,
+ 0x00a8,0x0130,0x015e,0x011e,0x0134,0x00ad,UBOGON,0x017b,
+ 0x00b0,0x0127,0x00b2,0x00b3,0x00b4,0x00b5,0x0125,0x00b7,
+ 0x00b8,0x0131,0x015f,0x011f,0x0135,0x00bd,UBOGON,0x017c,
+ 0x00c0,0x00c1,0x00c2,UBOGON,0x00c4,0x010a,0x0108,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ UBOGON,0x00d1,0x00d2,0x00d3,0x00d4,0x0120,0x00d6,0x00d7,
+ 0x011c,0x00d9,0x00da,0x00db,0x00dc,0x016c,0x015c,0x00df,
+ 0x00e0,0x00e1,0x00e2,UBOGON,0x00e4,0x010b,0x0109,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ UBOGON,0x00f1,0x00f2,0x00f3,0x00f4,0x0121,0x00f6,0x00f7,
+ 0x011d,0x00f9,0x00fa,0x00fb,0x00fc,0x016d,0x015d,0x02d9
+};
+
+
+ /* Latin-4 (North European) */
+static const unsigned short iso8859_4tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0138,0x0156,0x00a4,0x0128,0x013b,0x00a7,
+ 0x00a8,0x0160,0x0112,0x0122,0x0166,0x00ad,0x017d,0x00af,
+ 0x00b0,0x0105,0x02db,0x0157,0x00b4,0x0129,0x013c,0x02c7,
+ 0x00b8,0x0161,0x0113,0x0123,0x0167,0x014a,0x017e,0x014b,
+ 0x0100,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x012e,
+ 0x010c,0x00c9,0x0118,0x00cb,0x0116,0x00cd,0x00ce,0x012a,
+ 0x0110,0x0145,0x014c,0x0136,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x0172,0x00da,0x00db,0x00dc,0x0168,0x016a,0x00df,
+ 0x0101,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x012f,
+ 0x010d,0x00e9,0x0119,0x00eb,0x0117,0x00ed,0x00ee,0x012b,
+ 0x0111,0x0146,0x014d,0x0137,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x0173,0x00fa,0x00fb,0x00fc,0x0169,0x016b,0x02d9
+};
+
+
+ /* Cyrillic */
+static const unsigned short iso8859_5tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x00ad,0x040e,0x040f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,
+ 0x2116,0x0451,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,
+ 0x0458,0x0459,0x045a,0x045b,0x045c,0x00a7,0x045e,0x045f
+};
+
+
+ /* Arabic */
+static const unsigned short iso8859_6tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,UBOGON,UBOGON,UBOGON,0x00a4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x060c,0x00ad,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x061b,UBOGON,UBOGON,UBOGON,0x061f,
+ UBOGON,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x0637,
+ 0x0638,0x0639,0x063a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x0640,0x0641,0x0642,0x0643,0x0644,0x0645,0x0646,0x0647,
+ 0x0648,0x0649,0x064a,0x064b,0x064c,0x064d,0x064e,0x064f,
+ 0x0650,0x0651,0x0652,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+};
+
+
+ /* Greek */
+static const unsigned short iso8859_7tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x2018,0x2019,0x00a3,UBOGON,UBOGON,0x00a6,0x00a7,
+ 0x00a8,0x00a9,UBOGON,0x00ab,0x00ac,0x00ad,UBOGON,0x2015,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x0384,0x0385,0x0386,0x00b7,
+ 0x0388,0x0389,0x038a,0x00bb,0x038c,0x00bd,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,UBOGON,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x03ac,0x03ad,0x03ae,0x03af,
+ 0x03b0,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,
+ 0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,
+ 0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,0x03ce,UBOGON
+};
+
+
+ /* Hebrew */
+static const unsigned short iso8859_8tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,UBOGON,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00d7,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00f7,0x00bb,0x00bc,0x00bd,0x00be,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2017,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,UBOGON,UBOGON,0x200e,0x200f,UBOGON
+};
+
+
+ /* Latin-5 (Turkish) */
+static const unsigned short iso8859_9tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x011e,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x0130,0x015e,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x011f,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x0131,0x015f,0x00ff
+};
+
+
+ /* Latin-6 (Nordic) */
+static const unsigned short iso8859_10tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0112,0x0122,0x012a,0x0128,0x0136,0x00a7,
+ 0x013b,0x0110,0x0160,0x0166,0x017d,0x00ad,0x016a,0x014a,
+ 0x00b0,0x0105,0x0113,0x0123,0x012b,0x0129,0x0137,0x00b7,
+ 0x013c,0x0111,0x0161,0x0167,0x017e,0x2015,0x016b,0x014b,
+ 0x0100,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x012e,
+ 0x010c,0x00c9,0x0118,0x00cb,0x0116,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x0145,0x014c,0x00d3,0x00d4,0x00d5,0x00d6,0x0168,
+ 0x00d8,0x0172,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x0101,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x012f,
+ 0x010d,0x00e9,0x0119,0x00eb,0x0117,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x0146,0x014d,0x00f3,0x00f4,0x00f5,0x00f6,0x0169,
+ 0x00f8,0x0173,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x0138
+};
+
+
+ /* Thai */
+#define iso8859_11tab tis620tab
+
+ /* reserved for ISCII Indian */
+
+ /* Latin-7 (Baltic) */
+static const unsigned short iso8859_13tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x201d,0x00a2,0x00a3,0x00a4,0x201e,0x00a6,0x00a7,
+ 0x00d8,0x00a9,0x0156,0x00ab,0x00ac,0x00ad,0x00ae,0x00c6,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x201c,0x00b5,0x00b6,0x00b7,
+ 0x00f8,0x00b9,0x0157,0x00bb,0x00bc,0x00bd,0x00be,0x00e6,
+ 0x0104,0x012e,0x0100,0x0106,0x00c4,0x00c5,0x0118,0x0112,
+ 0x010c,0x00c9,0x0179,0x0116,0x0122,0x0136,0x012a,0x013b,
+ 0x0160,0x0143,0x0145,0x00d3,0x014c,0x00d5,0x00d6,0x00d7,
+ 0x0172,0x0141,0x015a,0x016a,0x00dc,0x017b,0x017d,0x00df,
+ 0x0105,0x012f,0x0101,0x0107,0x00e4,0x00e5,0x0119,0x0113,
+ 0x010d,0x00e9,0x017a,0x0117,0x0123,0x0137,0x012b,0x013c,
+ 0x0161,0x0144,0x0146,0x00f3,0x014d,0x00f5,0x00f6,0x00f7,
+ 0x0173,0x0142,0x015b,0x016b,0x00fc,0x017c,0x017e,0x2019
+};
+
+
+ /* Latin-8 (Celtic) */
+
+static const unsigned short iso8859_14tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x1e02,0x1e03,0x00a3,0x010a,0x010b,0x1e0a,0x00a7,
+ 0x1e80,0x00a9,0x1e82,0x1e0b,0x1ef2,0x00ad,0x00ae,0x0178,
+ 0x1e1e,0x1e1f,0x0120,0x0121,0x1e40,0x1e41,0x00b6,0x1e56,
+ 0x1e81,0x1e57,0x1e83,0x1e60,0x1ef3,0x1e84,0x1e85,0x1e61,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x0174,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x1e6a,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x0176,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x0175,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x1e6b,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x0177,0x00ff
+};
+
+
+ /* Latin-9 a.k.a. Latin-0 (Euro) */
+static const unsigned short iso8859_15tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x20ac,0x00a5,0x0160,0x00a7,
+ 0x0161,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x017d,0x00b5,0x00b6,0x00b7,
+ 0x017e,0x00b9,0x00ba,0x00bb,0x0152,0x0153,0x0178,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff
+};
+
+
+ /* Latin-10 (Balkan) */
+
+static const unsigned short iso8859_16tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0105,0x0141,0x20ac,0x201e,0x0160,0x00a7,
+ 0x0161,0x00a9,0x0218,0x00ab,0x0179,0x00ad,0x017a,0x017b,
+ 0x00b0,0x00b1,0x010c,0x0142,0x017d,0x201d,0x00b6,0x00b7,
+ 0x017e,0x010d,0x0219,0x00bb,0x0152,0x0153,0x0178,0x017c,
+ 0x00c0,0x00c1,0x00c2,0x0102,0x00c4,0x0106,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x0110,0x0143,0x00d2,0x00d3,0x00d4,0x0150,0x00d6,0x015a,
+ 0x0170,0x00d9,0x00da,0x00db,0x00dc,0x0118,0x021a,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x0103,0x00e4,0x0107,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x0111,0x0144,0x00f2,0x00f3,0x00f4,0x0151,0x00f6,0x015b,
+ 0x0171,0x00f9,0x00fa,0x00fb,0x00fc,0x0119,0x021b,0x00ff
+};
diff --git a/imap/src/charset/jis_0208.c b/imap/src/charset/jis_0208.c
new file mode 100644
index 00000000..cdb8f4f6
--- /dev/null
+++ b/imap/src/charset/jis_0208.c
@@ -0,0 +1,1092 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: JIS X0208 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* JIS X0208 is the industrial standard of Japan. */
+
+#define BASE_JIS0208_KU 0x21
+#define BASE_JIS0208_TEN 0x21
+#define MAX_JIS0208_KU 84
+#define MAX_JIS0208_TEN 94
+
+
+#define SJISTOJIS(c,c1) \
+ c = ((c - ((c < 0xa0) ? 0x70 : 0xb0)) << 1); \
+ if (c1 < 0x9f) { \
+ c--; \
+ c1 -= 0x1f + (c1 > 0x7f); \
+ } \
+ else c1 -= 0x7e;
+
+
+#define JISTOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_JIS0208_KU) < MAX_JIS0208_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_JIS0208_TEN) < MAX_JIS0208_TEN)) ? \
+ jis0208tab[ku][ten] : UBOGON)
+
+
+static const unsigned short jis0208tab[MAX_JIS0208_KU][MAX_JIS0208_TEN] = {
+ { /* ku 01 */
+ 0x3000,0x3001,0x3002,0xff0c,0xff0e,0x30fb,0xff1a,0xff1b,0xff1f,0xff01,
+ 0x309b,0x309c,0x00b4,0xff40,0x00a8,0xff3e,0xffe3,0xff3f,0x30fd,0x30fe,
+ 0x309d,0x309e,0x3003,0x4edd,0x3005,0x3006,0x3007,0x30fc,0x2015,0x2010,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT shows 01/32 as U+005C instead of U+FF3C.
+ *
+ * AOL suggests that 01/33 should be U+FF5E instead of U+301C and
+ * 01/34 should be U+2225 instead of U+2016.
+ * I disagree; 01/33 is JIS punctuation (not a tilde); and 01/34 is
+ * double vertical line.
+ */
+ 0xff0f,0xff3c,0x301c,0x2016,0xff5c,0x2026,0x2025,0x2018,0x2019,0x201c,
+ 0x201d,0xff08,0xff09,0x3014,0x3015,0xff3b,0xff3d,0xff5b,0xff5d,0x3008,
+ 0x3009,0x300a,0x300b,0x300c,0x300d,0x300e,0x300f,0x3010,0x3011,0xff0b,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT 01/61 has U+2212 instead of U+FF0D.
+ */
+ 0xff0d,0x00b1,0x00d7,0x00f7,0xff1d,0x2260,0xff1c,0xff1e,0x2266,0x2267,
+ 0x221e,0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,0xffe5,0xff04,
+ /* Fullwidth/halfwidth correction:
+ * JIS 0208 shows 01/81 as U+00A2 instead of U+FFE0, and
+ * 01/82 as U+00A3 instead of U+FFE1.
+ */
+ 0xffe0,0xffe1,0xff05,0xff03,0xff06,0xff0a,0xff20,0x00a7,0x2606,0x2605,
+ 0x25cb,0x25cf,0x25ce,0x25c7
+ },
+ { /* ku 02 */
+ 0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x25bd,0x25bc,0x203b,0x3012,0x2192,
+ 0x2190,0x2191,0x2193,0x3013,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2208,0x220b,0x2286,0x2287,0x2282,
+ 0x2283,0x222a,0x2229,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT shows 02/44 as U+00AC instead of U+FFE2.
+ */
+ UBOGON,0x2227,0x2228,0xffe2,0x21d2,0x21d4,0x2200,0x2203,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2220,
+ 0x22a5,0x2312,0x2202,0x2207,0x2261,0x2252,0x226a,0x226b,0x221a,0x223d,
+ 0x221d,0x2235,0x222b,0x222c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x212b,0x2030,0x266f,0x266d,0x266a,0x2020,0x2021,0x00b6,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x25ef
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xff10,0xff11,0xff12,0xff13,0xff14,
+ 0xff15,0xff16,0xff17,0xff18,0xff19,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,
+ 0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,
+ 0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,
+ 0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,
+ 0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,
+ 0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,
+ 0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,
+ 0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,
+ 0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,
+ 0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,
+ 0x3091,0x3092,0x3093,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ 0x30a1,0x30a2,0x30a3,0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,
+ 0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,
+ 0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,
+ 0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,
+ 0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,
+ 0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,
+ 0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,
+ 0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,
+ 0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,
+ 0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,
+ 0x042d,0x042e,0x042f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,
+ 0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,
+ 0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,
+ 0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,
+ 0x044f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x2500,0x2502,0x250c,0x2510,0x2518,0x2514,0x251c,0x252c,0x2524,0x2534,
+ 0x253c,0x2501,0x2503,0x250f,0x2513,0x251b,0x2517,0x2523,0x2533,0x252b,
+ 0x253b,0x254b,0x2520,0x252f,0x2528,0x2537,0x253f,0x251d,0x2530,0x2525,
+ 0x2538,0x2542,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+#if 0
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+#else /* additional mappings suggested by AOL */
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,
+ 0x246a,0x246b,0x246c,0x246d,0x246e,0x246f,0x2470,0x2471,0x2472,0x2473,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ UBOGON,0x3349,0x3314,0x3322,0x334d,0x3318,0x3327,0x3303,0x3336,0x3351,
+ 0x3357,0x330d,0x3326,0x3323,0x332b,0x334a,0x333b,0x339c,0x339d,0x339e,
+ 0x338e,0x338f,0x33c4,0x33a1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x337b,0x301d,0x301f,0x2116,0x33cd,0x2121,0x32a4,0x32a5,
+ 0x32a6,0x32a7,0x32a8,0x3231,0x3232,0x3239,0x337e,0x337d,0x337c,UBOGON,
+ UBOGON,UBOGON,0x222e,0x2211,UBOGON,UBOGON,UBOGON,0x221f,0x22bf,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+#endif
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x4e9c,0x5516,0x5a03,0x963f,0x54c0,0x611b,0x6328,0x59f6,0x9022,0x8475,
+ 0x831c,0x7a50,0x60aa,0x63e1,0x6e25,0x65ed,0x8466,0x82a6,0x9bf5,0x6893,
+ 0x5727,0x65a1,0x6271,0x5b9b,0x59d0,0x867b,0x98f4,0x7d62,0x7dbe,0x9b8e,
+ 0x6216,0x7c9f,0x88b7,0x5b89,0x5eb5,0x6309,0x6697,0x6848,0x95c7,0x978d,
+ 0x674f,0x4ee5,0x4f0a,0x4f4d,0x4f9d,0x5049,0x56f2,0x5937,0x59d4,0x5a01,
+ 0x5c09,0x60df,0x610f,0x6170,0x6613,0x6905,0x70ba,0x754f,0x7570,0x79fb,
+ 0x7dad,0x7def,0x80c3,0x840e,0x8863,0x8b02,0x9055,0x907a,0x533b,0x4e95,
+ 0x4ea5,0x57df,0x80b2,0x90c1,0x78ef,0x4e00,0x58f1,0x6ea2,0x9038,0x7a32,
+ 0x8328,0x828b,0x9c2f,0x5141,0x5370,0x54bd,0x54e1,0x56e0,0x59fb,0x5f15,
+ 0x98f2,0x6deb,0x80e4,0x852d
+ },
+ { /* ku 11 */
+ 0x9662,0x9670,0x96a0,0x97fb,0x540b,0x53f3,0x5b87,0x70cf,0x7fbd,0x8fc2,
+ 0x96e8,0x536f,0x9d5c,0x7aba,0x4e11,0x7893,0x81fc,0x6e26,0x5618,0x5504,
+ 0x6b1d,0x851a,0x9c3b,0x59e5,0x53a9,0x6d66,0x74dc,0x958f,0x5642,0x4e91,
+ 0x904b,0x96f2,0x834f,0x990c,0x53e1,0x55b6,0x5b30,0x5f71,0x6620,0x66f3,
+ 0x6804,0x6c38,0x6cf3,0x6d29,0x745b,0x76c8,0x7a4e,0x9834,0x82f1,0x885b,
+ 0x8a60,0x92ed,0x6db2,0x75ab,0x76ca,0x99c5,0x60a6,0x8b01,0x8d8a,0x95b2,
+ 0x698e,0x53ad,0x5186,0x5712,0x5830,0x5944,0x5bb4,0x5ef6,0x6028,0x63a9,
+ 0x63f4,0x6cbf,0x6f14,0x708e,0x7114,0x7159,0x71d5,0x733f,0x7e01,0x8276,
+ 0x82d1,0x8597,0x9060,0x925b,0x9d1b,0x5869,0x65bc,0x6c5a,0x7525,0x51f9,
+ 0x592e,0x5965,0x5f80,0x5fdc
+ },
+ { /* ku 12 */
+ 0x62bc,0x65fa,0x6a2a,0x6b27,0x6bb4,0x738b,0x7fc1,0x8956,0x9d2c,0x9d0e,
+ 0x9ec4,0x5ca1,0x6c96,0x837b,0x5104,0x5c4b,0x61b6,0x81c6,0x6876,0x7261,
+ 0x4e59,0x4ffa,0x5378,0x6069,0x6e29,0x7a4f,0x97f3,0x4e0b,0x5316,0x4eee,
+ 0x4f55,0x4f3d,0x4fa1,0x4f73,0x52a0,0x53ef,0x5609,0x590f,0x5ac1,0x5bb6,
+ 0x5be1,0x79d1,0x6687,0x679c,0x67b6,0x6b4c,0x6cb3,0x706b,0x73c2,0x798d,
+ 0x79be,0x7a3c,0x7b87,0x82b1,0x82db,0x8304,0x8377,0x83ef,0x83d3,0x8766,
+ 0x8ab2,0x5629,0x8ca8,0x8fe6,0x904e,0x971e,0x868a,0x4fc4,0x5ce8,0x6211,
+ 0x7259,0x753b,0x81e5,0x82bd,0x86fe,0x8cc0,0x96c5,0x9913,0x99d5,0x4ecb,
+ 0x4f1a,0x89e3,0x56de,0x584a,0x58ca,0x5efb,0x5feb,0x602a,0x6094,0x6062,
+ 0x61d0,0x6212,0x62d0,0x6539
+ },
+ { /* ku 13 */
+ 0x9b41,0x6666,0x68b0,0x6d77,0x7070,0x754c,0x7686,0x7d75,0x82a5,0x87f9,
+ 0x958b,0x968e,0x8c9d,0x51f1,0x52be,0x5916,0x54b3,0x5bb3,0x5d16,0x6168,
+ 0x6982,0x6daf,0x788d,0x84cb,0x8857,0x8a72,0x93a7,0x9ab8,0x6d6c,0x99a8,
+ 0x86d9,0x57a3,0x67ff,0x86ce,0x920e,0x5283,0x5687,0x5404,0x5ed3,0x62e1,
+ 0x64b9,0x683c,0x6838,0x6bbb,0x7372,0x78ba,0x7a6b,0x899a,0x89d2,0x8d6b,
+ 0x8f03,0x90ed,0x95a3,0x9694,0x9769,0x5b66,0x5cb3,0x697d,0x984d,0x984e,
+ 0x639b,0x7b20,0x6a2b,0x6a7f,0x68b6,0x9c0d,0x6f5f,0x5272,0x559d,0x6070,
+ 0x62ec,0x6d3b,0x6e07,0x6ed1,0x845b,0x8910,0x8f44,0x4e14,0x9c39,0x53f6,
+ 0x691b,0x6a3a,0x9784,0x682a,0x515c,0x7ac3,0x84b2,0x91dc,0x938c,0x565b,
+ 0x9d28,0x6822,0x8305,0x8431
+ },
+ { /* ku 14 */
+ 0x7ca5,0x5208,0x82c5,0x74e6,0x4e7e,0x4f83,0x51a0,0x5bd2,0x520a,0x52d8,
+ 0x52e7,0x5dfb,0x559a,0x582a,0x59e6,0x5b8c,0x5b98,0x5bdb,0x5e72,0x5e79,
+ 0x60a3,0x611f,0x6163,0x61be,0x63db,0x6562,0x67d1,0x6853,0x68fa,0x6b3e,
+ 0x6b53,0x6c57,0x6f22,0x6f97,0x6f45,0x74b0,0x7518,0x76e3,0x770b,0x7aff,
+ 0x7ba1,0x7c21,0x7de9,0x7f36,0x7ff0,0x809d,0x8266,0x839e,0x89b3,0x8acc,
+ 0x8cab,0x9084,0x9451,0x9593,0x9591,0x95a2,0x9665,0x97d3,0x9928,0x8218,
+ 0x4e38,0x542b,0x5cb8,0x5dcc,0x73a9,0x764c,0x773c,0x5ca9,0x7feb,0x8d0b,
+ 0x96c1,0x9811,0x9854,0x9858,0x4f01,0x4f0e,0x5371,0x559c,0x5668,0x57fa,
+ 0x5947,0x5b09,0x5bc4,0x5c90,0x5e0c,0x5e7e,0x5fcc,0x63ee,0x673a,0x65d7,
+ 0x65e2,0x671f,0x68cb,0x68c4
+ },
+ { /* ku 15 */
+ 0x6a5f,0x5e30,0x6bc5,0x6c17,0x6c7d,0x757f,0x7948,0x5b63,0x7a00,0x7d00,
+ 0x5fbd,0x898f,0x8a18,0x8cb4,0x8d77,0x8ecc,0x8f1d,0x98e2,0x9a0e,0x9b3c,
+ 0x4e80,0x507d,0x5100,0x5993,0x5b9c,0x622f,0x6280,0x64ec,0x6b3a,0x72a0,
+ 0x7591,0x7947,0x7fa9,0x87fb,0x8abc,0x8b70,0x63ac,0x83ca,0x97a0,0x5409,
+ 0x5403,0x55ab,0x6854,0x6a58,0x8a70,0x7827,0x6775,0x9ecd,0x5374,0x5ba2,
+ 0x811a,0x8650,0x9006,0x4e18,0x4e45,0x4ec7,0x4f11,0x53ca,0x5438,0x5bae,
+ 0x5f13,0x6025,0x6551,0x673d,0x6c42,0x6c72,0x6ce3,0x7078,0x7403,0x7a76,
+ 0x7aae,0x7b08,0x7d1a,0x7cfe,0x7d66,0x65e7,0x725b,0x53bb,0x5c45,0x5de8,
+ 0x62d2,0x62e0,0x6319,0x6e20,0x865a,0x8a31,0x8ddd,0x92f8,0x6f01,0x79a6,
+ 0x9b5a,0x4ea8,0x4eab,0x4eac
+ },
+ { /* ku 16 */
+ 0x4f9b,0x4fa0,0x50d1,0x5147,0x7af6,0x5171,0x51f6,0x5354,0x5321,0x537f,
+ 0x53eb,0x55ac,0x5883,0x5ce1,0x5f37,0x5f4a,0x602f,0x6050,0x606d,0x631f,
+ 0x6559,0x6a4b,0x6cc1,0x72c2,0x72ed,0x77ef,0x80f8,0x8105,0x8208,0x854e,
+ 0x90f7,0x93e1,0x97ff,0x9957,0x9a5a,0x4ef0,0x51dd,0x5c2d,0x6681,0x696d,
+ 0x5c40,0x66f2,0x6975,0x7389,0x6850,0x7c81,0x50c5,0x52e4,0x5747,0x5dfe,
+ 0x9326,0x65a4,0x6b23,0x6b3d,0x7434,0x7981,0x79bd,0x7b4b,0x7dca,0x82b9,
+ 0x83cc,0x887f,0x895f,0x8b39,0x8fd1,0x91d1,0x541f,0x9280,0x4e5d,0x5036,
+ 0x53e5,0x533a,0x72d7,0x7396,0x77e9,0x82e6,0x8eaf,0x99c6,0x99c8,0x99d2,
+ 0x5177,0x611a,0x865e,0x55b0,0x7a7a,0x5076,0x5bd3,0x9047,0x9685,0x4e32,
+ 0x6adb,0x91e7,0x5c51,0x5c48
+ },
+ { /* ku 17 */
+ 0x6398,0x7a9f,0x6c93,0x9774,0x8f61,0x7aaa,0x718a,0x9688,0x7c82,0x6817,
+ 0x7e70,0x6851,0x936c,0x52f2,0x541b,0x85ab,0x8a13,0x7fa4,0x8ecd,0x90e1,
+ 0x5366,0x8888,0x7941,0x4fc2,0x50be,0x5211,0x5144,0x5553,0x572d,0x73ea,
+ 0x578b,0x5951,0x5f62,0x5f84,0x6075,0x6176,0x6167,0x61a9,0x63b2,0x643a,
+ 0x656c,0x666f,0x6842,0x6e13,0x7566,0x7a3d,0x7cfb,0x7d4c,0x7d99,0x7e4b,
+ 0x7f6b,0x830e,0x834a,0x86cd,0x8a08,0x8a63,0x8b66,0x8efd,0x981a,0x9d8f,
+ 0x82b8,0x8fce,0x9be8,0x5287,0x621f,0x6483,0x6fc0,0x9699,0x6841,0x5091,
+ 0x6b20,0x6c7a,0x6f54,0x7a74,0x7d50,0x8840,0x8a23,0x6708,0x4ef6,0x5039,
+ 0x5026,0x5065,0x517c,0x5238,0x5263,0x55a7,0x570f,0x5805,0x5acc,0x5efa,
+ 0x61b2,0x61f8,0x62f3,0x6372
+ },
+ { /* ku 18 */
+ 0x691c,0x6a29,0x727d,0x72ac,0x732e,0x7814,0x786f,0x7d79,0x770c,0x80a9,
+ 0x898b,0x8b19,0x8ce2,0x8ed2,0x9063,0x9375,0x967a,0x9855,0x9a13,0x9e78,
+ 0x5143,0x539f,0x53b3,0x5e7b,0x5f26,0x6e1b,0x6e90,0x7384,0x73fe,0x7d43,
+ 0x8237,0x8a00,0x8afa,0x9650,0x4e4e,0x500b,0x53e4,0x547c,0x56fa,0x59d1,
+ 0x5b64,0x5df1,0x5eab,0x5f27,0x6238,0x6545,0x67af,0x6e56,0x72d0,0x7cca,
+ 0x88b4,0x80a1,0x80e1,0x83f0,0x864e,0x8a87,0x8de8,0x9237,0x96c7,0x9867,
+ 0x9f13,0x4e94,0x4e92,0x4f0d,0x5348,0x5449,0x543e,0x5a2f,0x5f8c,0x5fa1,
+ 0x609f,0x68a7,0x6a8e,0x745a,0x7881,0x8a9e,0x8aa4,0x8b77,0x9190,0x4e5e,
+ 0x9bc9,0x4ea4,0x4f7c,0x4faf,0x5019,0x5016,0x5149,0x516c,0x529f,0x52b9,
+ 0x52fe,0x539a,0x53e3,0x5411
+ },
+ { /* ku 19 */
+ 0x540e,0x5589,0x5751,0x57a2,0x597d,0x5b54,0x5b5d,0x5b8f,0x5de5,0x5de7,
+ 0x5df7,0x5e78,0x5e83,0x5e9a,0x5eb7,0x5f18,0x6052,0x614c,0x6297,0x62d8,
+ 0x63a7,0x653b,0x6602,0x6643,0x66f4,0x676d,0x6821,0x6897,0x69cb,0x6c5f,
+ 0x6d2a,0x6d69,0x6e2f,0x6e9d,0x7532,0x7687,0x786c,0x7a3f,0x7ce0,0x7d05,
+ 0x7d18,0x7d5e,0x7db1,0x8015,0x8003,0x80af,0x80b1,0x8154,0x818f,0x822a,
+ 0x8352,0x884c,0x8861,0x8b1b,0x8ca2,0x8cfc,0x90ca,0x9175,0x9271,0x783f,
+ 0x92fc,0x95a4,0x964d,0x9805,0x9999,0x9ad8,0x9d3b,0x525b,0x52ab,0x53f7,
+ 0x5408,0x58d5,0x62f7,0x6fe0,0x8c6a,0x8f5f,0x9eb9,0x514b,0x523b,0x544a,
+ 0x56fd,0x7a40,0x9177,0x9d60,0x9ed2,0x7344,0x6f09,0x8170,0x7511,0x5ffd,
+ 0x60da,0x9aa8,0x72db,0x8fbc
+ },
+ { /* ku 1a */
+ 0x6b64,0x9803,0x4eca,0x56f0,0x5764,0x58be,0x5a5a,0x6068,0x61c7,0x660f,
+ 0x6606,0x6839,0x68b1,0x6df7,0x75d5,0x7d3a,0x826e,0x9b42,0x4e9b,0x4f50,
+ 0x53c9,0x5506,0x5d6f,0x5de6,0x5dee,0x67fb,0x6c99,0x7473,0x7802,0x8a50,
+ 0x9396,0x88df,0x5750,0x5ea7,0x632b,0x50b5,0x50ac,0x518d,0x6700,0x54c9,
+ 0x585e,0x59bb,0x5bb0,0x5f69,0x624d,0x63a1,0x683d,0x6b73,0x6e08,0x707d,
+ 0x91c7,0x7280,0x7815,0x7826,0x796d,0x658e,0x7d30,0x83dc,0x88c1,0x8f09,
+ 0x969b,0x5264,0x5728,0x6750,0x7f6a,0x8ca1,0x51b4,0x5742,0x962a,0x583a,
+ 0x698a,0x80b4,0x54b2,0x5d0e,0x57fc,0x7895,0x9dfa,0x4f5c,0x524a,0x548b,
+ 0x643e,0x6628,0x6714,0x67f5,0x7a84,0x7b56,0x7d22,0x932f,0x685c,0x9bad,
+ 0x7b39,0x5319,0x518a,0x5237
+ },
+ { /* ku 1b */
+ 0x5bdf,0x62f6,0x64ae,0x64e6,0x672d,0x6bba,0x85a9,0x96d1,0x7690,0x9bd6,
+ 0x634c,0x9306,0x9bab,0x76bf,0x6652,0x4e09,0x5098,0x53c2,0x5c71,0x60e8,
+ 0x6492,0x6563,0x685f,0x71e6,0x73ca,0x7523,0x7b97,0x7e82,0x8695,0x8b83,
+ 0x8cdb,0x9178,0x9910,0x65ac,0x66ab,0x6b8b,0x4ed5,0x4ed4,0x4f3a,0x4f7f,
+ 0x523a,0x53f8,0x53f2,0x55e3,0x56db,0x58eb,0x59cb,0x59c9,0x59ff,0x5b50,
+ 0x5c4d,0x5e02,0x5e2b,0x5fd7,0x601d,0x6307,0x652f,0x5b5c,0x65af,0x65bd,
+ 0x65e8,0x679d,0x6b62,0x6b7b,0x6c0f,0x7345,0x7949,0x79c1,0x7cf8,0x7d19,
+ 0x7d2b,0x80a2,0x8102,0x81f3,0x8996,0x8a5e,0x8a69,0x8a66,0x8a8c,0x8aee,
+ 0x8cc7,0x8cdc,0x96cc,0x98fc,0x6b6f,0x4e8b,0x4f3c,0x4f8d,0x5150,0x5b57,
+ 0x5bfa,0x6148,0x6301,0x6642
+ },
+ { /* ku 1c */
+ 0x6b21,0x6ecb,0x6cbb,0x723e,0x74bd,0x75d4,0x78c1,0x793a,0x800c,0x8033,
+ 0x81ea,0x8494,0x8f9e,0x6c50,0x9e7f,0x5f0f,0x8b58,0x9d2b,0x7afa,0x8ef8,
+ 0x5b8d,0x96eb,0x4e03,0x53f1,0x57f7,0x5931,0x5ac9,0x5ba4,0x6089,0x6e7f,
+ 0x6f06,0x75be,0x8cea,0x5b9f,0x8500,0x7be0,0x5072,0x67f4,0x829d,0x5c61,
+ 0x854a,0x7e1e,0x820e,0x5199,0x5c04,0x6368,0x8d66,0x659c,0x716e,0x793e,
+ 0x7d17,0x8005,0x8b1d,0x8eca,0x906e,0x86c7,0x90aa,0x501f,0x52fa,0x5c3a,
+ 0x6753,0x707c,0x7235,0x914c,0x91c8,0x932b,0x82e5,0x5bc2,0x5f31,0x60f9,
+ 0x4e3b,0x53d6,0x5b88,0x624b,0x6731,0x6b8a,0x72e9,0x73e0,0x7a2e,0x816b,
+ 0x8da3,0x9152,0x9996,0x5112,0x53d7,0x546a,0x5bff,0x6388,0x6a39,0x7dac,
+ 0x9700,0x56da,0x53ce,0x5468
+ },
+ { /* ku 1d */
+ 0x5b97,0x5c31,0x5dde,0x4fee,0x6101,0x62fe,0x6d32,0x79c0,0x79cb,0x7d42,
+ 0x7e4d,0x7fd2,0x81ed,0x821f,0x8490,0x8846,0x8972,0x8b90,0x8e74,0x8f2f,
+ 0x9031,0x914b,0x916c,0x96c6,0x919c,0x4ec0,0x4f4f,0x5145,0x5341,0x5f93,
+ 0x620e,0x67d4,0x6c41,0x6e0b,0x7363,0x7e26,0x91cd,0x9283,0x53d4,0x5919,
+ 0x5bbf,0x6dd1,0x795d,0x7e2e,0x7c9b,0x587e,0x719f,0x51fa,0x8853,0x8ff0,
+ 0x4fca,0x5cfb,0x6625,0x77ac,0x7ae3,0x821c,0x99ff,0x51c6,0x5faa,0x65ec,
+ 0x696f,0x6b89,0x6df3,0x6e96,0x6f64,0x76fe,0x7d14,0x5de1,0x9075,0x9187,
+ 0x9806,0x51e6,0x521d,0x6240,0x6691,0x66d9,0x6e1a,0x5eb6,0x7dd2,0x7f72,
+ 0x66f8,0x85af,0x85f7,0x8af8,0x52a9,0x53d9,0x5973,0x5e8f,0x5f90,0x6055,
+ 0x92e4,0x9664,0x50b7,0x511f
+ },
+ { /* ku 1e */
+ 0x52dd,0x5320,0x5347,0x53ec,0x54e8,0x5546,0x5531,0x5617,0x5968,0x59be,
+ 0x5a3c,0x5bb5,0x5c06,0x5c0f,0x5c11,0x5c1a,0x5e84,0x5e8a,0x5ee0,0x5f70,
+ 0x627f,0x6284,0x62db,0x638c,0x6377,0x6607,0x660c,0x662d,0x6676,0x677e,
+ 0x68a2,0x6a1f,0x6a35,0x6cbc,0x6d88,0x6e09,0x6e58,0x713c,0x7126,0x7167,
+ 0x75c7,0x7701,0x785d,0x7901,0x7965,0x79f0,0x7ae0,0x7b11,0x7ca7,0x7d39,
+ 0x8096,0x83d6,0x848b,0x8549,0x885d,0x88f3,0x8a1f,0x8a3c,0x8a54,0x8a73,
+ 0x8c61,0x8cde,0x91a4,0x9266,0x937e,0x9418,0x969c,0x9798,0x4e0a,0x4e08,
+ 0x4e1e,0x4e57,0x5197,0x5270,0x57ce,0x5834,0x58cc,0x5b22,0x5e38,0x60c5,
+ 0x64fe,0x6761,0x6756,0x6d44,0x72b6,0x7573,0x7a63,0x84b8,0x8b72,0x91b8,
+ 0x9320,0x5631,0x57f4,0x98fe
+ },
+ { /* ku 1f */
+ 0x62ed,0x690d,0x6b96,0x71ed,0x7e54,0x8077,0x8272,0x89e6,0x98df,0x8755,
+ 0x8fb1,0x5c3b,0x4f38,0x4fe1,0x4fb5,0x5507,0x5a20,0x5bdd,0x5be9,0x5fc3,
+ 0x614e,0x632f,0x65b0,0x664b,0x68ee,0x699b,0x6d78,0x6df1,0x7533,0x75b9,
+ 0x771f,0x795e,0x79e6,0x7d33,0x81e3,0x82af,0x85aa,0x89aa,0x8a3a,0x8eab,
+ 0x8f9b,0x9032,0x91dd,0x9707,0x4eba,0x4ec1,0x5203,0x5875,0x58ec,0x5c0b,
+ 0x751a,0x5c3d,0x814e,0x8a0a,0x8fc5,0x9663,0x976d,0x7b25,0x8acf,0x9808,
+ 0x9162,0x56f3,0x53a8,0x9017,0x5439,0x5782,0x5e25,0x63a8,0x6c34,0x708a,
+ 0x7761,0x7c8b,0x7fe0,0x8870,0x9042,0x9154,0x9310,0x9318,0x968f,0x745e,
+ 0x9ac4,0x5d07,0x5d69,0x6570,0x67a2,0x8da8,0x96db,0x636e,0x6749,0x6919,
+ 0x83c5,0x9817,0x96c0,0x88fe
+ },
+ { /* ku 20 */
+ 0x6f84,0x647a,0x5bf8,0x4e16,0x702c,0x755d,0x662f,0x51c4,0x5236,0x52e2,
+ 0x59d3,0x5f81,0x6027,0x6210,0x653f,0x6574,0x661f,0x6674,0x68f2,0x6816,
+ 0x6b63,0x6e05,0x7272,0x751f,0x76db,0x7cbe,0x8056,0x58f0,0x88fd,0x897f,
+ 0x8aa0,0x8a93,0x8acb,0x901d,0x9192,0x9752,0x9759,0x6589,0x7a0e,0x8106,
+ 0x96bb,0x5e2d,0x60dc,0x621a,0x65a5,0x6614,0x6790,0x77f3,0x7a4d,0x7c4d,
+ 0x7e3e,0x810a,0x8cac,0x8d64,0x8de1,0x8e5f,0x78a9,0x5207,0x62d9,0x63a5,
+ 0x6442,0x6298,0x8a2d,0x7a83,0x7bc0,0x8aac,0x96ea,0x7d76,0x820c,0x8749,
+ 0x4ed9,0x5148,0x5343,0x5360,0x5ba3,0x5c02,0x5c16,0x5ddd,0x6226,0x6247,
+ 0x64b0,0x6813,0x6834,0x6cc9,0x6d45,0x6d17,0x67d3,0x6f5c,0x714e,0x717d,
+ 0x65cb,0x7a7f,0x7bad,0x7dda
+ },
+ { /* ku 21 */
+ 0x7e4a,0x7fa8,0x817a,0x821b,0x8239,0x85a6,0x8a6e,0x8cce,0x8df5,0x9078,
+ 0x9077,0x92ad,0x9291,0x9583,0x9bae,0x524d,0x5584,0x6f38,0x7136,0x5168,
+ 0x7985,0x7e55,0x81b3,0x7cce,0x564c,0x5851,0x5ca8,0x63aa,0x66fe,0x66fd,
+ 0x695a,0x72d9,0x758f,0x758e,0x790e,0x7956,0x79df,0x7c97,0x7d20,0x7d44,
+ 0x8607,0x8a34,0x963b,0x9061,0x9f20,0x50e7,0x5275,0x53cc,0x53e2,0x5009,
+ 0x55aa,0x58ee,0x594f,0x723d,0x5b8b,0x5c64,0x531d,0x60e3,0x60f3,0x635c,
+ 0x6383,0x633f,0x63bb,0x64cd,0x65e9,0x66f9,0x5de3,0x69cd,0x69fd,0x6f15,
+ 0x71e5,0x4e89,0x75e9,0x76f8,0x7a93,0x7cdf,0x7dcf,0x7d9c,0x8061,0x8349,
+ 0x8358,0x846c,0x84bc,0x85fb,0x88c5,0x8d70,0x9001,0x906d,0x9397,0x971c,
+ 0x9a12,0x50cf,0x5897,0x618e
+ },
+ { /* ku 22 */
+ 0x81d3,0x8535,0x8d08,0x9020,0x4fc3,0x5074,0x5247,0x5373,0x606f,0x6349,
+ 0x675f,0x6e2c,0x8db3,0x901f,0x4fd7,0x5c5e,0x8cca,0x65cf,0x7d9a,0x5352,
+ 0x8896,0x5176,0x63c3,0x5b58,0x5b6b,0x5c0a,0x640d,0x6751,0x905c,0x4ed6,
+ 0x591a,0x592a,0x6c70,0x8a51,0x553e,0x5815,0x59a5,0x60f0,0x6253,0x67c1,
+ 0x8235,0x6955,0x9640,0x99c4,0x9a28,0x4f53,0x5806,0x5bfe,0x8010,0x5cb1,
+ 0x5e2f,0x5f85,0x6020,0x614b,0x6234,0x66ff,0x6cf0,0x6ede,0x80ce,0x817f,
+ 0x82d4,0x888b,0x8cb8,0x9000,0x902e,0x968a,0x9edb,0x9bdb,0x4ee3,0x53f0,
+ 0x5927,0x7b2c,0x918d,0x984c,0x9df9,0x6edd,0x7027,0x5353,0x5544,0x5b85,
+ 0x6258,0x629e,0x62d3,0x6ca2,0x6fef,0x7422,0x8a17,0x9438,0x6fc1,0x8afe,
+ 0x8338,0x51e7,0x86f8,0x53ea
+ },
+ { /* ku 23 */
+ 0x53e9,0x4f46,0x9054,0x8fb0,0x596a,0x8131,0x5dfd,0x7aea,0x8fbf,0x68da,
+ 0x8c37,0x72f8,0x9c48,0x6a3d,0x8ab0,0x4e39,0x5358,0x5606,0x5766,0x62c5,
+ 0x63a2,0x65e6,0x6b4e,0x6de1,0x6e5b,0x70ad,0x77ed,0x7aef,0x7baa,0x7dbb,
+ 0x803d,0x80c6,0x86cb,0x8a95,0x935b,0x56e3,0x58c7,0x5f3e,0x65ad,0x6696,
+ 0x6a80,0x6bb5,0x7537,0x8ac7,0x5024,0x77e5,0x5730,0x5f1b,0x6065,0x667a,
+ 0x6c60,0x75f4,0x7a1a,0x7f6e,0x81f4,0x8718,0x9045,0x99b3,0x7bc9,0x755c,
+ 0x7af9,0x7b51,0x84c4,0x9010,0x79e9,0x7a92,0x8336,0x5ae1,0x7740,0x4e2d,
+ 0x4ef2,0x5b99,0x5fe0,0x62bd,0x663c,0x67f1,0x6ce8,0x866b,0x8877,0x8a3b,
+ 0x914e,0x92f3,0x99d0,0x6a17,0x7026,0x732a,0x82e7,0x8457,0x8caf,0x4e01,
+ 0x5146,0x51cb,0x558b,0x5bf5
+ },
+ { /* ku 24 */
+ 0x5e16,0x5e33,0x5e81,0x5f14,0x5f35,0x5f6b,0x5fb4,0x61f2,0x6311,0x66a2,
+ 0x671d,0x6f6e,0x7252,0x753a,0x773a,0x8074,0x8139,0x8178,0x8776,0x8abf,
+ 0x8adc,0x8d85,0x8df3,0x929a,0x9577,0x9802,0x9ce5,0x52c5,0x6357,0x76f4,
+ 0x6715,0x6c88,0x73cd,0x8cc3,0x93ae,0x9673,0x6d25,0x589c,0x690e,0x69cc,
+ 0x8ffd,0x939a,0x75db,0x901a,0x585a,0x6802,0x63b4,0x69fb,0x4f43,0x6f2c,
+ 0x67d8,0x8fbb,0x8526,0x7db4,0x9354,0x693f,0x6f70,0x576a,0x58f7,0x5b2c,
+ 0x7d2c,0x722a,0x540a,0x91e3,0x9db4,0x4ead,0x4f4e,0x505c,0x5075,0x5243,
+ 0x8c9e,0x5448,0x5824,0x5b9a,0x5e1d,0x5e95,0x5ead,0x5ef7,0x5f1f,0x608c,
+ 0x62b5,0x633a,0x63d0,0x68af,0x6c40,0x7887,0x798e,0x7a0b,0x7de0,0x8247,
+ 0x8a02,0x8ae6,0x8e44,0x9013
+ },
+ { /* ku 25 */
+ 0x90b8,0x912d,0x91d8,0x9f0e,0x6ce5,0x6458,0x64e2,0x6575,0x6ef4,0x7684,
+ 0x7b1b,0x9069,0x93d1,0x6eba,0x54f2,0x5fb9,0x64a4,0x8f4d,0x8fed,0x9244,
+ 0x5178,0x586b,0x5929,0x5c55,0x5e97,0x6dfb,0x7e8f,0x751c,0x8cbc,0x8ee2,
+ 0x985b,0x70b9,0x4f1d,0x6bbf,0x6fb1,0x7530,0x96fb,0x514e,0x5410,0x5835,
+ 0x5857,0x59ac,0x5c60,0x5f92,0x6597,0x675c,0x6e21,0x767b,0x83df,0x8ced,
+ 0x9014,0x90fd,0x934d,0x7825,0x783a,0x52aa,0x5ea6,0x571f,0x5974,0x6012,
+ 0x5012,0x515a,0x51ac,0x51cd,0x5200,0x5510,0x5854,0x5858,0x5957,0x5b95,
+ 0x5cf6,0x5d8b,0x60bc,0x6295,0x642d,0x6771,0x6843,0x68bc,0x68df,0x76d7,
+ 0x6dd8,0x6e6f,0x6d9b,0x706f,0x71c8,0x5f53,0x75d8,0x7977,0x7b49,0x7b54,
+ 0x7b52,0x7cd6,0x7d71,0x5230
+ },
+ { /* ku 26 */
+ 0x8463,0x8569,0x85e4,0x8a0e,0x8b04,0x8c46,0x8e0f,0x9003,0x900f,0x9419,
+ 0x9676,0x982d,0x9a30,0x95d8,0x50cd,0x52d5,0x540c,0x5802,0x5c0e,0x61a7,
+ 0x649e,0x6d1e,0x77b3,0x7ae5,0x80f4,0x8404,0x9053,0x9285,0x5ce0,0x9d07,
+ 0x533f,0x5f97,0x5fb3,0x6d9c,0x7279,0x7763,0x79bf,0x7be4,0x6bd2,0x72ec,
+ 0x8aad,0x6803,0x6a61,0x51f8,0x7a81,0x6934,0x5c4a,0x9cf6,0x82eb,0x5bc5,
+ 0x9149,0x701e,0x5678,0x5c6f,0x60c7,0x6566,0x6c8c,0x8c5a,0x9041,0x9813,
+ 0x5451,0x66c7,0x920d,0x5948,0x90a3,0x5185,0x4e4d,0x51ea,0x8599,0x8b0e,
+ 0x7058,0x637a,0x934b,0x6962,0x99b4,0x7e04,0x7577,0x5357,0x6960,0x8edf,
+ 0x96e3,0x6c5d,0x4e8c,0x5c3c,0x5f10,0x8fe9,0x5302,0x8cd1,0x8089,0x8679,
+ 0x5eff,0x65e5,0x4e73,0x5165
+ },
+ { /* ku 27 */
+ 0x5982,0x5c3f,0x97ee,0x4efb,0x598a,0x5fcd,0x8a8d,0x6fe1,0x79b0,0x7962,
+ 0x5be7,0x8471,0x732b,0x71b1,0x5e74,0x5ff5,0x637b,0x649a,0x71c3,0x7c98,
+ 0x4e43,0x5efc,0x4e4b,0x57dc,0x56a2,0x60a9,0x6fc3,0x7d0d,0x80fd,0x8133,
+ 0x81bf,0x8fb2,0x8997,0x86a4,0x5df4,0x628a,0x64ad,0x8987,0x6777,0x6ce2,
+ 0x6d3e,0x7436,0x7834,0x5a46,0x7f75,0x82ad,0x99ac,0x4ff3,0x5ec3,0x62dd,
+ 0x6392,0x6557,0x676f,0x76c3,0x724c,0x80cc,0x80ba,0x8f29,0x914d,0x500d,
+ 0x57f9,0x5a92,0x6885,0x6973,0x7164,0x72fd,0x8cb7,0x58f2,0x8ce0,0x966a,
+ 0x9019,0x877f,0x79e4,0x77e7,0x8429,0x4f2f,0x5265,0x535a,0x62cd,0x67cf,
+ 0x6cca,0x767d,0x7b94,0x7c95,0x8236,0x8584,0x8feb,0x66dd,0x6f20,0x7206,
+ 0x7e1b,0x83ab,0x99c1,0x9ea6
+ },
+ { /* ku 28 */
+ 0x51fd,0x7bb1,0x7872,0x7bb8,0x8087,0x7b48,0x6ae8,0x5e61,0x808c,0x7551,
+ 0x7560,0x516b,0x9262,0x6e8c,0x767a,0x9197,0x9aea,0x4f10,0x7f70,0x629c,
+ 0x7b4f,0x95a5,0x9ce9,0x567a,0x5859,0x86e4,0x96bc,0x4f34,0x5224,0x534a,
+ 0x53cd,0x53db,0x5e06,0x642c,0x6591,0x677f,0x6c3e,0x6c4e,0x7248,0x72af,
+ 0x73ed,0x7554,0x7e41,0x822c,0x85e9,0x8ca9,0x7bc4,0x91c6,0x7169,0x9812,
+ 0x98ef,0x633d,0x6669,0x756a,0x76e4,0x78d0,0x8543,0x86ee,0x532a,0x5351,
+ 0x5426,0x5983,0x5e87,0x5f7c,0x60b2,0x6249,0x6279,0x62ab,0x6590,0x6bd4,
+ 0x6ccc,0x75b2,0x76ae,0x7891,0x79d8,0x7dcb,0x7f77,0x80a5,0x88ab,0x8ab9,
+ 0x8cbb,0x907f,0x975e,0x98db,0x6a0b,0x7c38,0x5099,0x5c3e,0x5fae,0x6787,
+ 0x6bd8,0x7435,0x7709,0x7f8e
+ },
+ { /* ku 29 */
+ 0x9f3b,0x67ca,0x7a17,0x5339,0x758b,0x9aed,0x5f66,0x819d,0x83f1,0x8098,
+ 0x5f3c,0x5fc5,0x7562,0x7b46,0x903c,0x6867,0x59eb,0x5a9b,0x7d10,0x767e,
+ 0x8b2c,0x4ff5,0x5f6a,0x6a19,0x6c37,0x6f02,0x74e2,0x7968,0x8868,0x8a55,
+ 0x8c79,0x5edf,0x63cf,0x75c5,0x79d2,0x82d7,0x9328,0x92f2,0x849c,0x86ed,
+ 0x9c2d,0x54c1,0x5f6c,0x658c,0x6d5c,0x7015,0x8ca7,0x8cd3,0x983b,0x654f,
+ 0x74f6,0x4e0d,0x4ed8,0x57e0,0x592b,0x5a66,0x5bcc,0x51a8,0x5e03,0x5e9c,
+ 0x6016,0x6276,0x6577,0x65a7,0x666e,0x6d6e,0x7236,0x7b26,0x8150,0x819a,
+ 0x8299,0x8b5c,0x8ca0,0x8ce6,0x8d74,0x961c,0x9644,0x4fae,0x64ab,0x6b66,
+ 0x821e,0x8461,0x856a,0x90e8,0x5c01,0x6953,0x98a8,0x847a,0x8557,0x4f0f,
+ 0x526f,0x5fa9,0x5e45,0x670d
+ },
+ { /* ku 2a */
+ 0x798f,0x8179,0x8907,0x8986,0x6df5,0x5f17,0x6255,0x6cb8,0x4ecf,0x7269,
+ 0x9b92,0x5206,0x543b,0x5674,0x58b3,0x61a4,0x626e,0x711a,0x596e,0x7c89,
+ 0x7cde,0x7d1b,0x96f0,0x6587,0x805e,0x4e19,0x4f75,0x5175,0x5840,0x5e63,
+ 0x5e73,0x5f0a,0x67c4,0x4e26,0x853d,0x9589,0x965b,0x7c73,0x9801,0x50fb,
+ 0x58c1,0x7656,0x78a7,0x5225,0x77a5,0x8511,0x7b86,0x504f,0x5909,0x7247,
+ 0x7bc7,0x7de8,0x8fba,0x8fd4,0x904d,0x4fbf,0x52c9,0x5a29,0x5f01,0x97ad,
+ 0x4fdd,0x8217,0x92ea,0x5703,0x6355,0x6b69,0x752b,0x88dc,0x8f14,0x7a42,
+ 0x52df,0x5893,0x6155,0x620a,0x66ae,0x6bcd,0x7c3f,0x83e9,0x5023,0x4ff8,
+ 0x5305,0x5446,0x5831,0x5949,0x5b9d,0x5cf0,0x5cef,0x5d29,0x5e96,0x62b1,
+ 0x6367,0x653e,0x65b9,0x670b
+ },
+ { /* ku 2b */
+ 0x6cd5,0x6ce1,0x70f9,0x7832,0x7e2b,0x80de,0x82b3,0x840c,0x84ec,0x8702,
+ 0x8912,0x8a2a,0x8c4a,0x90a6,0x92d2,0x98fd,0x9cf3,0x9d6c,0x4e4f,0x4ea1,
+ 0x508d,0x5256,0x574a,0x59a8,0x5e3d,0x5fd8,0x5fd9,0x623f,0x66b4,0x671b,
+ 0x67d0,0x68d2,0x5192,0x7d21,0x80aa,0x81a8,0x8b00,0x8c8c,0x8cbf,0x927e,
+ 0x9632,0x5420,0x982c,0x5317,0x50d5,0x535c,0x58a8,0x64b2,0x6734,0x7267,
+ 0x7766,0x7a46,0x91e6,0x52c3,0x6ca1,0x6b86,0x5800,0x5e4c,0x5954,0x672c,
+ 0x7ffb,0x51e1,0x76c6,0x6469,0x78e8,0x9b54,0x9ebb,0x57cb,0x59b9,0x6627,
+ 0x679a,0x6bce,0x54e9,0x69d9,0x5e55,0x819c,0x6795,0x9baa,0x67fe,0x9c52,
+ 0x685d,0x4ea6,0x4fe3,0x53c8,0x62b9,0x672b,0x6cab,0x8fc4,0x4fad,0x7e6d,
+ 0x9ebf,0x4e07,0x6162,0x6e80
+ },
+ { /* ku 2c */
+ 0x6f2b,0x8513,0x5473,0x672a,0x9b45,0x5df3,0x7b95,0x5cac,0x5bc6,0x871c,
+ 0x6e4a,0x84d1,0x7a14,0x8108,0x5999,0x7c8d,0x6c11,0x7720,0x52d9,0x5922,
+ 0x7121,0x725f,0x77db,0x9727,0x9d61,0x690b,0x5a7f,0x5a18,0x51a5,0x540d,
+ 0x547d,0x660e,0x76df,0x8ff7,0x9298,0x9cf4,0x59ea,0x725d,0x6ec5,0x514d,
+ 0x68c9,0x7dbf,0x7dec,0x9762,0x9eba,0x6478,0x6a21,0x8302,0x5984,0x5b5f,
+ 0x6bdb,0x731b,0x76f2,0x7db2,0x8017,0x8499,0x5132,0x6728,0x9ed9,0x76ee,
+ 0x6762,0x52ff,0x9905,0x5c24,0x623b,0x7c7e,0x8cb0,0x554f,0x60b6,0x7d0b,
+ 0x9580,0x5301,0x4e5f,0x51b6,0x591c,0x723a,0x8036,0x91ce,0x5f25,0x77e2,
+ 0x5384,0x5f79,0x7d04,0x85ac,0x8a33,0x8e8d,0x9756,0x67f3,0x85ae,0x9453,
+ 0x6109,0x6108,0x6cb9,0x7652
+ },
+ { /* ku 2d */
+ 0x8aed,0x8f38,0x552f,0x4f51,0x512a,0x52c7,0x53cb,0x5ba5,0x5e7d,0x60a0,
+ 0x6182,0x63d6,0x6709,0x67da,0x6e67,0x6d8c,0x7336,0x7337,0x7531,0x7950,
+ 0x88d5,0x8a98,0x904a,0x9091,0x90f5,0x96c4,0x878d,0x5915,0x4e88,0x4f59,
+ 0x4e0e,0x8a89,0x8f3f,0x9810,0x50ad,0x5e7c,0x5996,0x5bb9,0x5eb8,0x63da,
+ 0x63fa,0x64c1,0x66dc,0x694a,0x69d8,0x6d0b,0x6eb6,0x7194,0x7528,0x7aaf,
+ 0x7f8a,0x8000,0x8449,0x84c9,0x8981,0x8b21,0x8e0a,0x9065,0x967d,0x990a,
+ 0x617e,0x6291,0x6b32,0x6c83,0x6d74,0x7fcc,0x7ffc,0x6dc0,0x7f85,0x87ba,
+ 0x88f8,0x6765,0x83b1,0x983c,0x96f7,0x6d1b,0x7d61,0x843d,0x916a,0x4e71,
+ 0x5375,0x5d50,0x6b04,0x6feb,0x85cd,0x862d,0x89a7,0x5229,0x540f,0x5c65,
+ 0x674e,0x68a8,0x7406,0x7483
+ },
+ { /* ku 2e */
+ 0x75e2,0x88cf,0x88e1,0x91cc,0x96e2,0x9678,0x5f8b,0x7387,0x7acb,0x844e,
+ 0x63a0,0x7565,0x5289,0x6d41,0x6e9c,0x7409,0x7559,0x786b,0x7c92,0x9686,
+ 0x7adc,0x9f8d,0x4fb6,0x616e,0x65c5,0x865c,0x4e86,0x4eae,0x50da,0x4e21,
+ 0x51cc,0x5bee,0x6599,0x6881,0x6dbc,0x731f,0x7642,0x77ad,0x7a1c,0x7ce7,
+ 0x826f,0x8ad2,0x907c,0x91cf,0x9675,0x9818,0x529b,0x7dd1,0x502b,0x5398,
+ 0x6797,0x6dcb,0x71d0,0x7433,0x81e8,0x8f2a,0x96a3,0x9c57,0x9e9f,0x7460,
+ 0x5841,0x6d99,0x7d2f,0x985e,0x4ee4,0x4f36,0x4f8b,0x51b7,0x52b1,0x5dba,
+ 0x601c,0x73b2,0x793c,0x82d3,0x9234,0x96b7,0x96f6,0x970a,0x9e97,0x9f62,
+ 0x66a6,0x6b74,0x5217,0x52a3,0x70c8,0x88c2,0x5ec9,0x604b,0x6190,0x6f23,
+ 0x7149,0x7c3e,0x7df4,0x806f
+ },
+ { /* ku 2f */
+ 0x84ee,0x9023,0x932c,0x5442,0x9b6f,0x6ad3,0x7089,0x8cc2,0x8def,0x9732,
+ 0x52b4,0x5a41,0x5eca,0x5f04,0x6717,0x697c,0x6994,0x6d6a,0x6f0f,0x7262,
+ 0x72fc,0x7bed,0x8001,0x807e,0x874b,0x90ce,0x516d,0x9e93,0x7984,0x808b,
+ 0x9332,0x8ad6,0x502d,0x548c,0x8a71,0x6b6a,0x8cc4,0x8107,0x60d1,0x67a0,
+ 0x9df2,0x4e99,0x4e98,0x9c10,0x8a6b,0x85c1,0x8568,0x6900,0x6e7e,0x7897,
+ 0x8155,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0x5f0c,0x4e10,0x4e15,0x4e2a,0x4e31,0x4e36,0x4e3c,0x4e3f,0x4e42,0x4e56,
+ 0x4e58,0x4e82,0x4e85,0x8c6b,0x4e8a,0x8212,0x5f0d,0x4e8e,0x4e9e,0x4e9f,
+ 0x4ea0,0x4ea2,0x4eb0,0x4eb3,0x4eb6,0x4ece,0x4ecd,0x4ec4,0x4ec6,0x4ec2,
+ 0x4ed7,0x4ede,0x4eed,0x4edf,0x4ef7,0x4f09,0x4f5a,0x4f30,0x4f5b,0x4f5d,
+ 0x4f57,0x4f47,0x4f76,0x4f88,0x4f8f,0x4f98,0x4f7b,0x4f69,0x4f70,0x4f91,
+ 0x4f6f,0x4f86,0x4f96,0x5118,0x4fd4,0x4fdf,0x4fce,0x4fd8,0x4fdb,0x4fd1,
+ 0x4fda,0x4fd0,0x4fe4,0x4fe5,0x501a,0x5028,0x5014,0x502a,0x5025,0x5005,
+ 0x4f1c,0x4ff6,0x5021,0x5029,0x502c,0x4ffe,0x4fef,0x5011,0x5006,0x5043,
+ 0x5047,0x6703,0x5055,0x5050,0x5048,0x505a,0x5056,0x506c,0x5078,0x5080,
+ 0x509a,0x5085,0x50b4,0x50b2
+ },
+ { /* ku 31 */
+ 0x50c9,0x50ca,0x50b3,0x50c2,0x50d6,0x50de,0x50e5,0x50ed,0x50e3,0x50ee,
+ 0x50f9,0x50f5,0x5109,0x5101,0x5102,0x5116,0x5115,0x5114,0x511a,0x5121,
+ 0x513a,0x5137,0x513c,0x513b,0x513f,0x5140,0x5152,0x514c,0x5154,0x5162,
+ 0x7af8,0x5169,0x516a,0x516e,0x5180,0x5182,0x56d8,0x518c,0x5189,0x518f,
+ 0x5191,0x5193,0x5195,0x5196,0x51a4,0x51a6,0x51a2,0x51a9,0x51aa,0x51ab,
+ 0x51b3,0x51b1,0x51b2,0x51b0,0x51b5,0x51bd,0x51c5,0x51c9,0x51db,0x51e0,
+ 0x8655,0x51e9,0x51ed,0x51f0,0x51f5,0x51fe,0x5204,0x520b,0x5214,0x520e,
+ 0x5227,0x522a,0x522e,0x5233,0x5239,0x524f,0x5244,0x524b,0x524c,0x525e,
+ 0x5254,0x526a,0x5274,0x5269,0x5273,0x527f,0x527d,0x528d,0x5294,0x5292,
+ 0x5271,0x5288,0x5291,0x8fa8
+ },
+ { /* ku 32 */
+ 0x8fa7,0x52ac,0x52ad,0x52bc,0x52b5,0x52c1,0x52cd,0x52d7,0x52de,0x52e3,
+ 0x52e6,0x98ed,0x52e0,0x52f3,0x52f5,0x52f8,0x52f9,0x5306,0x5308,0x7538,
+ 0x530d,0x5310,0x530f,0x5315,0x531a,0x5323,0x532f,0x5331,0x5333,0x5338,
+ 0x5340,0x5346,0x5345,0x4e17,0x5349,0x534d,0x51d6,0x535e,0x5369,0x536e,
+ 0x5918,0x537b,0x5377,0x5382,0x5396,0x53a0,0x53a6,0x53a5,0x53ae,0x53b0,
+ 0x53b6,0x53c3,0x7c12,0x96d9,0x53df,0x66fc,0x71ee,0x53ee,0x53e8,0x53ed,
+ 0x53fa,0x5401,0x543d,0x5440,0x542c,0x542d,0x543c,0x542e,0x5436,0x5429,
+ 0x541d,0x544e,0x548f,0x5475,0x548e,0x545f,0x5471,0x5477,0x5470,0x5492,
+ 0x547b,0x5480,0x5476,0x5484,0x5490,0x5486,0x54c7,0x54a2,0x54b8,0x54a5,
+ 0x54ac,0x54c4,0x54c8,0x54a8
+ },
+ { /* ku 33 */
+ 0x54ab,0x54c2,0x54a4,0x54be,0x54bc,0x54d8,0x54e5,0x54e6,0x550f,0x5514,
+ 0x54fd,0x54ee,0x54ed,0x54fa,0x54e2,0x5539,0x5540,0x5563,0x554c,0x552e,
+ 0x555c,0x5545,0x5556,0x5557,0x5538,0x5533,0x555d,0x5599,0x5580,0x54af,
+ 0x558a,0x559f,0x557b,0x557e,0x5598,0x559e,0x55ae,0x557c,0x5583,0x55a9,
+ 0x5587,0x55a8,0x55da,0x55c5,0x55df,0x55c4,0x55dc,0x55e4,0x55d4,0x5614,
+ 0x55f7,0x5616,0x55fe,0x55fd,0x561b,0x55f9,0x564e,0x5650,0x71df,0x5634,
+ 0x5636,0x5632,0x5638,0x566b,0x5664,0x562f,0x566c,0x566a,0x5686,0x5680,
+ 0x568a,0x56a0,0x5694,0x568f,0x56a5,0x56ae,0x56b6,0x56b4,0x56c2,0x56bc,
+ 0x56c1,0x56c3,0x56c0,0x56c8,0x56ce,0x56d1,0x56d3,0x56d7,0x56ee,0x56f9,
+ 0x5700,0x56ff,0x5704,0x5709
+ },
+ { /* ku 34 */
+ 0x5708,0x570b,0x570d,0x5713,0x5718,0x5716,0x55c7,0x571c,0x5726,0x5737,
+ 0x5738,0x574e,0x573b,0x5740,0x574f,0x5769,0x57c0,0x5788,0x5761,0x577f,
+ 0x5789,0x5793,0x57a0,0x57b3,0x57a4,0x57aa,0x57b0,0x57c3,0x57c6,0x57d4,
+ 0x57d2,0x57d3,0x580a,0x57d6,0x57e3,0x580b,0x5819,0x581d,0x5872,0x5821,
+ 0x5862,0x584b,0x5870,0x6bc0,0x5852,0x583d,0x5879,0x5885,0x58b9,0x589f,
+ 0x58ab,0x58ba,0x58de,0x58bb,0x58b8,0x58ae,0x58c5,0x58d3,0x58d1,0x58d7,
+ 0x58d9,0x58d8,0x58e5,0x58dc,0x58e4,0x58df,0x58ef,0x58fa,0x58f9,0x58fb,
+ 0x58fc,0x58fd,0x5902,0x590a,0x5910,0x591b,0x68a6,0x5925,0x592c,0x592d,
+ 0x5932,0x5938,0x593e,0x7ad2,0x5955,0x5950,0x594e,0x595a,0x5958,0x5962,
+ 0x5960,0x5967,0x596c,0x5969
+ },
+ { /* ku 35 */
+ 0x5978,0x5981,0x599d,0x4f5e,0x4fab,0x59a3,0x59b2,0x59c6,0x59e8,0x59dc,
+ 0x598d,0x59d9,0x59da,0x5a25,0x5a1f,0x5a11,0x5a1c,0x5a09,0x5a1a,0x5a40,
+ 0x5a6c,0x5a49,0x5a35,0x5a36,0x5a62,0x5a6a,0x5a9a,0x5abc,0x5abe,0x5acb,
+ 0x5ac2,0x5abd,0x5ae3,0x5ad7,0x5ae6,0x5ae9,0x5ad6,0x5afa,0x5afb,0x5b0c,
+ 0x5b0b,0x5b16,0x5b32,0x5ad0,0x5b2a,0x5b36,0x5b3e,0x5b43,0x5b45,0x5b40,
+ 0x5b51,0x5b55,0x5b5a,0x5b5b,0x5b65,0x5b69,0x5b70,0x5b73,0x5b75,0x5b78,
+ 0x6588,0x5b7a,0x5b80,0x5b83,0x5ba6,0x5bb8,0x5bc3,0x5bc7,0x5bc9,0x5bd4,
+ 0x5bd0,0x5be4,0x5be6,0x5be2,0x5bde,0x5be5,0x5beb,0x5bf0,0x5bf6,0x5bf3,
+ 0x5c05,0x5c07,0x5c08,0x5c0d,0x5c13,0x5c20,0x5c22,0x5c28,0x5c38,0x5c39,
+ 0x5c41,0x5c46,0x5c4e,0x5c53
+ },
+ { /* ku 36 */
+ 0x5c50,0x5c4f,0x5b71,0x5c6c,0x5c6e,0x4e62,0x5c76,0x5c79,0x5c8c,0x5c91,
+ 0x5c94,0x599b,0x5cab,0x5cbb,0x5cb6,0x5cbc,0x5cb7,0x5cc5,0x5cbe,0x5cc7,
+ 0x5cd9,0x5ce9,0x5cfd,0x5cfa,0x5ced,0x5d8c,0x5cea,0x5d0b,0x5d15,0x5d17,
+ 0x5d5c,0x5d1f,0x5d1b,0x5d11,0x5d14,0x5d22,0x5d1a,0x5d19,0x5d18,0x5d4c,
+ 0x5d52,0x5d4e,0x5d4b,0x5d6c,0x5d73,0x5d76,0x5d87,0x5d84,0x5d82,0x5da2,
+ 0x5d9d,0x5dac,0x5dae,0x5dbd,0x5d90,0x5db7,0x5dbc,0x5dc9,0x5dcd,0x5dd3,
+ 0x5dd2,0x5dd6,0x5ddb,0x5deb,0x5df2,0x5df5,0x5e0b,0x5e1a,0x5e19,0x5e11,
+ 0x5e1b,0x5e36,0x5e37,0x5e44,0x5e43,0x5e40,0x5e4e,0x5e57,0x5e54,0x5e5f,
+ 0x5e62,0x5e64,0x5e47,0x5e75,0x5e76,0x5e7a,0x9ebc,0x5e7f,0x5ea0,0x5ec1,
+ 0x5ec2,0x5ec8,0x5ed0,0x5ecf
+ },
+ { /* ku 37 */
+ 0x5ed6,0x5ee3,0x5edd,0x5eda,0x5edb,0x5ee2,0x5ee1,0x5ee8,0x5ee9,0x5eec,
+ 0x5ef1,0x5ef3,0x5ef0,0x5ef4,0x5ef8,0x5efe,0x5f03,0x5f09,0x5f5d,0x5f5c,
+ 0x5f0b,0x5f11,0x5f16,0x5f29,0x5f2d,0x5f38,0x5f41,0x5f48,0x5f4c,0x5f4e,
+ 0x5f2f,0x5f51,0x5f56,0x5f57,0x5f59,0x5f61,0x5f6d,0x5f73,0x5f77,0x5f83,
+ 0x5f82,0x5f7f,0x5f8a,0x5f88,0x5f91,0x5f87,0x5f9e,0x5f99,0x5f98,0x5fa0,
+ 0x5fa8,0x5fad,0x5fbc,0x5fd6,0x5ffb,0x5fe4,0x5ff8,0x5ff1,0x5fdd,0x60b3,
+ 0x5fff,0x6021,0x6060,0x6019,0x6010,0x6029,0x600e,0x6031,0x601b,0x6015,
+ 0x602b,0x6026,0x600f,0x603a,0x605a,0x6041,0x606a,0x6077,0x605f,0x604a,
+ 0x6046,0x604d,0x6063,0x6043,0x6064,0x6042,0x606c,0x606b,0x6059,0x6081,
+ 0x608d,0x60e7,0x6083,0x609a
+ },
+ { /* ku 38 */
+ 0x6084,0x609b,0x6096,0x6097,0x6092,0x60a7,0x608b,0x60e1,0x60b8,0x60e0,
+ 0x60d3,0x60b4,0x5ff0,0x60bd,0x60c6,0x60b5,0x60d8,0x614d,0x6115,0x6106,
+ 0x60f6,0x60f7,0x6100,0x60f4,0x60fa,0x6103,0x6121,0x60fb,0x60f1,0x610d,
+ 0x610e,0x6147,0x613e,0x6128,0x6127,0x614a,0x613f,0x613c,0x612c,0x6134,
+ 0x613d,0x6142,0x6144,0x6173,0x6177,0x6158,0x6159,0x615a,0x616b,0x6174,
+ 0x616f,0x6165,0x6171,0x615f,0x615d,0x6153,0x6175,0x6199,0x6196,0x6187,
+ 0x61ac,0x6194,0x619a,0x618a,0x6191,0x61ab,0x61ae,0x61cc,0x61ca,0x61c9,
+ 0x61f7,0x61c8,0x61c3,0x61c6,0x61ba,0x61cb,0x7f79,0x61cd,0x61e6,0x61e3,
+ 0x61f6,0x61fa,0x61f4,0x61ff,0x61fd,0x61fc,0x61fe,0x6200,0x6208,0x6209,
+ 0x620d,0x620c,0x6214,0x621b
+ },
+ { /* ku 39 */
+ 0x621e,0x6221,0x622a,0x622e,0x6230,0x6232,0x6233,0x6241,0x624e,0x625e,
+ 0x6263,0x625b,0x6260,0x6268,0x627c,0x6282,0x6289,0x627e,0x6292,0x6293,
+ 0x6296,0x62d4,0x6283,0x6294,0x62d7,0x62d1,0x62bb,0x62cf,0x62ff,0x62c6,
+ 0x64d4,0x62c8,0x62dc,0x62cc,0x62ca,0x62c2,0x62c7,0x629b,0x62c9,0x630c,
+ 0x62ee,0x62f1,0x6327,0x6302,0x6308,0x62ef,0x62f5,0x6350,0x633e,0x634d,
+ 0x641c,0x634f,0x6396,0x638e,0x6380,0x63ab,0x6376,0x63a3,0x638f,0x6389,
+ 0x639f,0x63b5,0x636b,0x6369,0x63be,0x63e9,0x63c0,0x63c6,0x63e3,0x63c9,
+ 0x63d2,0x63f6,0x63c4,0x6416,0x6434,0x6406,0x6413,0x6426,0x6436,0x651d,
+ 0x6417,0x6428,0x640f,0x6467,0x646f,0x6476,0x644e,0x652a,0x6495,0x6493,
+ 0x64a5,0x64a9,0x6488,0x64bc
+ },
+ { /* ku 3a */
+ 0x64da,0x64d2,0x64c5,0x64c7,0x64bb,0x64d8,0x64c2,0x64f1,0x64e7,0x8209,
+ 0x64e0,0x64e1,0x62ac,0x64e3,0x64ef,0x652c,0x64f6,0x64f4,0x64f2,0x64fa,
+ 0x6500,0x64fd,0x6518,0x651c,0x6505,0x6524,0x6523,0x652b,0x6534,0x6535,
+ 0x6537,0x6536,0x6538,0x754b,0x6548,0x6556,0x6555,0x654d,0x6558,0x655e,
+ 0x655d,0x6572,0x6578,0x6582,0x6583,0x8b8a,0x659b,0x659f,0x65ab,0x65b7,
+ 0x65c3,0x65c6,0x65c1,0x65c4,0x65cc,0x65d2,0x65db,0x65d9,0x65e0,0x65e1,
+ 0x65f1,0x6772,0x660a,0x6603,0x65fb,0x6773,0x6635,0x6636,0x6634,0x661c,
+ 0x664f,0x6644,0x6649,0x6641,0x665e,0x665d,0x6664,0x6667,0x6668,0x665f,
+ 0x6662,0x6670,0x6683,0x6688,0x668e,0x6689,0x6684,0x6698,0x669d,0x66c1,
+ 0x66b9,0x66c9,0x66be,0x66bc
+ },
+ { /* ku 3b */
+ 0x66c4,0x66b8,0x66d6,0x66da,0x66e0,0x663f,0x66e6,0x66e9,0x66f0,0x66f5,
+ 0x66f7,0x670f,0x6716,0x671e,0x6726,0x6727,0x9738,0x672e,0x673f,0x6736,
+ 0x6741,0x6738,0x6737,0x6746,0x675e,0x6760,0x6759,0x6763,0x6764,0x6789,
+ 0x6770,0x67a9,0x677c,0x676a,0x678c,0x678b,0x67a6,0x67a1,0x6785,0x67b7,
+ 0x67ef,0x67b4,0x67ec,0x67b3,0x67e9,0x67b8,0x67e4,0x67de,0x67dd,0x67e2,
+ 0x67ee,0x67b9,0x67ce,0x67c6,0x67e7,0x6a9c,0x681e,0x6846,0x6829,0x6840,
+ 0x684d,0x6832,0x684e,0x68b3,0x682b,0x6859,0x6863,0x6877,0x687f,0x689f,
+ 0x688f,0x68ad,0x6894,0x689d,0x689b,0x6883,0x6aae,0x68b9,0x6874,0x68b5,
+ 0x68a0,0x68ba,0x690f,0x688d,0x687e,0x6901,0x68ca,0x6908,0x68d8,0x6922,
+ 0x6926,0x68e1,0x690c,0x68cd
+ },
+ { /* ku 3c */
+ 0x68d4,0x68e7,0x68d5,0x6936,0x6912,0x6904,0x68d7,0x68e3,0x6925,0x68f9,
+ 0x68e0,0x68ef,0x6928,0x692a,0x691a,0x6923,0x6921,0x68c6,0x6979,0x6977,
+ 0x695c,0x6978,0x696b,0x6954,0x697e,0x696e,0x6939,0x6974,0x693d,0x6959,
+ 0x6930,0x6961,0x695e,0x695d,0x6981,0x696a,0x69b2,0x69ae,0x69d0,0x69bf,
+ 0x69c1,0x69d3,0x69be,0x69ce,0x5be8,0x69ca,0x69dd,0x69bb,0x69c3,0x69a7,
+ 0x6a2e,0x6991,0x69a0,0x699c,0x6995,0x69b4,0x69de,0x69e8,0x6a02,0x6a1b,
+ 0x69ff,0x6b0a,0x69f9,0x69f2,0x69e7,0x6a05,0x69b1,0x6a1e,0x69ed,0x6a14,
+ 0x69eb,0x6a0a,0x6a12,0x6ac1,0x6a23,0x6a13,0x6a44,0x6a0c,0x6a72,0x6a36,
+ 0x6a78,0x6a47,0x6a62,0x6a59,0x6a66,0x6a48,0x6a38,0x6a22,0x6a90,0x6a8d,
+ 0x6aa0,0x6a84,0x6aa2,0x6aa3
+ },
+ { /* ku 3d */
+ 0x6a97,0x8617,0x6abb,0x6ac3,0x6ac2,0x6ab8,0x6ab3,0x6aac,0x6ade,0x6ad1,
+ 0x6adf,0x6aaa,0x6ada,0x6aea,0x6afb,0x6b05,0x8616,0x6afa,0x6b12,0x6b16,
+ 0x9b31,0x6b1f,0x6b38,0x6b37,0x76dc,0x6b39,0x98ee,0x6b47,0x6b43,0x6b49,
+ 0x6b50,0x6b59,0x6b54,0x6b5b,0x6b5f,0x6b61,0x6b78,0x6b79,0x6b7f,0x6b80,
+ 0x6b84,0x6b83,0x6b8d,0x6b98,0x6b95,0x6b9e,0x6ba4,0x6baa,0x6bab,0x6baf,
+ 0x6bb2,0x6bb1,0x6bb3,0x6bb7,0x6bbc,0x6bc6,0x6bcb,0x6bd3,0x6bdf,0x6bec,
+ 0x6beb,0x6bf3,0x6bef,0x9ebe,0x6c08,0x6c13,0x6c14,0x6c1b,0x6c24,0x6c23,
+ 0x6c5e,0x6c55,0x6c62,0x6c6a,0x6c82,0x6c8d,0x6c9a,0x6c81,0x6c9b,0x6c7e,
+ 0x6c68,0x6c73,0x6c92,0x6c90,0x6cc4,0x6cf1,0x6cd3,0x6cbd,0x6cd7,0x6cc5,
+ 0x6cdd,0x6cae,0x6cb1,0x6cbe
+ },
+ { /* ku 3e */
+ 0x6cba,0x6cdb,0x6cef,0x6cd9,0x6cea,0x6d1f,0x884d,0x6d36,0x6d2b,0x6d3d,
+ 0x6d38,0x6d19,0x6d35,0x6d33,0x6d12,0x6d0c,0x6d63,0x6d93,0x6d64,0x6d5a,
+ 0x6d79,0x6d59,0x6d8e,0x6d95,0x6fe4,0x6d85,0x6df9,0x6e15,0x6e0a,0x6db5,
+ 0x6dc7,0x6de6,0x6db8,0x6dc6,0x6dec,0x6dde,0x6dcc,0x6de8,0x6dd2,0x6dc5,
+ 0x6dfa,0x6dd9,0x6de4,0x6dd5,0x6dea,0x6dee,0x6e2d,0x6e6e,0x6e2e,0x6e19,
+ 0x6e72,0x6e5f,0x6e3e,0x6e23,0x6e6b,0x6e2b,0x6e76,0x6e4d,0x6e1f,0x6e43,
+ 0x6e3a,0x6e4e,0x6e24,0x6eff,0x6e1d,0x6e38,0x6e82,0x6eaa,0x6e98,0x6ec9,
+ 0x6eb7,0x6ed3,0x6ebd,0x6eaf,0x6ec4,0x6eb2,0x6ed4,0x6ed5,0x6e8f,0x6ea5,
+ 0x6ec2,0x6e9f,0x6f41,0x6f11,0x704c,0x6eec,0x6ef8,0x6efe,0x6f3f,0x6ef2,
+ 0x6f31,0x6eef,0x6f32,0x6ecc
+ },
+ { /* ku 3f */
+ 0x6f3e,0x6f13,0x6ef7,0x6f86,0x6f7a,0x6f78,0x6f81,0x6f80,0x6f6f,0x6f5b,
+ 0x6ff3,0x6f6d,0x6f82,0x6f7c,0x6f58,0x6f8e,0x6f91,0x6fc2,0x6f66,0x6fb3,
+ 0x6fa3,0x6fa1,0x6fa4,0x6fb9,0x6fc6,0x6faa,0x6fdf,0x6fd5,0x6fec,0x6fd4,
+ 0x6fd8,0x6ff1,0x6fee,0x6fdb,0x7009,0x700b,0x6ffa,0x7011,0x7001,0x700f,
+ 0x6ffe,0x701b,0x701a,0x6f74,0x701d,0x7018,0x701f,0x7030,0x703e,0x7032,
+ 0x7051,0x7063,0x7099,0x7092,0x70af,0x70f1,0x70ac,0x70b8,0x70b3,0x70ae,
+ 0x70df,0x70cb,0x70dd,0x70d9,0x7109,0x70fd,0x711c,0x7119,0x7165,0x7155,
+ 0x7188,0x7166,0x7162,0x714c,0x7156,0x716c,0x718f,0x71fb,0x7184,0x7195,
+ 0x71a8,0x71ac,0x71d7,0x71b9,0x71be,0x71d2,0x71c9,0x71d4,0x71ce,0x71e0,
+ 0x71ec,0x71e7,0x71f5,0x71fc
+ },
+ { /* ku 40 */
+ 0x71f9,0x71ff,0x720d,0x7210,0x721b,0x7228,0x722d,0x722c,0x7230,0x7232,
+ 0x723b,0x723c,0x723f,0x7240,0x7246,0x724b,0x7258,0x7274,0x727e,0x7282,
+ 0x7281,0x7287,0x7292,0x7296,0x72a2,0x72a7,0x72b9,0x72b2,0x72c3,0x72c6,
+ 0x72c4,0x72ce,0x72d2,0x72e2,0x72e0,0x72e1,0x72f9,0x72f7,0x500f,0x7317,
+ 0x730a,0x731c,0x7316,0x731d,0x7334,0x732f,0x7329,0x7325,0x733e,0x734e,
+ 0x734f,0x9ed8,0x7357,0x736a,0x7368,0x7370,0x7378,0x7375,0x737b,0x737a,
+ 0x73c8,0x73b3,0x73ce,0x73bb,0x73c0,0x73e5,0x73ee,0x73de,0x74a2,0x7405,
+ 0x746f,0x7425,0x73f8,0x7432,0x743a,0x7455,0x743f,0x745f,0x7459,0x7441,
+ 0x745c,0x7469,0x7470,0x7463,0x746a,0x7476,0x747e,0x748b,0x749e,0x74a7,
+ 0x74ca,0x74cf,0x74d4,0x73f1
+ },
+ { /* ku 41 */
+ 0x74e0,0x74e3,0x74e7,0x74e9,0x74ee,0x74f2,0x74f0,0x74f1,0x74f8,0x74f7,
+ 0x7504,0x7503,0x7505,0x750c,0x750e,0x750d,0x7515,0x7513,0x751e,0x7526,
+ 0x752c,0x753c,0x7544,0x754d,0x754a,0x7549,0x755b,0x7546,0x755a,0x7569,
+ 0x7564,0x7567,0x756b,0x756d,0x7578,0x7576,0x7586,0x7587,0x7574,0x758a,
+ 0x7589,0x7582,0x7594,0x759a,0x759d,0x75a5,0x75a3,0x75c2,0x75b3,0x75c3,
+ 0x75b5,0x75bd,0x75b8,0x75bc,0x75b1,0x75cd,0x75ca,0x75d2,0x75d9,0x75e3,
+ 0x75de,0x75fe,0x75ff,0x75fc,0x7601,0x75f0,0x75fa,0x75f2,0x75f3,0x760b,
+ 0x760d,0x7609,0x761f,0x7627,0x7620,0x7621,0x7622,0x7624,0x7634,0x7630,
+ 0x763b,0x7647,0x7648,0x7646,0x765c,0x7658,0x7661,0x7662,0x7668,0x7669,
+ 0x766a,0x7667,0x766c,0x7670
+ },
+ { /* ku 42 */
+ 0x7672,0x7676,0x7678,0x767c,0x7680,0x7683,0x7688,0x768b,0x768e,0x7696,
+ 0x7693,0x7699,0x769a,0x76b0,0x76b4,0x76b8,0x76b9,0x76ba,0x76c2,0x76cd,
+ 0x76d6,0x76d2,0x76de,0x76e1,0x76e5,0x76e7,0x76ea,0x862f,0x76fb,0x7708,
+ 0x7707,0x7704,0x7729,0x7724,0x771e,0x7725,0x7726,0x771b,0x7737,0x7738,
+ 0x7747,0x775a,0x7768,0x776b,0x775b,0x7765,0x777f,0x777e,0x7779,0x778e,
+ 0x778b,0x7791,0x77a0,0x779e,0x77b0,0x77b6,0x77b9,0x77bf,0x77bc,0x77bd,
+ 0x77bb,0x77c7,0x77cd,0x77d7,0x77da,0x77dc,0x77e3,0x77ee,0x77fc,0x780c,
+ 0x7812,0x7926,0x7820,0x792a,0x7845,0x788e,0x7874,0x7886,0x787c,0x789a,
+ 0x788c,0x78a3,0x78b5,0x78aa,0x78af,0x78d1,0x78c6,0x78cb,0x78d4,0x78be,
+ 0x78bc,0x78c5,0x78ca,0x78ec
+ },
+ { /* ku 43 */
+ 0x78e7,0x78da,0x78fd,0x78f4,0x7907,0x7912,0x7911,0x7919,0x792c,0x792b,
+ 0x7940,0x7960,0x7957,0x795f,0x795a,0x7955,0x7953,0x797a,0x797f,0x798a,
+ 0x799d,0x79a7,0x9f4b,0x79aa,0x79ae,0x79b3,0x79b9,0x79ba,0x79c9,0x79d5,
+ 0x79e7,0x79ec,0x79e1,0x79e3,0x7a08,0x7a0d,0x7a18,0x7a19,0x7a20,0x7a1f,
+ 0x7980,0x7a31,0x7a3b,0x7a3e,0x7a37,0x7a43,0x7a57,0x7a49,0x7a61,0x7a62,
+ 0x7a69,0x9f9d,0x7a70,0x7a79,0x7a7d,0x7a88,0x7a97,0x7a95,0x7a98,0x7a96,
+ 0x7aa9,0x7ac8,0x7ab0,0x7ab6,0x7ac5,0x7ac4,0x7abf,0x9083,0x7ac7,0x7aca,
+ 0x7acd,0x7acf,0x7ad5,0x7ad3,0x7ad9,0x7ada,0x7add,0x7ae1,0x7ae2,0x7ae6,
+ 0x7aed,0x7af0,0x7b02,0x7b0f,0x7b0a,0x7b06,0x7b33,0x7b18,0x7b19,0x7b1e,
+ 0x7b35,0x7b28,0x7b36,0x7b50
+ },
+ { /* ku 44 */
+ 0x7b7a,0x7b04,0x7b4d,0x7b0b,0x7b4c,0x7b45,0x7b75,0x7b65,0x7b74,0x7b67,
+ 0x7b70,0x7b71,0x7b6c,0x7b6e,0x7b9d,0x7b98,0x7b9f,0x7b8d,0x7b9c,0x7b9a,
+ 0x7b8b,0x7b92,0x7b8f,0x7b5d,0x7b99,0x7bcb,0x7bc1,0x7bcc,0x7bcf,0x7bb4,
+ 0x7bc6,0x7bdd,0x7be9,0x7c11,0x7c14,0x7be6,0x7be5,0x7c60,0x7c00,0x7c07,
+ 0x7c13,0x7bf3,0x7bf7,0x7c17,0x7c0d,0x7bf6,0x7c23,0x7c27,0x7c2a,0x7c1f,
+ 0x7c37,0x7c2b,0x7c3d,0x7c4c,0x7c43,0x7c54,0x7c4f,0x7c40,0x7c50,0x7c58,
+ 0x7c5f,0x7c64,0x7c56,0x7c65,0x7c6c,0x7c75,0x7c83,0x7c90,0x7ca4,0x7cad,
+ 0x7ca2,0x7cab,0x7ca1,0x7ca8,0x7cb3,0x7cb2,0x7cb1,0x7cae,0x7cb9,0x7cbd,
+ 0x7cc0,0x7cc5,0x7cc2,0x7cd8,0x7cd2,0x7cdc,0x7ce2,0x9b3b,0x7cef,0x7cf2,
+ 0x7cf4,0x7cf6,0x7cfa,0x7d06
+ },
+ { /* ku 45 */
+ 0x7d02,0x7d1c,0x7d15,0x7d0a,0x7d45,0x7d4b,0x7d2e,0x7d32,0x7d3f,0x7d35,
+ 0x7d46,0x7d73,0x7d56,0x7d4e,0x7d72,0x7d68,0x7d6e,0x7d4f,0x7d63,0x7d93,
+ 0x7d89,0x7d5b,0x7d8f,0x7d7d,0x7d9b,0x7dba,0x7dae,0x7da3,0x7db5,0x7dc7,
+ 0x7dbd,0x7dab,0x7e3d,0x7da2,0x7daf,0x7ddc,0x7db8,0x7d9f,0x7db0,0x7dd8,
+ 0x7ddd,0x7de4,0x7dde,0x7dfb,0x7df2,0x7de1,0x7e05,0x7e0a,0x7e23,0x7e21,
+ 0x7e12,0x7e31,0x7e1f,0x7e09,0x7e0b,0x7e22,0x7e46,0x7e66,0x7e3b,0x7e35,
+ 0x7e39,0x7e43,0x7e37,0x7e32,0x7e3a,0x7e67,0x7e5d,0x7e56,0x7e5e,0x7e59,
+ 0x7e5a,0x7e79,0x7e6a,0x7e69,0x7e7c,0x7e7b,0x7e83,0x7dd5,0x7e7d,0x8fae,
+ 0x7e7f,0x7e88,0x7e89,0x7e8c,0x7e92,0x7e90,0x7e93,0x7e94,0x7e96,0x7e8e,
+ 0x7e9b,0x7e9c,0x7f38,0x7f3a
+ },
+ { /* ku 46 */
+ 0x7f45,0x7f4c,0x7f4d,0x7f4e,0x7f50,0x7f51,0x7f55,0x7f54,0x7f58,0x7f5f,
+ 0x7f60,0x7f68,0x7f69,0x7f67,0x7f78,0x7f82,0x7f86,0x7f83,0x7f88,0x7f87,
+ 0x7f8c,0x7f94,0x7f9e,0x7f9d,0x7f9a,0x7fa3,0x7faf,0x7fb2,0x7fb9,0x7fae,
+ 0x7fb6,0x7fb8,0x8b71,0x7fc5,0x7fc6,0x7fca,0x7fd5,0x7fd4,0x7fe1,0x7fe6,
+ 0x7fe9,0x7ff3,0x7ff9,0x98dc,0x8006,0x8004,0x800b,0x8012,0x8018,0x8019,
+ 0x801c,0x8021,0x8028,0x803f,0x803b,0x804a,0x8046,0x8052,0x8058,0x805a,
+ 0x805f,0x8062,0x8068,0x8073,0x8072,0x8070,0x8076,0x8079,0x807d,0x807f,
+ 0x8084,0x8086,0x8085,0x809b,0x8093,0x809a,0x80ad,0x5190,0x80ac,0x80db,
+ 0x80e5,0x80d9,0x80dd,0x80c4,0x80da,0x80d6,0x8109,0x80ef,0x80f1,0x811b,
+ 0x8129,0x8123,0x812f,0x814b
+ },
+ { /* ku 47 */
+ 0x968b,0x8146,0x813e,0x8153,0x8151,0x80fc,0x8171,0x816e,0x8165,0x8166,
+ 0x8174,0x8183,0x8188,0x818a,0x8180,0x8182,0x81a0,0x8195,0x81a4,0x81a3,
+ 0x815f,0x8193,0x81a9,0x81b0,0x81b5,0x81be,0x81b8,0x81bd,0x81c0,0x81c2,
+ 0x81ba,0x81c9,0x81cd,0x81d1,0x81d9,0x81d8,0x81c8,0x81da,0x81df,0x81e0,
+ 0x81e7,0x81fa,0x81fb,0x81fe,0x8201,0x8202,0x8205,0x8207,0x820a,0x820d,
+ 0x8210,0x8216,0x8229,0x822b,0x8238,0x8233,0x8240,0x8259,0x8258,0x825d,
+ 0x825a,0x825f,0x8264,0x8262,0x8268,0x826a,0x826b,0x822e,0x8271,0x8277,
+ 0x8278,0x827e,0x828d,0x8292,0x82ab,0x829f,0x82bb,0x82ac,0x82e1,0x82e3,
+ 0x82df,0x82d2,0x82f4,0x82f3,0x82fa,0x8393,0x8303,0x82fb,0x82f9,0x82de,
+ 0x8306,0x82dc,0x8309,0x82d9
+ },
+ { /* ku 48 */
+ 0x8335,0x8334,0x8316,0x8332,0x8331,0x8340,0x8339,0x8350,0x8345,0x832f,
+ 0x832b,0x8317,0x8318,0x8385,0x839a,0x83aa,0x839f,0x83a2,0x8396,0x8323,
+ 0x838e,0x8387,0x838a,0x837c,0x83b5,0x8373,0x8375,0x83a0,0x8389,0x83a8,
+ 0x83f4,0x8413,0x83eb,0x83ce,0x83fd,0x8403,0x83d8,0x840b,0x83c1,0x83f7,
+ 0x8407,0x83e0,0x83f2,0x840d,0x8422,0x8420,0x83bd,0x8438,0x8506,0x83fb,
+ 0x846d,0x842a,0x843c,0x855a,0x8484,0x8477,0x846b,0x84ad,0x846e,0x8482,
+ 0x8469,0x8446,0x842c,0x846f,0x8479,0x8435,0x84ca,0x8462,0x84b9,0x84bf,
+ 0x849f,0x84d9,0x84cd,0x84bb,0x84da,0x84d0,0x84c1,0x84c6,0x84d6,0x84a1,
+ 0x8521,0x84ff,0x84f4,0x8517,0x8518,0x852c,0x851f,0x8515,0x8514,0x84fc,
+ 0x8540,0x8563,0x8558,0x8548
+ },
+ { /* ku 49 */
+ 0x8541,0x8602,0x854b,0x8555,0x8580,0x85a4,0x8588,0x8591,0x858a,0x85a8,
+ 0x856d,0x8594,0x859b,0x85ea,0x8587,0x859c,0x8577,0x857e,0x8590,0x85c9,
+ 0x85ba,0x85cf,0x85b9,0x85d0,0x85d5,0x85dd,0x85e5,0x85dc,0x85f9,0x860a,
+ 0x8613,0x860b,0x85fe,0x85fa,0x8606,0x8622,0x861a,0x8630,0x863f,0x864d,
+ 0x4e55,0x8654,0x865f,0x8667,0x8671,0x8693,0x86a3,0x86a9,0x86aa,0x868b,
+ 0x868c,0x86b6,0x86af,0x86c4,0x86c6,0x86b0,0x86c9,0x8823,0x86ab,0x86d4,
+ 0x86de,0x86e9,0x86ec,0x86df,0x86db,0x86ef,0x8712,0x8706,0x8708,0x8700,
+ 0x8703,0x86fb,0x8711,0x8709,0x870d,0x86f9,0x870a,0x8734,0x873f,0x8737,
+ 0x873b,0x8725,0x8729,0x871a,0x8760,0x875f,0x8778,0x874c,0x874e,0x8774,
+ 0x8757,0x8768,0x876e,0x8759
+ },
+ { /* ku 4a */
+ 0x8753,0x8763,0x876a,0x8805,0x87a2,0x879f,0x8782,0x87af,0x87cb,0x87bd,
+ 0x87c0,0x87d0,0x96d6,0x87ab,0x87c4,0x87b3,0x87c7,0x87c6,0x87bb,0x87ef,
+ 0x87f2,0x87e0,0x880f,0x880d,0x87fe,0x87f6,0x87f7,0x880e,0x87d2,0x8811,
+ 0x8816,0x8815,0x8822,0x8821,0x8831,0x8836,0x8839,0x8827,0x883b,0x8844,
+ 0x8842,0x8852,0x8859,0x885e,0x8862,0x886b,0x8881,0x887e,0x889e,0x8875,
+ 0x887d,0x88b5,0x8872,0x8882,0x8897,0x8892,0x88ae,0x8899,0x88a2,0x888d,
+ 0x88a4,0x88b0,0x88bf,0x88b1,0x88c3,0x88c4,0x88d4,0x88d8,0x88d9,0x88dd,
+ 0x88f9,0x8902,0x88fc,0x88f4,0x88e8,0x88f2,0x8904,0x890c,0x890a,0x8913,
+ 0x8943,0x891e,0x8925,0x892a,0x892b,0x8941,0x8944,0x893b,0x8936,0x8938,
+ 0x894c,0x891d,0x8960,0x895e
+ },
+ { /* ku 4b */
+ 0x8966,0x8964,0x896d,0x896a,0x896f,0x8974,0x8977,0x897e,0x8983,0x8988,
+ 0x898a,0x8993,0x8998,0x89a1,0x89a9,0x89a6,0x89ac,0x89af,0x89b2,0x89ba,
+ 0x89bd,0x89bf,0x89c0,0x89da,0x89dc,0x89dd,0x89e7,0x89f4,0x89f8,0x8a03,
+ 0x8a16,0x8a10,0x8a0c,0x8a1b,0x8a1d,0x8a25,0x8a36,0x8a41,0x8a5b,0x8a52,
+ 0x8a46,0x8a48,0x8a7c,0x8a6d,0x8a6c,0x8a62,0x8a85,0x8a82,0x8a84,0x8aa8,
+ 0x8aa1,0x8a91,0x8aa5,0x8aa6,0x8a9a,0x8aa3,0x8ac4,0x8acd,0x8ac2,0x8ada,
+ 0x8aeb,0x8af3,0x8ae7,0x8ae4,0x8af1,0x8b14,0x8ae0,0x8ae2,0x8af7,0x8ade,
+ 0x8adb,0x8b0c,0x8b07,0x8b1a,0x8ae1,0x8b16,0x8b10,0x8b17,0x8b20,0x8b33,
+ 0x97ab,0x8b26,0x8b2b,0x8b3e,0x8b28,0x8b41,0x8b4c,0x8b4f,0x8b4e,0x8b49,
+ 0x8b56,0x8b5b,0x8b5a,0x8b6b
+ },
+ { /* ku 4c */
+ 0x8b5f,0x8b6c,0x8b6f,0x8b74,0x8b7d,0x8b80,0x8b8c,0x8b8e,0x8b92,0x8b93,
+ 0x8b96,0x8b99,0x8b9a,0x8c3a,0x8c41,0x8c3f,0x8c48,0x8c4c,0x8c4e,0x8c50,
+ 0x8c55,0x8c62,0x8c6c,0x8c78,0x8c7a,0x8c82,0x8c89,0x8c85,0x8c8a,0x8c8d,
+ 0x8c8e,0x8c94,0x8c7c,0x8c98,0x621d,0x8cad,0x8caa,0x8cbd,0x8cb2,0x8cb3,
+ 0x8cae,0x8cb6,0x8cc8,0x8cc1,0x8ce4,0x8ce3,0x8cda,0x8cfd,0x8cfa,0x8cfb,
+ 0x8d04,0x8d05,0x8d0a,0x8d07,0x8d0f,0x8d0d,0x8d10,0x9f4e,0x8d13,0x8ccd,
+ 0x8d14,0x8d16,0x8d67,0x8d6d,0x8d71,0x8d73,0x8d81,0x8d99,0x8dc2,0x8dbe,
+ 0x8dba,0x8dcf,0x8dda,0x8dd6,0x8dcc,0x8ddb,0x8dcb,0x8dea,0x8deb,0x8ddf,
+ 0x8de3,0x8dfc,0x8e08,0x8e09,0x8dff,0x8e1d,0x8e1e,0x8e10,0x8e1f,0x8e42,
+ 0x8e35,0x8e30,0x8e34,0x8e4a
+ },
+ { /* ku 4d */
+ 0x8e47,0x8e49,0x8e4c,0x8e50,0x8e48,0x8e59,0x8e64,0x8e60,0x8e2a,0x8e63,
+ 0x8e55,0x8e76,0x8e72,0x8e7c,0x8e81,0x8e87,0x8e85,0x8e84,0x8e8b,0x8e8a,
+ 0x8e93,0x8e91,0x8e94,0x8e99,0x8eaa,0x8ea1,0x8eac,0x8eb0,0x8ec6,0x8eb1,
+ 0x8ebe,0x8ec5,0x8ec8,0x8ecb,0x8edb,0x8ee3,0x8efc,0x8efb,0x8eeb,0x8efe,
+ 0x8f0a,0x8f05,0x8f15,0x8f12,0x8f19,0x8f13,0x8f1c,0x8f1f,0x8f1b,0x8f0c,
+ 0x8f26,0x8f33,0x8f3b,0x8f39,0x8f45,0x8f42,0x8f3e,0x8f4c,0x8f49,0x8f46,
+ 0x8f4e,0x8f57,0x8f5c,0x8f62,0x8f63,0x8f64,0x8f9c,0x8f9f,0x8fa3,0x8fad,
+ 0x8faf,0x8fb7,0x8fda,0x8fe5,0x8fe2,0x8fea,0x8fef,0x9087,0x8ff4,0x9005,
+ 0x8ff9,0x8ffa,0x9011,0x9015,0x9021,0x900d,0x901e,0x9016,0x900b,0x9027,
+ 0x9036,0x9035,0x9039,0x8ff8
+ },
+ { /* ku 4e */
+ 0x904f,0x9050,0x9051,0x9052,0x900e,0x9049,0x903e,0x9056,0x9058,0x905e,
+ 0x9068,0x906f,0x9076,0x96a8,0x9072,0x9082,0x907d,0x9081,0x9080,0x908a,
+ 0x9089,0x908f,0x90a8,0x90af,0x90b1,0x90b5,0x90e2,0x90e4,0x6248,0x90db,
+ 0x9102,0x9112,0x9119,0x9132,0x9130,0x914a,0x9156,0x9158,0x9163,0x9165,
+ 0x9169,0x9173,0x9172,0x918b,0x9189,0x9182,0x91a2,0x91ab,0x91af,0x91aa,
+ 0x91b5,0x91b4,0x91ba,0x91c0,0x91c1,0x91c9,0x91cb,0x91d0,0x91d6,0x91df,
+ 0x91e1,0x91db,0x91fc,0x91f5,0x91f6,0x921e,0x91ff,0x9214,0x922c,0x9215,
+ 0x9211,0x925e,0x9257,0x9245,0x9249,0x9264,0x9248,0x9295,0x923f,0x924b,
+ 0x9250,0x929c,0x9296,0x9293,0x929b,0x925a,0x92cf,0x92b9,0x92b7,0x92e9,
+ 0x930f,0x92fa,0x9344,0x932e
+ },
+ { /* ku 4f */
+ 0x9319,0x9322,0x931a,0x9323,0x933a,0x9335,0x933b,0x935c,0x9360,0x937c,
+ 0x936e,0x9356,0x93b0,0x93ac,0x93ad,0x9394,0x93b9,0x93d6,0x93d7,0x93e8,
+ 0x93e5,0x93d8,0x93c3,0x93dd,0x93d0,0x93c8,0x93e4,0x941a,0x9414,0x9413,
+ 0x9403,0x9407,0x9410,0x9436,0x942b,0x9435,0x9421,0x943a,0x9441,0x9452,
+ 0x9444,0x945b,0x9460,0x9462,0x945e,0x946a,0x9229,0x9470,0x9475,0x9477,
+ 0x947d,0x945a,0x947c,0x947e,0x9481,0x947f,0x9582,0x9587,0x958a,0x9594,
+ 0x9596,0x9598,0x9599,0x95a0,0x95a8,0x95a7,0x95ad,0x95bc,0x95bb,0x95b9,
+ 0x95be,0x95ca,0x6ff6,0x95c3,0x95cd,0x95cc,0x95d5,0x95d4,0x95d6,0x95dc,
+ 0x95e1,0x95e5,0x95e2,0x9621,0x9628,0x962e,0x962f,0x9642,0x964c,0x964f,
+ 0x964b,0x9677,0x965c,0x965e
+ },
+ { /* ku 50 */
+ 0x965d,0x965f,0x9666,0x9672,0x966c,0x968d,0x9698,0x9695,0x9697,0x96aa,
+ 0x96a7,0x96b1,0x96b2,0x96b0,0x96b4,0x96b6,0x96b8,0x96b9,0x96ce,0x96cb,
+ 0x96c9,0x96cd,0x894d,0x96dc,0x970d,0x96d5,0x96f9,0x9704,0x9706,0x9708,
+ 0x9713,0x970e,0x9711,0x970f,0x9716,0x9719,0x9724,0x972a,0x9730,0x9739,
+ 0x973d,0x973e,0x9744,0x9746,0x9748,0x9742,0x9749,0x975c,0x9760,0x9764,
+ 0x9766,0x9768,0x52d2,0x976b,0x9771,0x9779,0x9785,0x977c,0x9781,0x977a,
+ 0x9786,0x978b,0x978f,0x9790,0x979c,0x97a8,0x97a6,0x97a3,0x97b3,0x97b4,
+ 0x97c3,0x97c6,0x97c8,0x97cb,0x97dc,0x97ed,0x9f4f,0x97f2,0x7adf,0x97f6,
+ 0x97f5,0x980f,0x980c,0x9838,0x9824,0x9821,0x9837,0x983d,0x9846,0x984f,
+ 0x984b,0x986b,0x986f,0x9870
+ },
+ { /* ku 51 */
+ 0x9871,0x9874,0x9873,0x98aa,0x98af,0x98b1,0x98b6,0x98c4,0x98c3,0x98c6,
+ 0x98e9,0x98eb,0x9903,0x9909,0x9912,0x9914,0x9918,0x9921,0x991d,0x991e,
+ 0x9924,0x9920,0x992c,0x992e,0x993d,0x993e,0x9942,0x9949,0x9945,0x9950,
+ 0x994b,0x9951,0x9952,0x994c,0x9955,0x9997,0x9998,0x99a5,0x99ad,0x99ae,
+ 0x99bc,0x99df,0x99db,0x99dd,0x99d8,0x99d1,0x99ed,0x99ee,0x99f1,0x99f2,
+ 0x99fb,0x99f8,0x9a01,0x9a0f,0x9a05,0x99e2,0x9a19,0x9a2b,0x9a37,0x9a45,
+ 0x9a42,0x9a40,0x9a43,0x9a3e,0x9a55,0x9a4d,0x9a5b,0x9a57,0x9a5f,0x9a62,
+ 0x9a65,0x9a64,0x9a69,0x9a6b,0x9a6a,0x9aad,0x9ab0,0x9abc,0x9ac0,0x9acf,
+ 0x9ad1,0x9ad3,0x9ad4,0x9ade,0x9adf,0x9ae2,0x9ae3,0x9ae6,0x9aef,0x9aeb,
+ 0x9aee,0x9af4,0x9af1,0x9af7
+ },
+ { /* ku 52 */
+ 0x9afb,0x9b06,0x9b18,0x9b1a,0x9b1f,0x9b22,0x9b23,0x9b25,0x9b27,0x9b28,
+ 0x9b29,0x9b2a,0x9b2e,0x9b2f,0x9b32,0x9b44,0x9b43,0x9b4f,0x9b4d,0x9b4e,
+ 0x9b51,0x9b58,0x9b74,0x9b93,0x9b83,0x9b91,0x9b96,0x9b97,0x9b9f,0x9ba0,
+ 0x9ba8,0x9bb4,0x9bc0,0x9bca,0x9bb9,0x9bc6,0x9bcf,0x9bd1,0x9bd2,0x9be3,
+ 0x9be2,0x9be4,0x9bd4,0x9be1,0x9c3a,0x9bf2,0x9bf1,0x9bf0,0x9c15,0x9c14,
+ 0x9c09,0x9c13,0x9c0c,0x9c06,0x9c08,0x9c12,0x9c0a,0x9c04,0x9c2e,0x9c1b,
+ 0x9c25,0x9c24,0x9c21,0x9c30,0x9c47,0x9c32,0x9c46,0x9c3e,0x9c5a,0x9c60,
+ 0x9c67,0x9c76,0x9c78,0x9ce7,0x9cec,0x9cf0,0x9d09,0x9d08,0x9ceb,0x9d03,
+ 0x9d06,0x9d2a,0x9d26,0x9daf,0x9d23,0x9d1f,0x9d44,0x9d15,0x9d12,0x9d41,
+ 0x9d3f,0x9d3e,0x9d46,0x9d48
+ },
+ { /* ku 53 */
+ 0x9d5d,0x9d5e,0x9d64,0x9d51,0x9d50,0x9d59,0x9d72,0x9d89,0x9d87,0x9dab,
+ 0x9d6f,0x9d7a,0x9d9a,0x9da4,0x9da9,0x9db2,0x9dc4,0x9dc1,0x9dbb,0x9db8,
+ 0x9dba,0x9dc6,0x9dcf,0x9dc2,0x9dd9,0x9dd3,0x9df8,0x9de6,0x9ded,0x9def,
+ 0x9dfd,0x9e1a,0x9e1b,0x9e1e,0x9e75,0x9e79,0x9e7d,0x9e81,0x9e88,0x9e8b,
+ 0x9e8c,0x9e92,0x9e95,0x9e91,0x9e9d,0x9ea5,0x9ea9,0x9eb8,0x9eaa,0x9ead,
+ 0x9761,0x9ecc,0x9ece,0x9ecf,0x9ed0,0x9ed4,0x9edc,0x9ede,0x9edd,0x9ee0,
+ 0x9ee5,0x9ee8,0x9eef,0x9ef4,0x9ef6,0x9ef7,0x9ef9,0x9efb,0x9efc,0x9efd,
+ 0x9f07,0x9f08,0x76b7,0x9f15,0x9f21,0x9f2c,0x9f3e,0x9f4a,0x9f52,0x9f54,
+ 0x9f63,0x9f5f,0x9f60,0x9f61,0x9f66,0x9f67,0x9f6c,0x9f6a,0x9f77,0x9f72,
+ 0x9f76,0x9f95,0x9f9c,0x9fa0
+ },
+ { /* ku 54 */
+ 0x582f,0x69c7,0x9059,0x7464,0x51dc,0x7199,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/jis_0212.c b/imap/src/charset/jis_0212.c
new file mode 100644
index 00000000..d47d8a42
--- /dev/null
+++ b/imap/src/charset/jis_0212.c
@@ -0,0 +1,962 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: JIS X0212 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 August 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* JIS X0212 is the supplemental industrial standard of Japan. */
+
+#define BASE_JIS0212_KU 0x22
+#define BASE_JIS0212_TEN 0x21
+#define MAX_JIS0212_KU 76
+#define MAX_JIS0212_TEN 94
+
+
+#define JIS0212TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_JIS0212_KU) < MAX_JIS0212_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_JIS0212_TEN) < MAX_JIS0212_TEN)) ? \
+ jis0212tab[ku][ten] : UBOGON)
+
+
+static const unsigned short jis0212tab[MAX_JIS0212_KU][MAX_JIS0212_TEN] = {
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x02d8,0x02c7,0x00b8,0x02d9,0x02dd,0x00af,
+ 0x02db,0x02da,0x007e,0x0384,0x0385,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x00a1,0x00a6,0x00bf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x00ba,0x00aa,0x00a9,0x00ae,0x2122,0x00a4,
+ 0x2116,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ /* Note: ku/ten codepoints 05/87 - 05/90 are proposed for addition to
+ * JIS X 0212; I don't know if they've been formally accepted yet.
+ * They represent katakana VA, VI, VE, and VO, and are in the BMP but
+ * not in Unicode's JIS conversion tables. They're useful enough that
+ * I decided to put them here.
+ */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x30f7,0x30f8,0x30f9,0x30fa,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x0386,0x0388,0x0389,0x038a,0x03aa,UBOGON,
+ 0x038c,UBOGON,0x038e,0x03ab,UBOGON,0x038f,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x03ac,0x03ad,0x03ae,0x03af,0x03ca,0x0390,0x03cc,0x03c2,0x03cd,0x03cb,
+ 0x03b0,0x03ce,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,0x0408,
+ 0x0409,0x040a,0x040b,0x040c,0x040e,0x040f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,0x0458,0x0459,0x045a,
+ 0x045b,0x045c,0x045e,0x045f
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ 0x00c6,0x0110,UBOGON,0x0126,UBOGON,0x0132,UBOGON,0x0141,0x013f,UBOGON,
+ 0x014a,0x00d8,0x0152,UBOGON,0x0166,0x00de,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x00e6,0x0111,0x00f0,0x0127,0x0131,0x0133,0x0138,0x0142,
+ 0x0140,0x0149,0x014b,0x00f8,0x0153,0x00df,0x0167,0x00fe,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ 0x00c1,0x00c0,0x00c4,0x00c2,0x0102,0x01cd,0x0100,0x0104,0x00c5,0x00c3,
+ 0x0106,0x0108,0x010c,0x00c7,0x010a,0x010e,0x00c9,0x00c8,0x00cb,0x00ca,
+ 0x011a,0x0116,0x0112,0x0118,UBOGON,0x011c,0x011e,0x0122,0x0120,0x0124,
+ 0x00cd,0x00cc,0x00cf,0x00ce,0x01cf,0x0130,0x012a,0x012e,0x0128,0x0134,
+ 0x0136,0x0139,0x013d,0x013b,0x0143,0x0147,0x0145,0x00d1,0x00d3,0x00d2,
+ 0x00d6,0x00d4,0x01d1,0x0150,0x014c,0x00d5,0x0154,0x0158,0x0156,0x015a,
+ 0x015c,0x0160,0x015e,0x0164,0x0162,0x00da,0x00d9,0x00dc,0x00db,0x016c,
+ 0x01d3,0x0170,0x016a,0x0172,0x016e,0x0168,0x01d7,0x01db,0x01d9,0x01d5,
+ 0x0174,0x00dd,0x0178,0x0176,0x0179,0x017d,0x017b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ 0x00e1,0x00e0,0x00e4,0x00e2,0x0103,0x01ce,0x0101,0x0105,0x00e5,0x00e3,
+ 0x0107,0x0109,0x010d,0x00e7,0x010b,0x010f,0x00e9,0x00e8,0x00eb,0x00ea,
+ 0x011b,0x0117,0x0113,0x0119,0x01f5,0x011d,0x011f,UBOGON,0x0121,0x0125,
+ 0x00ed,0x00ec,0x00ef,0x00ee,0x01d0,UBOGON,0x012b,0x012f,0x0129,0x0135,
+ 0x0137,0x013a,0x013e,0x013c,0x0144,0x0148,0x0146,0x00f1,0x00f3,0x00f2,
+ 0x00f6,0x00f4,0x01d2,0x0151,0x014d,0x00f5,0x0155,0x0159,0x0157,0x015b,
+ 0x015d,0x0161,0x015f,0x0165,0x0163,0x00fa,0x00f9,0x00fc,0x00fb,0x016d,
+ 0x01d4,0x0171,0x016b,0x0173,0x016f,0x0169,0x01d8,0x01dc,0x01da,0x01d6,
+ 0x0175,0x00fd,0x00ff,0x0177,0x017a,0x017e,0x017c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x4e02,0x4e04,0x4e05,0x4e0c,0x4e12,0x4e1f,0x4e23,0x4e24,0x4e28,0x4e2b,
+ 0x4e2e,0x4e2f,0x4e30,0x4e35,0x4e40,0x4e41,0x4e44,0x4e47,0x4e51,0x4e5a,
+ 0x4e5c,0x4e63,0x4e68,0x4e69,0x4e74,0x4e75,0x4e79,0x4e7f,0x4e8d,0x4e96,
+ 0x4e97,0x4e9d,0x4eaf,0x4eb9,0x4ec3,0x4ed0,0x4eda,0x4edb,0x4ee0,0x4ee1,
+ 0x4ee2,0x4ee8,0x4eef,0x4ef1,0x4ef3,0x4ef5,0x4efd,0x4efe,0x4eff,0x4f00,
+ 0x4f02,0x4f03,0x4f08,0x4f0b,0x4f0c,0x4f12,0x4f15,0x4f16,0x4f17,0x4f19,
+ 0x4f2e,0x4f31,0x4f60,0x4f33,0x4f35,0x4f37,0x4f39,0x4f3b,0x4f3e,0x4f40,
+ 0x4f42,0x4f48,0x4f49,0x4f4b,0x4f4c,0x4f52,0x4f54,0x4f56,0x4f58,0x4f5f,
+ 0x4f63,0x4f6a,0x4f6c,0x4f6e,0x4f71,0x4f77,0x4f78,0x4f79,0x4f7a,0x4f7d,
+ 0x4f7e,0x4f81,0x4f82,0x4f84
+ },
+ { /* ku 11 */
+ 0x4f85,0x4f89,0x4f8a,0x4f8c,0x4f8e,0x4f90,0x4f92,0x4f93,0x4f94,0x4f97,
+ 0x4f99,0x4f9a,0x4f9e,0x4f9f,0x4fb2,0x4fb7,0x4fb9,0x4fbb,0x4fbc,0x4fbd,
+ 0x4fbe,0x4fc0,0x4fc1,0x4fc5,0x4fc6,0x4fc8,0x4fc9,0x4fcb,0x4fcc,0x4fcd,
+ 0x4fcf,0x4fd2,0x4fdc,0x4fe0,0x4fe2,0x4ff0,0x4ff2,0x4ffc,0x4ffd,0x4fff,
+ 0x5000,0x5001,0x5004,0x5007,0x500a,0x500c,0x500e,0x5010,0x5013,0x5017,
+ 0x5018,0x501b,0x501c,0x501d,0x501e,0x5022,0x5027,0x502e,0x5030,0x5032,
+ 0x5033,0x5035,0x5040,0x5041,0x5042,0x5045,0x5046,0x504a,0x504c,0x504e,
+ 0x5051,0x5052,0x5053,0x5057,0x5059,0x505f,0x5060,0x5062,0x5063,0x5066,
+ 0x5067,0x506a,0x506d,0x5070,0x5071,0x503b,0x5081,0x5083,0x5084,0x5086,
+ 0x508a,0x508e,0x508f,0x5090
+ },
+ { /* ku 12 */
+ 0x5092,0x5093,0x5094,0x5096,0x509b,0x509c,0x509e,0x509f,0x50a0,0x50a1,
+ 0x50a2,0x50aa,0x50af,0x50b0,0x50b9,0x50ba,0x50bd,0x50c0,0x50c3,0x50c4,
+ 0x50c7,0x50cc,0x50ce,0x50d0,0x50d3,0x50d4,0x50d8,0x50dc,0x50dd,0x50df,
+ 0x50e2,0x50e4,0x50e6,0x50e8,0x50e9,0x50ef,0x50f1,0x50f6,0x50fa,0x50fe,
+ 0x5103,0x5106,0x5107,0x5108,0x510b,0x510c,0x510d,0x510e,0x50f2,0x5110,
+ 0x5117,0x5119,0x511b,0x511c,0x511d,0x511e,0x5123,0x5127,0x5128,0x512c,
+ 0x512d,0x512f,0x5131,0x5133,0x5134,0x5135,0x5138,0x5139,0x5142,0x514a,
+ 0x514f,0x5153,0x5155,0x5157,0x5158,0x515f,0x5164,0x5166,0x517e,0x5183,
+ 0x5184,0x518b,0x518e,0x5198,0x519d,0x51a1,0x51a3,0x51ad,0x51b8,0x51ba,
+ 0x51bc,0x51be,0x51bf,0x51c2
+ },
+ { /* ku 13 */
+ 0x51c8,0x51cf,0x51d1,0x51d2,0x51d3,0x51d5,0x51d8,0x51de,0x51e2,0x51e5,
+ 0x51ee,0x51f2,0x51f3,0x51f4,0x51f7,0x5201,0x5202,0x5205,0x5212,0x5213,
+ 0x5215,0x5216,0x5218,0x5222,0x5228,0x5231,0x5232,0x5235,0x523c,0x5245,
+ 0x5249,0x5255,0x5257,0x5258,0x525a,0x525c,0x525f,0x5260,0x5261,0x5266,
+ 0x526e,0x5277,0x5278,0x5279,0x5280,0x5282,0x5285,0x528a,0x528c,0x5293,
+ 0x5295,0x5296,0x5297,0x5298,0x529a,0x529c,0x52a4,0x52a5,0x52a6,0x52a7,
+ 0x52af,0x52b0,0x52b6,0x52b7,0x52b8,0x52ba,0x52bb,0x52bd,0x52c0,0x52c4,
+ 0x52c6,0x52c8,0x52cc,0x52cf,0x52d1,0x52d4,0x52d6,0x52db,0x52dc,0x52e1,
+ 0x52e5,0x52e8,0x52e9,0x52ea,0x52ec,0x52f0,0x52f1,0x52f4,0x52f6,0x52f7,
+ 0x5300,0x5303,0x530a,0x530b
+ },
+ { /* ku 14 */
+ 0x530c,0x5311,0x5313,0x5318,0x531b,0x531c,0x531e,0x531f,0x5325,0x5327,
+ 0x5328,0x5329,0x532b,0x532c,0x532d,0x5330,0x5332,0x5335,0x533c,0x533d,
+ 0x533e,0x5342,0x534c,0x534b,0x5359,0x535b,0x5361,0x5363,0x5365,0x536c,
+ 0x536d,0x5372,0x5379,0x537e,0x5383,0x5387,0x5388,0x538e,0x5393,0x5394,
+ 0x5399,0x539d,0x53a1,0x53a4,0x53aa,0x53ab,0x53af,0x53b2,0x53b4,0x53b5,
+ 0x53b7,0x53b8,0x53ba,0x53bd,0x53c0,0x53c5,0x53cf,0x53d2,0x53d3,0x53d5,
+ 0x53da,0x53dd,0x53de,0x53e0,0x53e6,0x53e7,0x53f5,0x5402,0x5413,0x541a,
+ 0x5421,0x5427,0x5428,0x542a,0x542f,0x5431,0x5434,0x5435,0x5443,0x5444,
+ 0x5447,0x544d,0x544f,0x545e,0x5462,0x5464,0x5466,0x5467,0x5469,0x546b,
+ 0x546d,0x546e,0x5474,0x547f
+ },
+ { /* ku 15 */
+ 0x5481,0x5483,0x5485,0x5488,0x5489,0x548d,0x5491,0x5495,0x5496,0x549c,
+ 0x549f,0x54a1,0x54a6,0x54a7,0x54a9,0x54aa,0x54ad,0x54ae,0x54b1,0x54b7,
+ 0x54b9,0x54ba,0x54bb,0x54bf,0x54c6,0x54ca,0x54cd,0x54ce,0x54e0,0x54ea,
+ 0x54ec,0x54ef,0x54f6,0x54fc,0x54fe,0x54ff,0x5500,0x5501,0x5505,0x5508,
+ 0x5509,0x550c,0x550d,0x550e,0x5515,0x552a,0x552b,0x5532,0x5535,0x5536,
+ 0x553b,0x553c,0x553d,0x5541,0x5547,0x5549,0x554a,0x554d,0x5550,0x5551,
+ 0x5558,0x555a,0x555b,0x555e,0x5560,0x5561,0x5564,0x5566,0x557f,0x5581,
+ 0x5582,0x5586,0x5588,0x558e,0x558f,0x5591,0x5592,0x5593,0x5594,0x5597,
+ 0x55a3,0x55a4,0x55ad,0x55b2,0x55bf,0x55c1,0x55c3,0x55c6,0x55c9,0x55cb,
+ 0x55cc,0x55ce,0x55d1,0x55d2
+ },
+ { /* ku 16 */
+ 0x55d3,0x55d7,0x55d8,0x55db,0x55de,0x55e2,0x55e9,0x55f6,0x55ff,0x5605,
+ 0x5608,0x560a,0x560d,0x560e,0x560f,0x5610,0x5611,0x5612,0x5619,0x562c,
+ 0x5630,0x5633,0x5635,0x5637,0x5639,0x563b,0x563c,0x563d,0x563f,0x5640,
+ 0x5641,0x5643,0x5644,0x5646,0x5649,0x564b,0x564d,0x564f,0x5654,0x565e,
+ 0x5660,0x5661,0x5662,0x5663,0x5666,0x5669,0x566d,0x566f,0x5671,0x5672,
+ 0x5675,0x5684,0x5685,0x5688,0x568b,0x568c,0x5695,0x5699,0x569a,0x569d,
+ 0x569e,0x569f,0x56a6,0x56a7,0x56a8,0x56a9,0x56ab,0x56ac,0x56ad,0x56b1,
+ 0x56b3,0x56b7,0x56be,0x56c5,0x56c9,0x56ca,0x56cb,0x56cf,0x56d0,0x56cc,
+ 0x56cd,0x56d9,0x56dc,0x56dd,0x56df,0x56e1,0x56e4,0x56e5,0x56e6,0x56e7,
+ 0x56e8,0x56f1,0x56eb,0x56ed
+ },
+ { /* ku 17 */
+ 0x56f6,0x56f7,0x5701,0x5702,0x5707,0x570a,0x570c,0x5711,0x5715,0x571a,
+ 0x571b,0x571d,0x5720,0x5722,0x5723,0x5724,0x5725,0x5729,0x572a,0x572c,
+ 0x572e,0x572f,0x5733,0x5734,0x573d,0x573e,0x573f,0x5745,0x5746,0x574c,
+ 0x574d,0x5752,0x5762,0x5765,0x5767,0x5768,0x576b,0x576d,0x576e,0x576f,
+ 0x5770,0x5771,0x5773,0x5774,0x5775,0x5777,0x5779,0x577a,0x577b,0x577c,
+ 0x577e,0x5781,0x5783,0x578c,0x5794,0x5797,0x5799,0x579a,0x579c,0x579d,
+ 0x579e,0x579f,0x57a1,0x5795,0x57a7,0x57a8,0x57a9,0x57ac,0x57b8,0x57bd,
+ 0x57c7,0x57c8,0x57cc,0x57cf,0x57d5,0x57dd,0x57de,0x57e4,0x57e6,0x57e7,
+ 0x57e9,0x57ed,0x57f0,0x57f5,0x57f6,0x57f8,0x57fd,0x57fe,0x57ff,0x5803,
+ 0x5804,0x5808,0x5809,0x57e1
+ },
+ { /* ku 18 */
+ 0x580c,0x580d,0x581b,0x581e,0x581f,0x5820,0x5826,0x5827,0x582d,0x5832,
+ 0x5839,0x583f,0x5849,0x584c,0x584d,0x584f,0x5850,0x5855,0x585f,0x5861,
+ 0x5864,0x5867,0x5868,0x5878,0x587c,0x587f,0x5880,0x5881,0x5887,0x5888,
+ 0x5889,0x588a,0x588c,0x588d,0x588f,0x5890,0x5894,0x5896,0x589d,0x58a0,
+ 0x58a1,0x58a2,0x58a6,0x58a9,0x58b1,0x58b2,0x58c4,0x58bc,0x58c2,0x58c8,
+ 0x58cd,0x58ce,0x58d0,0x58d2,0x58d4,0x58d6,0x58da,0x58dd,0x58e1,0x58e2,
+ 0x58e9,0x58f3,0x5905,0x5906,0x590b,0x590c,0x5912,0x5913,0x5914,0x8641,
+ 0x591d,0x5921,0x5923,0x5924,0x5928,0x592f,0x5930,0x5933,0x5935,0x5936,
+ 0x593f,0x5943,0x5946,0x5952,0x5953,0x5959,0x595b,0x595d,0x595e,0x595f,
+ 0x5961,0x5963,0x596b,0x596d
+ },
+ { /* ku 19 */
+ 0x596f,0x5972,0x5975,0x5976,0x5979,0x597b,0x597c,0x598b,0x598c,0x598e,
+ 0x5992,0x5995,0x5997,0x599f,0x59a4,0x59a7,0x59ad,0x59ae,0x59af,0x59b0,
+ 0x59b3,0x59b7,0x59ba,0x59bc,0x59c1,0x59c3,0x59c4,0x59c8,0x59ca,0x59cd,
+ 0x59d2,0x59dd,0x59de,0x59df,0x59e3,0x59e4,0x59e7,0x59ee,0x59ef,0x59f1,
+ 0x59f2,0x59f4,0x59f7,0x5a00,0x5a04,0x5a0c,0x5a0d,0x5a0e,0x5a12,0x5a13,
+ 0x5a1e,0x5a23,0x5a24,0x5a27,0x5a28,0x5a2a,0x5a2d,0x5a30,0x5a44,0x5a45,
+ 0x5a47,0x5a48,0x5a4c,0x5a50,0x5a55,0x5a5e,0x5a63,0x5a65,0x5a67,0x5a6d,
+ 0x5a77,0x5a7a,0x5a7b,0x5a7e,0x5a8b,0x5a90,0x5a93,0x5a96,0x5a99,0x5a9c,
+ 0x5a9e,0x5a9f,0x5aa0,0x5aa2,0x5aa7,0x5aac,0x5ab1,0x5ab2,0x5ab3,0x5ab5,
+ 0x5ab8,0x5aba,0x5abb,0x5abf
+ },
+ { /* ku 1a */
+ 0x5ac4,0x5ac6,0x5ac8,0x5acf,0x5ada,0x5adc,0x5ae0,0x5ae5,0x5aea,0x5aee,
+ 0x5af5,0x5af6,0x5afd,0x5b00,0x5b01,0x5b08,0x5b17,0x5b34,0x5b19,0x5b1b,
+ 0x5b1d,0x5b21,0x5b25,0x5b2d,0x5b38,0x5b41,0x5b4b,0x5b4c,0x5b52,0x5b56,
+ 0x5b5e,0x5b68,0x5b6e,0x5b6f,0x5b7c,0x5b7d,0x5b7e,0x5b7f,0x5b81,0x5b84,
+ 0x5b86,0x5b8a,0x5b8e,0x5b90,0x5b91,0x5b93,0x5b94,0x5b96,0x5ba8,0x5ba9,
+ 0x5bac,0x5bad,0x5baf,0x5bb1,0x5bb2,0x5bb7,0x5bba,0x5bbc,0x5bc0,0x5bc1,
+ 0x5bcd,0x5bcf,0x5bd6,0x5bd7,0x5bd8,0x5bd9,0x5bda,0x5be0,0x5bef,0x5bf1,
+ 0x5bf4,0x5bfd,0x5c0c,0x5c17,0x5c1e,0x5c1f,0x5c23,0x5c26,0x5c29,0x5c2b,
+ 0x5c2c,0x5c2e,0x5c30,0x5c32,0x5c35,0x5c36,0x5c59,0x5c5a,0x5c5c,0x5c62,
+ 0x5c63,0x5c67,0x5c68,0x5c69
+ },
+ { /* ku 1b */
+ 0x5c6d,0x5c70,0x5c74,0x5c75,0x5c7a,0x5c7b,0x5c7c,0x5c7d,0x5c87,0x5c88,
+ 0x5c8a,0x5c8f,0x5c92,0x5c9d,0x5c9f,0x5ca0,0x5ca2,0x5ca3,0x5ca6,0x5caa,
+ 0x5cb2,0x5cb4,0x5cb5,0x5cba,0x5cc9,0x5ccb,0x5cd2,0x5cdd,0x5cd7,0x5cee,
+ 0x5cf1,0x5cf2,0x5cf4,0x5d01,0x5d06,0x5d0d,0x5d12,0x5d2b,0x5d23,0x5d24,
+ 0x5d26,0x5d27,0x5d31,0x5d34,0x5d39,0x5d3d,0x5d3f,0x5d42,0x5d43,0x5d46,
+ 0x5d48,0x5d55,0x5d51,0x5d59,0x5d4a,0x5d5f,0x5d60,0x5d61,0x5d62,0x5d64,
+ 0x5d6a,0x5d6d,0x5d70,0x5d79,0x5d7a,0x5d7e,0x5d7f,0x5d81,0x5d83,0x5d88,
+ 0x5d8a,0x5d92,0x5d93,0x5d94,0x5d95,0x5d99,0x5d9b,0x5d9f,0x5da0,0x5da7,
+ 0x5dab,0x5db0,0x5db4,0x5db8,0x5db9,0x5dc3,0x5dc7,0x5dcb,0x5dd0,0x5dce,
+ 0x5dd8,0x5dd9,0x5de0,0x5de4
+ },
+ { /* ku 1c */
+ 0x5de9,0x5df8,0x5df9,0x5e00,0x5e07,0x5e0d,0x5e12,0x5e14,0x5e15,0x5e18,
+ 0x5e1f,0x5e20,0x5e2e,0x5e28,0x5e32,0x5e35,0x5e3e,0x5e4b,0x5e50,0x5e49,
+ 0x5e51,0x5e56,0x5e58,0x5e5b,0x5e5c,0x5e5e,0x5e68,0x5e6a,0x5e6b,0x5e6c,
+ 0x5e6d,0x5e6e,0x5e70,0x5e80,0x5e8b,0x5e8e,0x5ea2,0x5ea4,0x5ea5,0x5ea8,
+ 0x5eaa,0x5eac,0x5eb1,0x5eb3,0x5ebd,0x5ebe,0x5ebf,0x5ec6,0x5ecc,0x5ecb,
+ 0x5ece,0x5ed1,0x5ed2,0x5ed4,0x5ed5,0x5edc,0x5ede,0x5ee5,0x5eeb,0x5f02,
+ 0x5f06,0x5f07,0x5f08,0x5f0e,0x5f19,0x5f1c,0x5f1d,0x5f21,0x5f22,0x5f23,
+ 0x5f24,0x5f28,0x5f2b,0x5f2c,0x5f2e,0x5f30,0x5f34,0x5f36,0x5f3b,0x5f3d,
+ 0x5f3f,0x5f40,0x5f44,0x5f45,0x5f47,0x5f4d,0x5f50,0x5f54,0x5f58,0x5f5b,
+ 0x5f60,0x5f63,0x5f64,0x5f67
+ },
+ { /* ku 1d */
+ 0x5f6f,0x5f72,0x5f74,0x5f75,0x5f78,0x5f7a,0x5f7d,0x5f7e,0x5f89,0x5f8d,
+ 0x5f8f,0x5f96,0x5f9c,0x5f9d,0x5fa2,0x5fa7,0x5fab,0x5fa4,0x5fac,0x5faf,
+ 0x5fb0,0x5fb1,0x5fb8,0x5fc4,0x5fc7,0x5fc8,0x5fc9,0x5fcb,0x5fd0,0x5fd1,
+ 0x5fd2,0x5fd3,0x5fd4,0x5fde,0x5fe1,0x5fe2,0x5fe8,0x5fe9,0x5fea,0x5fec,
+ 0x5fed,0x5fee,0x5fef,0x5ff2,0x5ff3,0x5ff6,0x5ffa,0x5ffc,0x6007,0x600a,
+ 0x600d,0x6013,0x6014,0x6017,0x6018,0x601a,0x601f,0x6024,0x602d,0x6033,
+ 0x6035,0x6040,0x6047,0x6048,0x6049,0x604c,0x6051,0x6054,0x6056,0x6057,
+ 0x605d,0x6061,0x6067,0x6071,0x607e,0x607f,0x6082,0x6086,0x6088,0x608a,
+ 0x608e,0x6091,0x6093,0x6095,0x6098,0x609d,0x609e,0x60a2,0x60a4,0x60a5,
+ 0x60a8,0x60b0,0x60b1,0x60b7
+ },
+ { /* ku 1e */
+ 0x60bb,0x60be,0x60c2,0x60c4,0x60c8,0x60c9,0x60ca,0x60cb,0x60ce,0x60cf,
+ 0x60d4,0x60d5,0x60d9,0x60db,0x60dd,0x60de,0x60e2,0x60e5,0x60f2,0x60f5,
+ 0x60f8,0x60fc,0x60fd,0x6102,0x6107,0x610a,0x610c,0x6110,0x6111,0x6112,
+ 0x6113,0x6114,0x6116,0x6117,0x6119,0x611c,0x611e,0x6122,0x612a,0x612b,
+ 0x6130,0x6131,0x6135,0x6136,0x6137,0x6139,0x6141,0x6145,0x6146,0x6149,
+ 0x615e,0x6160,0x616c,0x6172,0x6178,0x617b,0x617c,0x617f,0x6180,0x6181,
+ 0x6183,0x6184,0x618b,0x618d,0x6192,0x6193,0x6197,0x6198,0x619c,0x619d,
+ 0x619f,0x61a0,0x61a5,0x61a8,0x61aa,0x61ad,0x61b8,0x61b9,0x61bc,0x61c0,
+ 0x61c1,0x61c2,0x61ce,0x61cf,0x61d5,0x61dc,0x61dd,0x61de,0x61df,0x61e1,
+ 0x61e2,0x61e7,0x61e9,0x61e5
+ },
+ { /* ku 1f */
+ 0x61ec,0x61ed,0x61ef,0x6201,0x6203,0x6204,0x6207,0x6213,0x6215,0x621c,
+ 0x6220,0x6222,0x6223,0x6227,0x6229,0x622b,0x6239,0x623d,0x6242,0x6243,
+ 0x6244,0x6246,0x624c,0x6250,0x6251,0x6252,0x6254,0x6256,0x625a,0x625c,
+ 0x6264,0x626d,0x626f,0x6273,0x627a,0x627d,0x628d,0x628e,0x628f,0x6290,
+ 0x62a6,0x62a8,0x62b3,0x62b6,0x62b7,0x62ba,0x62be,0x62bf,0x62c4,0x62ce,
+ 0x62d5,0x62d6,0x62da,0x62ea,0x62f2,0x62f4,0x62fc,0x62fd,0x6303,0x6304,
+ 0x630a,0x630b,0x630d,0x6310,0x6313,0x6316,0x6318,0x6329,0x632a,0x632d,
+ 0x6335,0x6336,0x6339,0x633c,0x6341,0x6342,0x6343,0x6344,0x6346,0x634a,
+ 0x634b,0x634e,0x6352,0x6353,0x6354,0x6358,0x635b,0x6365,0x6366,0x636c,
+ 0x636d,0x6371,0x6374,0x6375
+ },
+ { /* ku 20 */
+ 0x6378,0x637c,0x637d,0x637f,0x6382,0x6384,0x6387,0x638a,0x6390,0x6394,
+ 0x6395,0x6399,0x639a,0x639e,0x63a4,0x63a6,0x63ad,0x63ae,0x63af,0x63bd,
+ 0x63c1,0x63c5,0x63c8,0x63ce,0x63d1,0x63d3,0x63d4,0x63d5,0x63dc,0x63e0,
+ 0x63e5,0x63ea,0x63ec,0x63f2,0x63f3,0x63f5,0x63f8,0x63f9,0x6409,0x640a,
+ 0x6410,0x6412,0x6414,0x6418,0x641e,0x6420,0x6422,0x6424,0x6425,0x6429,
+ 0x642a,0x642f,0x6430,0x6435,0x643d,0x643f,0x644b,0x644f,0x6451,0x6452,
+ 0x6453,0x6454,0x645a,0x645b,0x645c,0x645d,0x645f,0x6460,0x6461,0x6463,
+ 0x646d,0x6473,0x6474,0x647b,0x647d,0x6485,0x6487,0x648f,0x6490,0x6491,
+ 0x6498,0x6499,0x649b,0x649d,0x649f,0x64a1,0x64a3,0x64a6,0x64a8,0x64ac,
+ 0x64b3,0x64bd,0x64be,0x64bf
+ },
+ { /* ku 21 */
+ 0x64c4,0x64c9,0x64ca,0x64cb,0x64cc,0x64ce,0x64d0,0x64d1,0x64d5,0x64d7,
+ 0x64e4,0x64e5,0x64e9,0x64ea,0x64ed,0x64f0,0x64f5,0x64f7,0x64fb,0x64ff,
+ 0x6501,0x6504,0x6508,0x6509,0x650a,0x650f,0x6513,0x6514,0x6516,0x6519,
+ 0x651b,0x651e,0x651f,0x6522,0x6526,0x6529,0x652e,0x6531,0x653a,0x653c,
+ 0x653d,0x6543,0x6547,0x6549,0x6550,0x6552,0x6554,0x655f,0x6560,0x6567,
+ 0x656b,0x657a,0x657d,0x6581,0x6585,0x658a,0x6592,0x6595,0x6598,0x659d,
+ 0x65a0,0x65a3,0x65a6,0x65ae,0x65b2,0x65b3,0x65b4,0x65bf,0x65c2,0x65c8,
+ 0x65c9,0x65ce,0x65d0,0x65d4,0x65d6,0x65d8,0x65df,0x65f0,0x65f2,0x65f4,
+ 0x65f5,0x65f9,0x65fe,0x65ff,0x6600,0x6604,0x6608,0x6609,0x660d,0x6611,
+ 0x6612,0x6615,0x6616,0x661d
+ },
+ { /* ku 22 */
+ 0x661e,0x6621,0x6622,0x6623,0x6624,0x6626,0x6629,0x662a,0x662b,0x662c,
+ 0x662e,0x6630,0x6631,0x6633,0x6639,0x6637,0x6640,0x6645,0x6646,0x664a,
+ 0x664c,0x6651,0x664e,0x6657,0x6658,0x6659,0x665b,0x665c,0x6660,0x6661,
+ 0x66fb,0x666a,0x666b,0x666c,0x667e,0x6673,0x6675,0x667f,0x6677,0x6678,
+ 0x6679,0x667b,0x6680,0x667c,0x668b,0x668c,0x668d,0x6690,0x6692,0x6699,
+ 0x669a,0x669b,0x669c,0x669f,0x66a0,0x66a4,0x66ad,0x66b1,0x66b2,0x66b5,
+ 0x66bb,0x66bf,0x66c0,0x66c2,0x66c3,0x66c8,0x66cc,0x66ce,0x66cf,0x66d4,
+ 0x66db,0x66df,0x66e8,0x66eb,0x66ec,0x66ee,0x66fa,0x6705,0x6707,0x670e,
+ 0x6713,0x6719,0x671c,0x6720,0x6722,0x6733,0x673e,0x6745,0x6747,0x6748,
+ 0x674c,0x6754,0x6755,0x675d
+ },
+ { /* ku 23 */
+ 0x6766,0x676c,0x676e,0x6774,0x6776,0x677b,0x6781,0x6784,0x678e,0x678f,
+ 0x6791,0x6793,0x6796,0x6798,0x6799,0x679b,0x67b0,0x67b1,0x67b2,0x67b5,
+ 0x67bb,0x67bc,0x67bd,0x67f9,0x67c0,0x67c2,0x67c3,0x67c5,0x67c8,0x67c9,
+ 0x67d2,0x67d7,0x67d9,0x67dc,0x67e1,0x67e6,0x67f0,0x67f2,0x67f6,0x67f7,
+ 0x6852,0x6814,0x6819,0x681d,0x681f,0x6828,0x6827,0x682c,0x682d,0x682f,
+ 0x6830,0x6831,0x6833,0x683b,0x683f,0x6844,0x6845,0x684a,0x684c,0x6855,
+ 0x6857,0x6858,0x685b,0x686b,0x686e,0x686f,0x6870,0x6871,0x6872,0x6875,
+ 0x6879,0x687a,0x687b,0x687c,0x6882,0x6884,0x6886,0x6888,0x6896,0x6898,
+ 0x689a,0x689c,0x68a1,0x68a3,0x68a5,0x68a9,0x68aa,0x68ae,0x68b2,0x68bb,
+ 0x68c5,0x68c8,0x68cc,0x68cf
+ },
+ { /* ku 24 */
+ 0x68d0,0x68d1,0x68d3,0x68d6,0x68d9,0x68dc,0x68dd,0x68e5,0x68e8,0x68ea,
+ 0x68eb,0x68ec,0x68ed,0x68f0,0x68f1,0x68f5,0x68f6,0x68fb,0x68fc,0x68fd,
+ 0x6906,0x6909,0x690a,0x6910,0x6911,0x6913,0x6916,0x6917,0x6931,0x6933,
+ 0x6935,0x6938,0x693b,0x6942,0x6945,0x6949,0x694e,0x6957,0x695b,0x6963,
+ 0x6964,0x6965,0x6966,0x6968,0x6969,0x696c,0x6970,0x6971,0x6972,0x697a,
+ 0x697b,0x697f,0x6980,0x698d,0x6992,0x6996,0x6998,0x69a1,0x69a5,0x69a6,
+ 0x69a8,0x69ab,0x69ad,0x69af,0x69b7,0x69b8,0x69ba,0x69bc,0x69c5,0x69c8,
+ 0x69d1,0x69d6,0x69d7,0x69e2,0x69e5,0x69ee,0x69ef,0x69f1,0x69f3,0x69f5,
+ 0x69fe,0x6a00,0x6a01,0x6a03,0x6a0f,0x6a11,0x6a15,0x6a1a,0x6a1d,0x6a20,
+ 0x6a24,0x6a28,0x6a30,0x6a32
+ },
+ { /* ku 25 */
+ 0x6a34,0x6a37,0x6a3b,0x6a3e,0x6a3f,0x6a45,0x6a46,0x6a49,0x6a4a,0x6a4e,
+ 0x6a50,0x6a51,0x6a52,0x6a55,0x6a56,0x6a5b,0x6a64,0x6a67,0x6a6a,0x6a71,
+ 0x6a73,0x6a7e,0x6a81,0x6a83,0x6a86,0x6a87,0x6a89,0x6a8b,0x6a91,0x6a9b,
+ 0x6a9d,0x6a9e,0x6a9f,0x6aa5,0x6aab,0x6aaf,0x6ab0,0x6ab1,0x6ab4,0x6abd,
+ 0x6abe,0x6abf,0x6ac6,0x6ac9,0x6ac8,0x6acc,0x6ad0,0x6ad4,0x6ad5,0x6ad6,
+ 0x6adc,0x6add,0x6ae4,0x6ae7,0x6aec,0x6af0,0x6af1,0x6af2,0x6afc,0x6afd,
+ 0x6b02,0x6b03,0x6b06,0x6b07,0x6b09,0x6b0f,0x6b10,0x6b11,0x6b17,0x6b1b,
+ 0x6b1e,0x6b24,0x6b28,0x6b2b,0x6b2c,0x6b2f,0x6b35,0x6b36,0x6b3b,0x6b3f,
+ 0x6b46,0x6b4a,0x6b4d,0x6b52,0x6b56,0x6b58,0x6b5d,0x6b60,0x6b67,0x6b6b,
+ 0x6b6e,0x6b70,0x6b75,0x6b7d
+ },
+ { /* ku 26 */
+ 0x6b7e,0x6b82,0x6b85,0x6b97,0x6b9b,0x6b9f,0x6ba0,0x6ba2,0x6ba3,0x6ba8,
+ 0x6ba9,0x6bac,0x6bad,0x6bae,0x6bb0,0x6bb8,0x6bb9,0x6bbd,0x6bbe,0x6bc3,
+ 0x6bc4,0x6bc9,0x6bcc,0x6bd6,0x6bda,0x6be1,0x6be3,0x6be6,0x6be7,0x6bee,
+ 0x6bf1,0x6bf7,0x6bf9,0x6bff,0x6c02,0x6c04,0x6c05,0x6c09,0x6c0d,0x6c0e,
+ 0x6c10,0x6c12,0x6c19,0x6c1f,0x6c26,0x6c27,0x6c28,0x6c2c,0x6c2e,0x6c33,
+ 0x6c35,0x6c36,0x6c3a,0x6c3b,0x6c3f,0x6c4a,0x6c4b,0x6c4d,0x6c4f,0x6c52,
+ 0x6c54,0x6c59,0x6c5b,0x6c5c,0x6c6b,0x6c6d,0x6c6f,0x6c74,0x6c76,0x6c78,
+ 0x6c79,0x6c7b,0x6c85,0x6c86,0x6c87,0x6c89,0x6c94,0x6c95,0x6c97,0x6c98,
+ 0x6c9c,0x6c9f,0x6cb0,0x6cb2,0x6cb4,0x6cc2,0x6cc6,0x6ccd,0x6ccf,0x6cd0,
+ 0x6cd1,0x6cd2,0x6cd4,0x6cd6
+ },
+ { /* ku 27 */
+ 0x6cda,0x6cdc,0x6ce0,0x6ce7,0x6ce9,0x6ceb,0x6cec,0x6cee,0x6cf2,0x6cf4,
+ 0x6d04,0x6d07,0x6d0a,0x6d0e,0x6d0f,0x6d11,0x6d13,0x6d1a,0x6d26,0x6d27,
+ 0x6d28,0x6c67,0x6d2e,0x6d2f,0x6d31,0x6d39,0x6d3c,0x6d3f,0x6d57,0x6d5e,
+ 0x6d5f,0x6d61,0x6d65,0x6d67,0x6d6f,0x6d70,0x6d7c,0x6d82,0x6d87,0x6d91,
+ 0x6d92,0x6d94,0x6d96,0x6d97,0x6d98,0x6daa,0x6dac,0x6db4,0x6db7,0x6db9,
+ 0x6dbd,0x6dbf,0x6dc4,0x6dc8,0x6dca,0x6dce,0x6dcf,0x6dd6,0x6ddb,0x6ddd,
+ 0x6ddf,0x6de0,0x6de2,0x6de5,0x6de9,0x6def,0x6df0,0x6df4,0x6df6,0x6dfc,
+ 0x6e00,0x6e04,0x6e1e,0x6e22,0x6e27,0x6e32,0x6e36,0x6e39,0x6e3b,0x6e3c,
+ 0x6e44,0x6e45,0x6e48,0x6e49,0x6e4b,0x6e4f,0x6e51,0x6e52,0x6e53,0x6e54,
+ 0x6e57,0x6e5c,0x6e5d,0x6e5e
+ },
+ { /* ku 28 */
+ 0x6e62,0x6e63,0x6e68,0x6e73,0x6e7b,0x6e7d,0x6e8d,0x6e93,0x6e99,0x6ea0,
+ 0x6ea7,0x6ead,0x6eae,0x6eb1,0x6eb3,0x6ebb,0x6ebf,0x6ec0,0x6ec1,0x6ec3,
+ 0x6ec7,0x6ec8,0x6eca,0x6ecd,0x6ece,0x6ecf,0x6eeb,0x6eed,0x6eee,0x6ef9,
+ 0x6efb,0x6efd,0x6f04,0x6f08,0x6f0a,0x6f0c,0x6f0d,0x6f16,0x6f18,0x6f1a,
+ 0x6f1b,0x6f26,0x6f29,0x6f2a,0x6f2f,0x6f30,0x6f33,0x6f36,0x6f3b,0x6f3c,
+ 0x6f2d,0x6f4f,0x6f51,0x6f52,0x6f53,0x6f57,0x6f59,0x6f5a,0x6f5d,0x6f5e,
+ 0x6f61,0x6f62,0x6f68,0x6f6c,0x6f7d,0x6f7e,0x6f83,0x6f87,0x6f88,0x6f8b,
+ 0x6f8c,0x6f8d,0x6f90,0x6f92,0x6f93,0x6f94,0x6f96,0x6f9a,0x6f9f,0x6fa0,
+ 0x6fa5,0x6fa6,0x6fa7,0x6fa8,0x6fae,0x6faf,0x6fb0,0x6fb5,0x6fb6,0x6fbc,
+ 0x6fc5,0x6fc7,0x6fc8,0x6fca
+ },
+ { /* ku 29 */
+ 0x6fda,0x6fde,0x6fe8,0x6fe9,0x6ff0,0x6ff5,0x6ff9,0x6ffc,0x6ffd,0x7000,
+ 0x7005,0x7006,0x7007,0x700d,0x7017,0x7020,0x7023,0x702f,0x7034,0x7037,
+ 0x7039,0x703c,0x7043,0x7044,0x7048,0x7049,0x704a,0x704b,0x7054,0x7055,
+ 0x705d,0x705e,0x704e,0x7064,0x7065,0x706c,0x706e,0x7075,0x7076,0x707e,
+ 0x7081,0x7085,0x7086,0x7094,0x7095,0x7096,0x7097,0x7098,0x709b,0x70a4,
+ 0x70ab,0x70b0,0x70b1,0x70b4,0x70b7,0x70ca,0x70d1,0x70d3,0x70d4,0x70d5,
+ 0x70d6,0x70d8,0x70dc,0x70e4,0x70fa,0x7103,0x7104,0x7105,0x7106,0x7107,
+ 0x710b,0x710c,0x710f,0x711e,0x7120,0x712b,0x712d,0x712f,0x7130,0x7131,
+ 0x7138,0x7141,0x7145,0x7146,0x7147,0x714a,0x714b,0x7150,0x7152,0x7157,
+ 0x715a,0x715c,0x715e,0x7160
+ },
+ { /* ku 2a */
+ 0x7168,0x7179,0x7180,0x7185,0x7187,0x718c,0x7192,0x719a,0x719b,0x71a0,
+ 0x71a2,0x71af,0x71b0,0x71b2,0x71b3,0x71ba,0x71bf,0x71c0,0x71c1,0x71c4,
+ 0x71cb,0x71cc,0x71d3,0x71d6,0x71d9,0x71da,0x71dc,0x71f8,0x71fe,0x7200,
+ 0x7207,0x7208,0x7209,0x7213,0x7217,0x721a,0x721d,0x721f,0x7224,0x722b,
+ 0x722f,0x7234,0x7238,0x7239,0x7241,0x7242,0x7243,0x7245,0x724e,0x724f,
+ 0x7250,0x7253,0x7255,0x7256,0x725a,0x725c,0x725e,0x7260,0x7263,0x7268,
+ 0x726b,0x726e,0x726f,0x7271,0x7277,0x7278,0x727b,0x727c,0x727f,0x7284,
+ 0x7289,0x728d,0x728e,0x7293,0x729b,0x72a8,0x72ad,0x72ae,0x72b1,0x72b4,
+ 0x72be,0x72c1,0x72c7,0x72c9,0x72cc,0x72d5,0x72d6,0x72d8,0x72df,0x72e5,
+ 0x72f3,0x72f4,0x72fa,0x72fb
+ },
+ { /* ku 2b */
+ 0x72fe,0x7302,0x7304,0x7305,0x7307,0x730b,0x730d,0x7312,0x7313,0x7318,
+ 0x7319,0x731e,0x7322,0x7324,0x7327,0x7328,0x732c,0x7331,0x7332,0x7335,
+ 0x733a,0x733b,0x733d,0x7343,0x734d,0x7350,0x7352,0x7356,0x7358,0x735d,
+ 0x735e,0x735f,0x7360,0x7366,0x7367,0x7369,0x736b,0x736c,0x736e,0x736f,
+ 0x7371,0x7377,0x7379,0x737c,0x7380,0x7381,0x7383,0x7385,0x7386,0x738e,
+ 0x7390,0x7393,0x7395,0x7397,0x7398,0x739c,0x739e,0x739f,0x73a0,0x73a2,
+ 0x73a5,0x73a6,0x73aa,0x73ab,0x73ad,0x73b5,0x73b7,0x73b9,0x73bc,0x73bd,
+ 0x73bf,0x73c5,0x73c6,0x73c9,0x73cb,0x73cc,0x73cf,0x73d2,0x73d3,0x73d6,
+ 0x73d9,0x73dd,0x73e1,0x73e3,0x73e6,0x73e7,0x73e9,0x73f4,0x73f5,0x73f7,
+ 0x73f9,0x73fa,0x73fb,0x73fd
+ },
+ { /* ku 2c */
+ 0x73ff,0x7400,0x7401,0x7404,0x7407,0x740a,0x7411,0x741a,0x741b,0x7424,
+ 0x7426,0x7428,0x7429,0x742a,0x742b,0x742c,0x742d,0x742e,0x742f,0x7430,
+ 0x7431,0x7439,0x7440,0x7443,0x7444,0x7446,0x7447,0x744b,0x744d,0x7451,
+ 0x7452,0x7457,0x745d,0x7462,0x7466,0x7467,0x7468,0x746b,0x746d,0x746e,
+ 0x7471,0x7472,0x7480,0x7481,0x7485,0x7486,0x7487,0x7489,0x748f,0x7490,
+ 0x7491,0x7492,0x7498,0x7499,0x749a,0x749c,0x749f,0x74a0,0x74a1,0x74a3,
+ 0x74a6,0x74a8,0x74a9,0x74aa,0x74ab,0x74ae,0x74af,0x74b1,0x74b2,0x74b5,
+ 0x74b9,0x74bb,0x74bf,0x74c8,0x74c9,0x74cc,0x74d0,0x74d3,0x74d8,0x74da,
+ 0x74db,0x74de,0x74df,0x74e4,0x74e8,0x74ea,0x74eb,0x74ef,0x74f4,0x74fa,
+ 0x74fb,0x74fc,0x74ff,0x7506
+ },
+ { /* ku 2d */
+ 0x7512,0x7516,0x7517,0x7520,0x7521,0x7524,0x7527,0x7529,0x752a,0x752f,
+ 0x7536,0x7539,0x753d,0x753e,0x753f,0x7540,0x7543,0x7547,0x7548,0x754e,
+ 0x7550,0x7552,0x7557,0x755e,0x755f,0x7561,0x756f,0x7571,0x7579,0x757a,
+ 0x757b,0x757c,0x757d,0x757e,0x7581,0x7585,0x7590,0x7592,0x7593,0x7595,
+ 0x7599,0x759c,0x75a2,0x75a4,0x75b4,0x75ba,0x75bf,0x75c0,0x75c1,0x75c4,
+ 0x75c6,0x75cc,0x75ce,0x75cf,0x75d7,0x75dc,0x75df,0x75e0,0x75e1,0x75e4,
+ 0x75e7,0x75ec,0x75ee,0x75ef,0x75f1,0x75f9,0x7600,0x7602,0x7603,0x7604,
+ 0x7607,0x7608,0x760a,0x760c,0x760f,0x7612,0x7613,0x7615,0x7616,0x7619,
+ 0x761b,0x761c,0x761d,0x761e,0x7623,0x7625,0x7626,0x7629,0x762d,0x7632,
+ 0x7633,0x7635,0x7638,0x7639
+ },
+ { /* ku 2e */
+ 0x763a,0x763c,0x764a,0x7640,0x7641,0x7643,0x7644,0x7645,0x7649,0x764b,
+ 0x7655,0x7659,0x765f,0x7664,0x7665,0x766d,0x766e,0x766f,0x7671,0x7674,
+ 0x7681,0x7685,0x768c,0x768d,0x7695,0x769b,0x769c,0x769d,0x769f,0x76a0,
+ 0x76a2,0x76a3,0x76a4,0x76a5,0x76a6,0x76a7,0x76a8,0x76aa,0x76ad,0x76bd,
+ 0x76c1,0x76c5,0x76c9,0x76cb,0x76cc,0x76ce,0x76d4,0x76d9,0x76e0,0x76e6,
+ 0x76e8,0x76ec,0x76f0,0x76f1,0x76f6,0x76f9,0x76fc,0x7700,0x7706,0x770a,
+ 0x770e,0x7712,0x7714,0x7715,0x7717,0x7719,0x771a,0x771c,0x7722,0x7728,
+ 0x772d,0x772e,0x772f,0x7734,0x7735,0x7736,0x7739,0x773d,0x773e,0x7742,
+ 0x7745,0x7746,0x774a,0x774d,0x774e,0x774f,0x7752,0x7756,0x7757,0x775c,
+ 0x775e,0x775f,0x7760,0x7762
+ },
+ { /* ku 2f */
+ 0x7764,0x7767,0x776a,0x776c,0x7770,0x7772,0x7773,0x7774,0x777a,0x777d,
+ 0x7780,0x7784,0x778c,0x778d,0x7794,0x7795,0x7796,0x779a,0x779f,0x77a2,
+ 0x77a7,0x77aa,0x77ae,0x77af,0x77b1,0x77b5,0x77be,0x77c3,0x77c9,0x77d1,
+ 0x77d2,0x77d5,0x77d9,0x77de,0x77df,0x77e0,0x77e4,0x77e6,0x77ea,0x77ec,
+ 0x77f0,0x77f1,0x77f4,0x77f8,0x77fb,0x7805,0x7806,0x7809,0x780d,0x780e,
+ 0x7811,0x781d,0x7821,0x7822,0x7823,0x782d,0x782e,0x7830,0x7835,0x7837,
+ 0x7843,0x7844,0x7847,0x7848,0x784c,0x784e,0x7852,0x785c,0x785e,0x7860,
+ 0x7861,0x7863,0x7864,0x7868,0x786a,0x786e,0x787a,0x787e,0x788a,0x788f,
+ 0x7894,0x7898,0x78a1,0x789d,0x789e,0x789f,0x78a4,0x78a8,0x78ac,0x78ad,
+ 0x78b0,0x78b1,0x78b2,0x78b3
+ },
+ { /* ku 30 */
+ 0x78bb,0x78bd,0x78bf,0x78c7,0x78c8,0x78c9,0x78cc,0x78ce,0x78d2,0x78d3,
+ 0x78d5,0x78d6,0x78e4,0x78db,0x78df,0x78e0,0x78e1,0x78e6,0x78ea,0x78f2,
+ 0x78f3,0x7900,0x78f6,0x78f7,0x78fa,0x78fb,0x78ff,0x7906,0x790c,0x7910,
+ 0x791a,0x791c,0x791e,0x791f,0x7920,0x7925,0x7927,0x7929,0x792d,0x7931,
+ 0x7934,0x7935,0x793b,0x793d,0x793f,0x7944,0x7945,0x7946,0x794a,0x794b,
+ 0x794f,0x7951,0x7954,0x7958,0x795b,0x795c,0x7967,0x7969,0x796b,0x7972,
+ 0x7979,0x797b,0x797c,0x797e,0x798b,0x798c,0x7991,0x7993,0x7994,0x7995,
+ 0x7996,0x7998,0x799b,0x799c,0x79a1,0x79a8,0x79a9,0x79ab,0x79af,0x79b1,
+ 0x79b4,0x79b8,0x79bb,0x79c2,0x79c4,0x79c7,0x79c8,0x79ca,0x79cf,0x79d4,
+ 0x79d6,0x79da,0x79dd,0x79de
+ },
+ { /* ku 31 */
+ 0x79e0,0x79e2,0x79e5,0x79ea,0x79eb,0x79ed,0x79f1,0x79f8,0x79fc,0x7a02,
+ 0x7a03,0x7a07,0x7a09,0x7a0a,0x7a0c,0x7a11,0x7a15,0x7a1b,0x7a1e,0x7a21,
+ 0x7a27,0x7a2b,0x7a2d,0x7a2f,0x7a30,0x7a34,0x7a35,0x7a38,0x7a39,0x7a3a,
+ 0x7a44,0x7a45,0x7a47,0x7a48,0x7a4c,0x7a55,0x7a56,0x7a59,0x7a5c,0x7a5d,
+ 0x7a5f,0x7a60,0x7a65,0x7a67,0x7a6a,0x7a6d,0x7a75,0x7a78,0x7a7e,0x7a80,
+ 0x7a82,0x7a85,0x7a86,0x7a8a,0x7a8b,0x7a90,0x7a91,0x7a94,0x7a9e,0x7aa0,
+ 0x7aa3,0x7aac,0x7ab3,0x7ab5,0x7ab9,0x7abb,0x7abc,0x7ac6,0x7ac9,0x7acc,
+ 0x7ace,0x7ad1,0x7adb,0x7ae8,0x7ae9,0x7aeb,0x7aec,0x7af1,0x7af4,0x7afb,
+ 0x7afd,0x7afe,0x7b07,0x7b14,0x7b1f,0x7b23,0x7b27,0x7b29,0x7b2a,0x7b2b,
+ 0x7b2d,0x7b2e,0x7b2f,0x7b30
+ },
+ { /* ku 32 */
+ 0x7b31,0x7b34,0x7b3d,0x7b3f,0x7b40,0x7b41,0x7b47,0x7b4e,0x7b55,0x7b60,
+ 0x7b64,0x7b66,0x7b69,0x7b6a,0x7b6d,0x7b6f,0x7b72,0x7b73,0x7b77,0x7b84,
+ 0x7b89,0x7b8e,0x7b90,0x7b91,0x7b96,0x7b9b,0x7b9e,0x7ba0,0x7ba5,0x7bac,
+ 0x7baf,0x7bb0,0x7bb2,0x7bb5,0x7bb6,0x7bba,0x7bbb,0x7bbc,0x7bbd,0x7bc2,
+ 0x7bc5,0x7bc8,0x7bca,0x7bd4,0x7bd6,0x7bd7,0x7bd9,0x7bda,0x7bdb,0x7be8,
+ 0x7bea,0x7bf2,0x7bf4,0x7bf5,0x7bf8,0x7bf9,0x7bfa,0x7bfc,0x7bfe,0x7c01,
+ 0x7c02,0x7c03,0x7c04,0x7c06,0x7c09,0x7c0b,0x7c0c,0x7c0e,0x7c0f,0x7c19,
+ 0x7c1b,0x7c20,0x7c25,0x7c26,0x7c28,0x7c2c,0x7c31,0x7c33,0x7c34,0x7c36,
+ 0x7c39,0x7c3a,0x7c46,0x7c4a,0x7c55,0x7c51,0x7c52,0x7c53,0x7c59,0x7c5a,
+ 0x7c5b,0x7c5c,0x7c5d,0x7c5e
+ },
+ { /* ku 33 */
+ 0x7c61,0x7c63,0x7c67,0x7c69,0x7c6d,0x7c6e,0x7c70,0x7c72,0x7c79,0x7c7c,
+ 0x7c7d,0x7c86,0x7c87,0x7c8f,0x7c94,0x7c9e,0x7ca0,0x7ca6,0x7cb0,0x7cb6,
+ 0x7cb7,0x7cba,0x7cbb,0x7cbc,0x7cbf,0x7cc4,0x7cc7,0x7cc8,0x7cc9,0x7ccd,
+ 0x7ccf,0x7cd3,0x7cd4,0x7cd5,0x7cd7,0x7cd9,0x7cda,0x7cdd,0x7ce6,0x7ce9,
+ 0x7ceb,0x7cf5,0x7d03,0x7d07,0x7d08,0x7d09,0x7d0f,0x7d11,0x7d12,0x7d13,
+ 0x7d16,0x7d1d,0x7d1e,0x7d23,0x7d26,0x7d2a,0x7d2d,0x7d31,0x7d3c,0x7d3d,
+ 0x7d3e,0x7d40,0x7d41,0x7d47,0x7d48,0x7d4d,0x7d51,0x7d53,0x7d57,0x7d59,
+ 0x7d5a,0x7d5c,0x7d5d,0x7d65,0x7d67,0x7d6a,0x7d70,0x7d78,0x7d7a,0x7d7b,
+ 0x7d7f,0x7d81,0x7d82,0x7d83,0x7d85,0x7d86,0x7d88,0x7d8b,0x7d8c,0x7d8d,
+ 0x7d91,0x7d96,0x7d97,0x7d9d
+ },
+ { /* ku 34 */
+ 0x7d9e,0x7da6,0x7da7,0x7daa,0x7db3,0x7db6,0x7db7,0x7db9,0x7dc2,0x7dc3,
+ 0x7dc4,0x7dc5,0x7dc6,0x7dcc,0x7dcd,0x7dce,0x7dd7,0x7dd9,0x7e00,0x7de2,
+ 0x7de5,0x7de6,0x7dea,0x7deb,0x7ded,0x7df1,0x7df5,0x7df6,0x7df9,0x7dfa,
+ 0x7e08,0x7e10,0x7e11,0x7e15,0x7e17,0x7e1c,0x7e1d,0x7e20,0x7e27,0x7e28,
+ 0x7e2c,0x7e2d,0x7e2f,0x7e33,0x7e36,0x7e3f,0x7e44,0x7e45,0x7e47,0x7e4e,
+ 0x7e50,0x7e52,0x7e58,0x7e5f,0x7e61,0x7e62,0x7e65,0x7e6b,0x7e6e,0x7e6f,
+ 0x7e73,0x7e78,0x7e7e,0x7e81,0x7e86,0x7e87,0x7e8a,0x7e8d,0x7e91,0x7e95,
+ 0x7e98,0x7e9a,0x7e9d,0x7e9e,0x7f3c,0x7f3b,0x7f3d,0x7f3e,0x7f3f,0x7f43,
+ 0x7f44,0x7f47,0x7f4f,0x7f52,0x7f53,0x7f5b,0x7f5c,0x7f5d,0x7f61,0x7f63,
+ 0x7f64,0x7f65,0x7f66,0x7f6d
+ },
+ { /* ku 35 */
+ 0x7f71,0x7f7d,0x7f7e,0x7f7f,0x7f80,0x7f8b,0x7f8d,0x7f8f,0x7f90,0x7f91,
+ 0x7f96,0x7f97,0x7f9c,0x7fa1,0x7fa2,0x7fa6,0x7faa,0x7fad,0x7fb4,0x7fbc,
+ 0x7fbf,0x7fc0,0x7fc3,0x7fc8,0x7fce,0x7fcf,0x7fdb,0x7fdf,0x7fe3,0x7fe5,
+ 0x7fe8,0x7fec,0x7fee,0x7fef,0x7ff2,0x7ffa,0x7ffd,0x7ffe,0x7fff,0x8007,
+ 0x8008,0x800a,0x800d,0x800e,0x800f,0x8011,0x8013,0x8014,0x8016,0x801d,
+ 0x801e,0x801f,0x8020,0x8024,0x8026,0x802c,0x802e,0x8030,0x8034,0x8035,
+ 0x8037,0x8039,0x803a,0x803c,0x803e,0x8040,0x8044,0x8060,0x8064,0x8066,
+ 0x806d,0x8071,0x8075,0x8081,0x8088,0x808e,0x809c,0x809e,0x80a6,0x80a7,
+ 0x80ab,0x80b8,0x80b9,0x80c8,0x80cd,0x80cf,0x80d2,0x80d4,0x80d5,0x80d7,
+ 0x80d8,0x80e0,0x80ed,0x80ee
+ },
+ { /* ku 36 */
+ 0x80f0,0x80f2,0x80f3,0x80f6,0x80f9,0x80fa,0x80fe,0x8103,0x810b,0x8116,
+ 0x8117,0x8118,0x811c,0x811e,0x8120,0x8124,0x8127,0x812c,0x8130,0x8135,
+ 0x813a,0x813c,0x8145,0x8147,0x814a,0x814c,0x8152,0x8157,0x8160,0x8161,
+ 0x8167,0x8168,0x8169,0x816d,0x816f,0x8177,0x8181,0x8190,0x8184,0x8185,
+ 0x8186,0x818b,0x818e,0x8196,0x8198,0x819b,0x819e,0x81a2,0x81ae,0x81b2,
+ 0x81b4,0x81bb,0x81cb,0x81c3,0x81c5,0x81ca,0x81ce,0x81cf,0x81d5,0x81d7,
+ 0x81db,0x81dd,0x81de,0x81e1,0x81e4,0x81eb,0x81ec,0x81f0,0x81f1,0x81f2,
+ 0x81f5,0x81f6,0x81f8,0x81f9,0x81fd,0x81ff,0x8200,0x8203,0x820f,0x8213,
+ 0x8214,0x8219,0x821a,0x821d,0x8221,0x8222,0x8228,0x8232,0x8234,0x823a,
+ 0x8243,0x8244,0x8245,0x8246
+ },
+ { /* ku 37 */
+ 0x824b,0x824e,0x824f,0x8251,0x8256,0x825c,0x8260,0x8263,0x8267,0x826d,
+ 0x8274,0x827b,0x827d,0x827f,0x8280,0x8281,0x8283,0x8284,0x8287,0x8289,
+ 0x828a,0x828e,0x8291,0x8294,0x8296,0x8298,0x829a,0x829b,0x82a0,0x82a1,
+ 0x82a3,0x82a4,0x82a7,0x82a8,0x82a9,0x82aa,0x82ae,0x82b0,0x82b2,0x82b4,
+ 0x82b7,0x82ba,0x82bc,0x82be,0x82bf,0x82c6,0x82d0,0x82d5,0x82da,0x82e0,
+ 0x82e2,0x82e4,0x82e8,0x82ea,0x82ed,0x82ef,0x82f6,0x82f7,0x82fd,0x82fe,
+ 0x8300,0x8301,0x8307,0x8308,0x830a,0x830b,0x8354,0x831b,0x831d,0x831e,
+ 0x831f,0x8321,0x8322,0x832c,0x832d,0x832e,0x8330,0x8333,0x8337,0x833a,
+ 0x833c,0x833d,0x8342,0x8343,0x8344,0x8347,0x834d,0x834e,0x8351,0x8355,
+ 0x8356,0x8357,0x8370,0x8378
+ },
+ { /* ku 38 */
+ 0x837d,0x837f,0x8380,0x8382,0x8384,0x8386,0x838d,0x8392,0x8394,0x8395,
+ 0x8398,0x8399,0x839b,0x839c,0x839d,0x83a6,0x83a7,0x83a9,0x83ac,0x83be,
+ 0x83bf,0x83c0,0x83c7,0x83c9,0x83cf,0x83d0,0x83d1,0x83d4,0x83dd,0x8353,
+ 0x83e8,0x83ea,0x83f6,0x83f8,0x83f9,0x83fc,0x8401,0x8406,0x840a,0x840f,
+ 0x8411,0x8415,0x8419,0x83ad,0x842f,0x8439,0x8445,0x8447,0x8448,0x844a,
+ 0x844d,0x844f,0x8451,0x8452,0x8456,0x8458,0x8459,0x845a,0x845c,0x8460,
+ 0x8464,0x8465,0x8467,0x846a,0x8470,0x8473,0x8474,0x8476,0x8478,0x847c,
+ 0x847d,0x8481,0x8485,0x8492,0x8493,0x8495,0x849e,0x84a6,0x84a8,0x84a9,
+ 0x84aa,0x84af,0x84b1,0x84b4,0x84ba,0x84bd,0x84be,0x84c0,0x84c2,0x84c7,
+ 0x84c8,0x84cc,0x84cf,0x84d3
+ },
+ { /* ku 39 */
+ 0x84dc,0x84e7,0x84ea,0x84ef,0x84f0,0x84f1,0x84f2,0x84f7,0x8532,0x84fa,
+ 0x84fb,0x84fd,0x8502,0x8503,0x8507,0x850c,0x850e,0x8510,0x851c,0x851e,
+ 0x8522,0x8523,0x8524,0x8525,0x8527,0x852a,0x852b,0x852f,0x8533,0x8534,
+ 0x8536,0x853f,0x8546,0x854f,0x8550,0x8551,0x8552,0x8553,0x8556,0x8559,
+ 0x855c,0x855d,0x855e,0x855f,0x8560,0x8561,0x8562,0x8564,0x856b,0x856f,
+ 0x8579,0x857a,0x857b,0x857d,0x857f,0x8581,0x8585,0x8586,0x8589,0x858b,
+ 0x858c,0x858f,0x8593,0x8598,0x859d,0x859f,0x85a0,0x85a2,0x85a5,0x85a7,
+ 0x85b4,0x85b6,0x85b7,0x85b8,0x85bc,0x85bd,0x85be,0x85bf,0x85c2,0x85c7,
+ 0x85ca,0x85cb,0x85ce,0x85ad,0x85d8,0x85da,0x85df,0x85e0,0x85e6,0x85e8,
+ 0x85ed,0x85f3,0x85f6,0x85fc
+ },
+ { /* ku 3a */
+ 0x85ff,0x8600,0x8604,0x8605,0x860d,0x860e,0x8610,0x8611,0x8612,0x8618,
+ 0x8619,0x861b,0x861e,0x8621,0x8627,0x8629,0x8636,0x8638,0x863a,0x863c,
+ 0x863d,0x8640,0x8642,0x8646,0x8652,0x8653,0x8656,0x8657,0x8658,0x8659,
+ 0x865d,0x8660,0x8661,0x8662,0x8663,0x8664,0x8669,0x866c,0x866f,0x8675,
+ 0x8676,0x8677,0x867a,0x868d,0x8691,0x8696,0x8698,0x869a,0x869c,0x86a1,
+ 0x86a6,0x86a7,0x86a8,0x86ad,0x86b1,0x86b3,0x86b4,0x86b5,0x86b7,0x86b8,
+ 0x86b9,0x86bf,0x86c0,0x86c1,0x86c3,0x86c5,0x86d1,0x86d2,0x86d5,0x86d7,
+ 0x86da,0x86dc,0x86e0,0x86e3,0x86e5,0x86e7,0x8688,0x86fa,0x86fc,0x86fd,
+ 0x8704,0x8705,0x8707,0x870b,0x870e,0x870f,0x8710,0x8713,0x8714,0x8719,
+ 0x871e,0x871f,0x8721,0x8723
+ },
+ { /* ku 3b */
+ 0x8728,0x872e,0x872f,0x8731,0x8732,0x8739,0x873a,0x873c,0x873d,0x873e,
+ 0x8740,0x8743,0x8745,0x874d,0x8758,0x875d,0x8761,0x8764,0x8765,0x876f,
+ 0x8771,0x8772,0x877b,0x8783,0x8784,0x8785,0x8786,0x8787,0x8788,0x8789,
+ 0x878b,0x878c,0x8790,0x8793,0x8795,0x8797,0x8798,0x8799,0x879e,0x87a0,
+ 0x87a3,0x87a7,0x87ac,0x87ad,0x87ae,0x87b1,0x87b5,0x87be,0x87bf,0x87c1,
+ 0x87c8,0x87c9,0x87ca,0x87ce,0x87d5,0x87d6,0x87d9,0x87da,0x87dc,0x87df,
+ 0x87e2,0x87e3,0x87e4,0x87ea,0x87eb,0x87ed,0x87f1,0x87f3,0x87f8,0x87fa,
+ 0x87ff,0x8801,0x8803,0x8806,0x8809,0x880a,0x880b,0x8810,0x8819,0x8812,
+ 0x8813,0x8814,0x8818,0x881a,0x881b,0x881c,0x881e,0x881f,0x8828,0x882d,
+ 0x882e,0x8830,0x8832,0x8835
+ },
+ { /* ku 3c */
+ 0x883a,0x883c,0x8841,0x8843,0x8845,0x8848,0x8849,0x884a,0x884b,0x884e,
+ 0x8851,0x8855,0x8856,0x8858,0x885a,0x885c,0x885f,0x8860,0x8864,0x8869,
+ 0x8871,0x8879,0x887b,0x8880,0x8898,0x889a,0x889b,0x889c,0x889f,0x88a0,
+ 0x88a8,0x88aa,0x88ba,0x88bd,0x88be,0x88c0,0x88ca,0x88cb,0x88cc,0x88cd,
+ 0x88ce,0x88d1,0x88d2,0x88d3,0x88db,0x88de,0x88e7,0x88ef,0x88f0,0x88f1,
+ 0x88f5,0x88f7,0x8901,0x8906,0x890d,0x890e,0x890f,0x8915,0x8916,0x8918,
+ 0x8919,0x891a,0x891c,0x8920,0x8926,0x8927,0x8928,0x8930,0x8931,0x8932,
+ 0x8935,0x8939,0x893a,0x893e,0x8940,0x8942,0x8945,0x8946,0x8949,0x894f,
+ 0x8952,0x8957,0x895a,0x895b,0x895c,0x8961,0x8962,0x8963,0x896b,0x896e,
+ 0x8970,0x8973,0x8975,0x897a
+ },
+ { /* ku 3d */
+ 0x897b,0x897c,0x897d,0x8989,0x898d,0x8990,0x8994,0x8995,0x899b,0x899c,
+ 0x899f,0x89a0,0x89a5,0x89b0,0x89b4,0x89b5,0x89b6,0x89b7,0x89bc,0x89d4,
+ 0x89d5,0x89d6,0x89d7,0x89d8,0x89e5,0x89e9,0x89eb,0x89ed,0x89f1,0x89f3,
+ 0x89f6,0x89f9,0x89fd,0x89ff,0x8a04,0x8a05,0x8a07,0x8a0f,0x8a11,0x8a12,
+ 0x8a14,0x8a15,0x8a1e,0x8a20,0x8a22,0x8a24,0x8a26,0x8a2b,0x8a2c,0x8a2f,
+ 0x8a35,0x8a37,0x8a3d,0x8a3e,0x8a40,0x8a43,0x8a45,0x8a47,0x8a49,0x8a4d,
+ 0x8a4e,0x8a53,0x8a56,0x8a57,0x8a58,0x8a5c,0x8a5d,0x8a61,0x8a65,0x8a67,
+ 0x8a75,0x8a76,0x8a77,0x8a79,0x8a7a,0x8a7b,0x8a7e,0x8a7f,0x8a80,0x8a83,
+ 0x8a86,0x8a8b,0x8a8f,0x8a90,0x8a92,0x8a96,0x8a97,0x8a99,0x8a9f,0x8aa7,
+ 0x8aa9,0x8aae,0x8aaf,0x8ab3
+ },
+ { /* ku 3e */
+ 0x8ab6,0x8ab7,0x8abb,0x8abe,0x8ac3,0x8ac6,0x8ac8,0x8ac9,0x8aca,0x8ad1,
+ 0x8ad3,0x8ad4,0x8ad5,0x8ad7,0x8add,0x8adf,0x8aec,0x8af0,0x8af4,0x8af5,
+ 0x8af6,0x8afc,0x8aff,0x8b05,0x8b06,0x8b0b,0x8b11,0x8b1c,0x8b1e,0x8b1f,
+ 0x8b0a,0x8b2d,0x8b30,0x8b37,0x8b3c,0x8b42,0x8b43,0x8b44,0x8b45,0x8b46,
+ 0x8b48,0x8b52,0x8b53,0x8b54,0x8b59,0x8b4d,0x8b5e,0x8b63,0x8b6d,0x8b76,
+ 0x8b78,0x8b79,0x8b7c,0x8b7e,0x8b81,0x8b84,0x8b85,0x8b8b,0x8b8d,0x8b8f,
+ 0x8b94,0x8b95,0x8b9c,0x8b9e,0x8b9f,0x8c38,0x8c39,0x8c3d,0x8c3e,0x8c45,
+ 0x8c47,0x8c49,0x8c4b,0x8c4f,0x8c51,0x8c53,0x8c54,0x8c57,0x8c58,0x8c5b,
+ 0x8c5d,0x8c59,0x8c63,0x8c64,0x8c66,0x8c68,0x8c69,0x8c6d,0x8c73,0x8c75,
+ 0x8c76,0x8c7b,0x8c7e,0x8c86
+ },
+ { /* ku 3f */
+ 0x8c87,0x8c8b,0x8c90,0x8c92,0x8c93,0x8c99,0x8c9b,0x8c9c,0x8ca4,0x8cb9,
+ 0x8cba,0x8cc5,0x8cc6,0x8cc9,0x8ccb,0x8ccf,0x8cd6,0x8cd5,0x8cd9,0x8cdd,
+ 0x8ce1,0x8ce8,0x8cec,0x8cef,0x8cf0,0x8cf2,0x8cf5,0x8cf7,0x8cf8,0x8cfe,
+ 0x8cff,0x8d01,0x8d03,0x8d09,0x8d12,0x8d17,0x8d1b,0x8d65,0x8d69,0x8d6c,
+ 0x8d6e,0x8d7f,0x8d82,0x8d84,0x8d88,0x8d8d,0x8d90,0x8d91,0x8d95,0x8d9e,
+ 0x8d9f,0x8da0,0x8da6,0x8dab,0x8dac,0x8daf,0x8db2,0x8db5,0x8db7,0x8db9,
+ 0x8dbb,0x8dc0,0x8dc5,0x8dc6,0x8dc7,0x8dc8,0x8dca,0x8dce,0x8dd1,0x8dd4,
+ 0x8dd5,0x8dd7,0x8dd9,0x8de4,0x8de5,0x8de7,0x8dec,0x8df0,0x8dbc,0x8df1,
+ 0x8df2,0x8df4,0x8dfd,0x8e01,0x8e04,0x8e05,0x8e06,0x8e0b,0x8e11,0x8e14,
+ 0x8e16,0x8e20,0x8e21,0x8e22
+ },
+ { /* ku 40 */
+ 0x8e23,0x8e26,0x8e27,0x8e31,0x8e33,0x8e36,0x8e37,0x8e38,0x8e39,0x8e3d,
+ 0x8e40,0x8e41,0x8e4b,0x8e4d,0x8e4e,0x8e4f,0x8e54,0x8e5b,0x8e5c,0x8e5d,
+ 0x8e5e,0x8e61,0x8e62,0x8e69,0x8e6c,0x8e6d,0x8e6f,0x8e70,0x8e71,0x8e79,
+ 0x8e7a,0x8e7b,0x8e82,0x8e83,0x8e89,0x8e90,0x8e92,0x8e95,0x8e9a,0x8e9b,
+ 0x8e9d,0x8e9e,0x8ea2,0x8ea7,0x8ea9,0x8ead,0x8eae,0x8eb3,0x8eb5,0x8eba,
+ 0x8ebb,0x8ec0,0x8ec1,0x8ec3,0x8ec4,0x8ec7,0x8ecf,0x8ed1,0x8ed4,0x8edc,
+ 0x8ee8,0x8eee,0x8ef0,0x8ef1,0x8ef7,0x8ef9,0x8efa,0x8eed,0x8f00,0x8f02,
+ 0x8f07,0x8f08,0x8f0f,0x8f10,0x8f16,0x8f17,0x8f18,0x8f1e,0x8f20,0x8f21,
+ 0x8f23,0x8f25,0x8f27,0x8f28,0x8f2c,0x8f2d,0x8f2e,0x8f34,0x8f35,0x8f36,
+ 0x8f37,0x8f3a,0x8f40,0x8f41
+ },
+ { /* ku 41 */
+ 0x8f43,0x8f47,0x8f4f,0x8f51,0x8f52,0x8f53,0x8f54,0x8f55,0x8f58,0x8f5d,
+ 0x8f5e,0x8f65,0x8f9d,0x8fa0,0x8fa1,0x8fa4,0x8fa5,0x8fa6,0x8fb5,0x8fb6,
+ 0x8fb8,0x8fbe,0x8fc0,0x8fc1,0x8fc6,0x8fca,0x8fcb,0x8fcd,0x8fd0,0x8fd2,
+ 0x8fd3,0x8fd5,0x8fe0,0x8fe3,0x8fe4,0x8fe8,0x8fee,0x8ff1,0x8ff5,0x8ff6,
+ 0x8ffb,0x8ffe,0x9002,0x9004,0x9008,0x900c,0x9018,0x901b,0x9028,0x9029,
+ 0x902f,0x902a,0x902c,0x902d,0x9033,0x9034,0x9037,0x903f,0x9043,0x9044,
+ 0x904c,0x905b,0x905d,0x9062,0x9066,0x9067,0x906c,0x9070,0x9074,0x9079,
+ 0x9085,0x9088,0x908b,0x908c,0x908e,0x9090,0x9095,0x9097,0x9098,0x9099,
+ 0x909b,0x90a0,0x90a1,0x90a2,0x90a5,0x90b0,0x90b2,0x90b3,0x90b4,0x90b6,
+ 0x90bd,0x90cc,0x90be,0x90c3
+ },
+ { /* ku 42 */
+ 0x90c4,0x90c5,0x90c7,0x90c8,0x90d5,0x90d7,0x90d8,0x90d9,0x90dc,0x90dd,
+ 0x90df,0x90e5,0x90d2,0x90f6,0x90eb,0x90ef,0x90f0,0x90f4,0x90fe,0x90ff,
+ 0x9100,0x9104,0x9105,0x9106,0x9108,0x910d,0x9110,0x9114,0x9116,0x9117,
+ 0x9118,0x911a,0x911c,0x911e,0x9120,0x9125,0x9122,0x9123,0x9127,0x9129,
+ 0x912e,0x912f,0x9131,0x9134,0x9136,0x9137,0x9139,0x913a,0x913c,0x913d,
+ 0x9143,0x9147,0x9148,0x914f,0x9153,0x9157,0x9159,0x915a,0x915b,0x9161,
+ 0x9164,0x9167,0x916d,0x9174,0x9179,0x917a,0x917b,0x9181,0x9183,0x9185,
+ 0x9186,0x918a,0x918e,0x9191,0x9193,0x9194,0x9195,0x9198,0x919e,0x91a1,
+ 0x91a6,0x91a8,0x91ac,0x91ad,0x91ae,0x91b0,0x91b1,0x91b2,0x91b3,0x91b6,
+ 0x91bb,0x91bc,0x91bd,0x91bf
+ },
+ { /* ku 43 */
+ 0x91c2,0x91c3,0x91c5,0x91d3,0x91d4,0x91d7,0x91d9,0x91da,0x91de,0x91e4,
+ 0x91e5,0x91e9,0x91ea,0x91ec,0x91ed,0x91ee,0x91ef,0x91f0,0x91f1,0x91f7,
+ 0x91f9,0x91fb,0x91fd,0x9200,0x9201,0x9204,0x9205,0x9206,0x9207,0x9209,
+ 0x920a,0x920c,0x9210,0x9212,0x9213,0x9216,0x9218,0x921c,0x921d,0x9223,
+ 0x9224,0x9225,0x9226,0x9228,0x922e,0x922f,0x9230,0x9233,0x9235,0x9236,
+ 0x9238,0x9239,0x923a,0x923c,0x923e,0x9240,0x9242,0x9243,0x9246,0x9247,
+ 0x924a,0x924d,0x924e,0x924f,0x9251,0x9258,0x9259,0x925c,0x925d,0x9260,
+ 0x9261,0x9265,0x9267,0x9268,0x9269,0x926e,0x926f,0x9270,0x9275,0x9276,
+ 0x9277,0x9278,0x9279,0x927b,0x927c,0x927d,0x927f,0x9288,0x9289,0x928a,
+ 0x928d,0x928e,0x9292,0x9297
+ },
+ { /* ku 44 */
+ 0x9299,0x929f,0x92a0,0x92a4,0x92a5,0x92a7,0x92a8,0x92ab,0x92af,0x92b2,
+ 0x92b6,0x92b8,0x92ba,0x92bb,0x92bc,0x92bd,0x92bf,0x92c0,0x92c1,0x92c2,
+ 0x92c3,0x92c5,0x92c6,0x92c7,0x92c8,0x92cb,0x92cc,0x92cd,0x92ce,0x92d0,
+ 0x92d3,0x92d5,0x92d7,0x92d8,0x92d9,0x92dc,0x92dd,0x92df,0x92e0,0x92e1,
+ 0x92e3,0x92e5,0x92e7,0x92e8,0x92ec,0x92ee,0x92f0,0x92f9,0x92fb,0x92ff,
+ 0x9300,0x9302,0x9308,0x930d,0x9311,0x9314,0x9315,0x931c,0x931d,0x931e,
+ 0x931f,0x9321,0x9324,0x9325,0x9327,0x9329,0x932a,0x9333,0x9334,0x9336,
+ 0x9337,0x9347,0x9348,0x9349,0x9350,0x9351,0x9352,0x9355,0x9357,0x9358,
+ 0x935a,0x935e,0x9364,0x9365,0x9367,0x9369,0x936a,0x936d,0x936f,0x9370,
+ 0x9371,0x9373,0x9374,0x9376
+ },
+ { /* ku 45 */
+ 0x937a,0x937d,0x937f,0x9380,0x9381,0x9382,0x9388,0x938a,0x938b,0x938d,
+ 0x938f,0x9392,0x9395,0x9398,0x939b,0x939e,0x93a1,0x93a3,0x93a4,0x93a6,
+ 0x93a8,0x93ab,0x93b4,0x93b5,0x93b6,0x93ba,0x93a9,0x93c1,0x93c4,0x93c5,
+ 0x93c6,0x93c7,0x93c9,0x93ca,0x93cb,0x93cc,0x93cd,0x93d3,0x93d9,0x93dc,
+ 0x93de,0x93df,0x93e2,0x93e6,0x93e7,0x93f9,0x93f7,0x93f8,0x93fa,0x93fb,
+ 0x93fd,0x9401,0x9402,0x9404,0x9408,0x9409,0x940d,0x940e,0x940f,0x9415,
+ 0x9416,0x9417,0x941f,0x942e,0x942f,0x9431,0x9432,0x9433,0x9434,0x943b,
+ 0x943f,0x943d,0x9443,0x9445,0x9448,0x944a,0x944c,0x9455,0x9459,0x945c,
+ 0x945f,0x9461,0x9463,0x9468,0x946b,0x946d,0x946e,0x946f,0x9471,0x9472,
+ 0x9484,0x9483,0x9578,0x9579
+ },
+ { /* ku 46 */
+ 0x957e,0x9584,0x9588,0x958c,0x958d,0x958e,0x959d,0x959e,0x959f,0x95a1,
+ 0x95a6,0x95a9,0x95ab,0x95ac,0x95b4,0x95b6,0x95ba,0x95bd,0x95bf,0x95c6,
+ 0x95c8,0x95c9,0x95cb,0x95d0,0x95d1,0x95d2,0x95d3,0x95d9,0x95da,0x95dd,
+ 0x95de,0x95df,0x95e0,0x95e4,0x95e6,0x961d,0x961e,0x9622,0x9624,0x9625,
+ 0x9626,0x962c,0x9631,0x9633,0x9637,0x9638,0x9639,0x963a,0x963c,0x963d,
+ 0x9641,0x9652,0x9654,0x9656,0x9657,0x9658,0x9661,0x966e,0x9674,0x967b,
+ 0x967c,0x967e,0x967f,0x9681,0x9682,0x9683,0x9684,0x9689,0x9691,0x9696,
+ 0x969a,0x969d,0x969f,0x96a4,0x96a5,0x96a6,0x96a9,0x96ae,0x96af,0x96b3,
+ 0x96ba,0x96ca,0x96d2,0x5db2,0x96d8,0x96da,0x96dd,0x96de,0x96df,0x96e9,
+ 0x96ef,0x96f1,0x96fa,0x9702
+ },
+ { /* ku 47 */
+ 0x9703,0x9705,0x9709,0x971a,0x971b,0x971d,0x9721,0x9722,0x9723,0x9728,
+ 0x9731,0x9733,0x9741,0x9743,0x974a,0x974e,0x974f,0x9755,0x9757,0x9758,
+ 0x975a,0x975b,0x9763,0x9767,0x976a,0x976e,0x9773,0x9776,0x9777,0x9778,
+ 0x977b,0x977d,0x977f,0x9780,0x9789,0x9795,0x9796,0x9797,0x9799,0x979a,
+ 0x979e,0x979f,0x97a2,0x97ac,0x97ae,0x97b1,0x97b2,0x97b5,0x97b6,0x97b8,
+ 0x97b9,0x97ba,0x97bc,0x97be,0x97bf,0x97c1,0x97c4,0x97c5,0x97c7,0x97c9,
+ 0x97ca,0x97cc,0x97cd,0x97ce,0x97d0,0x97d1,0x97d4,0x97d7,0x97d8,0x97d9,
+ 0x97dd,0x97de,0x97e0,0x97db,0x97e1,0x97e4,0x97ef,0x97f1,0x97f4,0x97f7,
+ 0x97f8,0x97fa,0x9807,0x980a,0x9819,0x980d,0x980e,0x9814,0x9816,0x981c,
+ 0x981e,0x9820,0x9823,0x9826
+ },
+ { /* ku 48 */
+ 0x982b,0x982e,0x982f,0x9830,0x9832,0x9833,0x9835,0x9825,0x983e,0x9844,
+ 0x9847,0x984a,0x9851,0x9852,0x9853,0x9856,0x9857,0x9859,0x985a,0x9862,
+ 0x9863,0x9865,0x9866,0x986a,0x986c,0x98ab,0x98ad,0x98ae,0x98b0,0x98b4,
+ 0x98b7,0x98b8,0x98ba,0x98bb,0x98bf,0x98c2,0x98c5,0x98c8,0x98cc,0x98e1,
+ 0x98e3,0x98e5,0x98e6,0x98e7,0x98ea,0x98f3,0x98f6,0x9902,0x9907,0x9908,
+ 0x9911,0x9915,0x9916,0x9917,0x991a,0x991b,0x991c,0x991f,0x9922,0x9926,
+ 0x9927,0x992b,0x9931,0x9932,0x9933,0x9934,0x9935,0x9939,0x993a,0x993b,
+ 0x993c,0x9940,0x9941,0x9946,0x9947,0x9948,0x994d,0x994e,0x9954,0x9958,
+ 0x9959,0x995b,0x995c,0x995e,0x995f,0x9960,0x999b,0x999d,0x999f,0x99a6,
+ 0x99b0,0x99b1,0x99b2,0x99b5
+ },
+ { /* ku 49 */
+ 0x99b9,0x99ba,0x99bd,0x99bf,0x99c3,0x99c9,0x99d3,0x99d4,0x99d9,0x99da,
+ 0x99dc,0x99de,0x99e7,0x99ea,0x99eb,0x99ec,0x99f0,0x99f4,0x99f5,0x99f9,
+ 0x99fd,0x99fe,0x9a02,0x9a03,0x9a04,0x9a0b,0x9a0c,0x9a10,0x9a11,0x9a16,
+ 0x9a1e,0x9a20,0x9a22,0x9a23,0x9a24,0x9a27,0x9a2d,0x9a2e,0x9a33,0x9a35,
+ 0x9a36,0x9a38,0x9a47,0x9a41,0x9a44,0x9a4a,0x9a4b,0x9a4c,0x9a4e,0x9a51,
+ 0x9a54,0x9a56,0x9a5d,0x9aaa,0x9aac,0x9aae,0x9aaf,0x9ab2,0x9ab4,0x9ab5,
+ 0x9ab6,0x9ab9,0x9abb,0x9abe,0x9abf,0x9ac1,0x9ac3,0x9ac6,0x9ac8,0x9ace,
+ 0x9ad0,0x9ad2,0x9ad5,0x9ad6,0x9ad7,0x9adb,0x9adc,0x9ae0,0x9ae4,0x9ae5,
+ 0x9ae7,0x9ae9,0x9aec,0x9af2,0x9af3,0x9af5,0x9af9,0x9afa,0x9afd,0x9aff,
+ 0x9b00,0x9b01,0x9b02,0x9b03
+ },
+ { /* ku 4a */
+ 0x9b04,0x9b05,0x9b08,0x9b09,0x9b0b,0x9b0c,0x9b0d,0x9b0e,0x9b10,0x9b12,
+ 0x9b16,0x9b19,0x9b1b,0x9b1c,0x9b20,0x9b26,0x9b2b,0x9b2d,0x9b33,0x9b34,
+ 0x9b35,0x9b37,0x9b39,0x9b3a,0x9b3d,0x9b48,0x9b4b,0x9b4c,0x9b55,0x9b56,
+ 0x9b57,0x9b5b,0x9b5e,0x9b61,0x9b63,0x9b65,0x9b66,0x9b68,0x9b6a,0x9b6b,
+ 0x9b6c,0x9b6d,0x9b6e,0x9b73,0x9b75,0x9b77,0x9b78,0x9b79,0x9b7f,0x9b80,
+ 0x9b84,0x9b85,0x9b86,0x9b87,0x9b89,0x9b8a,0x9b8b,0x9b8d,0x9b8f,0x9b90,
+ 0x9b94,0x9b9a,0x9b9d,0x9b9e,0x9ba6,0x9ba7,0x9ba9,0x9bac,0x9bb0,0x9bb1,
+ 0x9bb2,0x9bb7,0x9bb8,0x9bbb,0x9bbc,0x9bbe,0x9bbf,0x9bc1,0x9bc7,0x9bc8,
+ 0x9bce,0x9bd0,0x9bd7,0x9bd8,0x9bdd,0x9bdf,0x9be5,0x9be7,0x9bea,0x9beb,
+ 0x9bef,0x9bf3,0x9bf7,0x9bf8
+ },
+ { /* ku 4b */
+ 0x9bf9,0x9bfa,0x9bfd,0x9bff,0x9c00,0x9c02,0x9c0b,0x9c0f,0x9c11,0x9c16,
+ 0x9c18,0x9c19,0x9c1a,0x9c1c,0x9c1e,0x9c22,0x9c23,0x9c26,0x9c27,0x9c28,
+ 0x9c29,0x9c2a,0x9c31,0x9c35,0x9c36,0x9c37,0x9c3d,0x9c41,0x9c43,0x9c44,
+ 0x9c45,0x9c49,0x9c4a,0x9c4e,0x9c4f,0x9c50,0x9c53,0x9c54,0x9c56,0x9c58,
+ 0x9c5b,0x9c5d,0x9c5e,0x9c5f,0x9c63,0x9c69,0x9c6a,0x9c5c,0x9c6b,0x9c68,
+ 0x9c6e,0x9c70,0x9c72,0x9c75,0x9c77,0x9c7b,0x9ce6,0x9cf2,0x9cf7,0x9cf9,
+ 0x9d0b,0x9d02,0x9d11,0x9d17,0x9d18,0x9d1c,0x9d1d,0x9d1e,0x9d2f,0x9d30,
+ 0x9d32,0x9d33,0x9d34,0x9d3a,0x9d3c,0x9d45,0x9d3d,0x9d42,0x9d43,0x9d47,
+ 0x9d4a,0x9d53,0x9d54,0x9d5f,0x9d63,0x9d62,0x9d65,0x9d69,0x9d6a,0x9d6b,
+ 0x9d70,0x9d76,0x9d77,0x9d7b
+ },
+ { /* ku 4c */
+ 0x9d7c,0x9d7e,0x9d83,0x9d84,0x9d86,0x9d8a,0x9d8d,0x9d8e,0x9d92,0x9d93,
+ 0x9d95,0x9d96,0x9d97,0x9d98,0x9da1,0x9daa,0x9dac,0x9dae,0x9db1,0x9db5,
+ 0x9db9,0x9dbc,0x9dbf,0x9dc3,0x9dc7,0x9dc9,0x9dca,0x9dd4,0x9dd5,0x9dd6,
+ 0x9dd7,0x9dda,0x9dde,0x9ddf,0x9de0,0x9de5,0x9de7,0x9de9,0x9deb,0x9dee,
+ 0x9df0,0x9df3,0x9df4,0x9dfe,0x9e0a,0x9e02,0x9e07,0x9e0e,0x9e10,0x9e11,
+ 0x9e12,0x9e15,0x9e16,0x9e19,0x9e1c,0x9e1d,0x9e7a,0x9e7b,0x9e7c,0x9e80,
+ 0x9e82,0x9e83,0x9e84,0x9e85,0x9e87,0x9e8e,0x9e8f,0x9e96,0x9e98,0x9e9b,
+ 0x9e9e,0x9ea4,0x9ea8,0x9eac,0x9eae,0x9eaf,0x9eb0,0x9eb3,0x9eb4,0x9eb5,
+ 0x9ec6,0x9ec8,0x9ecb,0x9ed5,0x9edf,0x9ee4,0x9ee7,0x9eec,0x9eed,0x9eee,
+ 0x9ef0,0x9ef1,0x9ef2,0x9ef5
+ },
+ { /* ku 4d */
+ 0x9ef8,0x9eff,0x9f02,0x9f03,0x9f09,0x9f0f,0x9f10,0x9f11,0x9f12,0x9f14,
+ 0x9f16,0x9f17,0x9f19,0x9f1a,0x9f1b,0x9f1f,0x9f22,0x9f26,0x9f2a,0x9f2b,
+ 0x9f2f,0x9f31,0x9f32,0x9f34,0x9f37,0x9f39,0x9f3a,0x9f3c,0x9f3d,0x9f3f,
+ 0x9f41,0x9f43,0x9f44,0x9f45,0x9f46,0x9f47,0x9f53,0x9f55,0x9f56,0x9f57,
+ 0x9f58,0x9f5a,0x9f5d,0x9f5e,0x9f68,0x9f69,0x9f6d,0x9f6e,0x9f6f,0x9f70,
+ 0x9f71,0x9f73,0x9f75,0x9f7a,0x9f7d,0x9f8f,0x9f90,0x9f91,0x9f92,0x9f94,
+ 0x9f96,0x9f97,0x9f9e,0x9fa1,0x9fa2,0x9fa3,0x9fa5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/koi8_r.c b/imap/src/charset/koi8_r.c
new file mode 100644
index 00000000..92dfcbcd
--- /dev/null
+++ b/imap/src/charset/koi8_r.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KOI8-R conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KOI8-R is a de-facto standard of Russia */
+
+static const unsigned short koi8rtab[128] = {
+ 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
+ 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
+ 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
+ 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
+ 0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556,
+ 0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,
+ 0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565,
+ 0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9,
+ 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
+ 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
+ 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
+ 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
+ 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
+ 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
+ 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
+};
diff --git a/imap/src/charset/koi8_u.c b/imap/src/charset/koi8_u.c
new file mode 100644
index 00000000..4fc8507b
--- /dev/null
+++ b/imap/src/charset/koi8_u.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KOI8-U conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KOI8-U is a de-facto standard of Ukraine */
+
+static const unsigned short koi8utab[128] = {
+ 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
+ 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
+ 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
+ 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
+ 0x2550,0x2551,0x2552,0x0451,0x0454,0x2554,0x0456,0x0457,
+ 0x2557,0x2558,0x2559,0x255a,0x255b,0x0491,0x255d,0x255e,
+ 0x255f,0x2560,0x2561,0x0401,0x0403,0x2563,0x0406,0x0407,
+ 0x2566,0x2567,0x2568,0x2569,0x256a,0x0490,0x256c,0x00a9,
+ 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
+ 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
+ 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
+ 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
+ 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
+ 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
+ 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
+};
diff --git a/imap/src/charset/ksc_5601.c b/imap/src/charset/ksc_5601.c
new file mode 100644
index 00000000..43a2f73b
--- /dev/null
+++ b/imap/src/charset/ksc_5601.c
@@ -0,0 +1,2673 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KSC 5601 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KSC 5601 is the national standard of the Republic of Korea (South Korea).
+ * It is believed that it is also the de-facto standard of the Democratic
+ * People's Republic of Korea (North Korea), although North Korea has its
+ * own official national standard (KPS 9566-97).
+ */
+
+#define BASE_KSC5601_KU 0x81
+#define BASE_KSC5601_TEN 0x41
+#define MAX_KSC5601_KU 125
+#define MAX_KSC5601_TEN 190
+
+
+#define KSCTOUNICODE(c,c1,ku,ten) \
+ ((((ku = c - BASE_KSC5601_KU) < MAX_KSC5601_KU) && \
+ ((ten = c1 - BASE_KSC5601_TEN) < MAX_KSC5601_TEN)) ? \
+ ksc5601tab[ku][ten] : UBOGON)
+
+
+static const unsigned short ksc5601tab[MAX_KSC5601_KU][MAX_KSC5601_TEN] = {
+ { /* ku 01 */
+ 0xac02,0xac03,0xac05,0xac06,0xac0b,0xac0c,0xac0d,0xac0e,0xac0f,0xac18,
+ 0xac1e,0xac1f,0xac21,0xac22,0xac23,0xac25,0xac26,0xac27,0xac28,0xac29,
+ 0xac2a,0xac2b,0xac2e,0xac32,0xac33,0xac34,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xac35,0xac36,0xac37,0xac3a,0xac3b,0xac3d,0xac3e,0xac3f,
+ 0xac41,0xac42,0xac43,0xac44,0xac45,0xac46,0xac47,0xac48,0xac49,0xac4a,
+ 0xac4c,0xac4e,0xac4f,0xac50,0xac51,0xac52,0xac53,0xac55,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xac56,0xac57,0xac59,0xac5a,0xac5b,0xac5d,
+ 0xac5e,0xac5f,0xac60,0xac61,0xac62,0xac63,0xac64,0xac65,0xac66,0xac67,
+ 0xac68,0xac69,0xac6a,0xac6b,0xac6c,0xac6d,0xac6e,0xac6f,0xac72,0xac73,
+ 0xac75,0xac76,0xac79,0xac7b,0xac7c,0xac7d,0xac7e,0xac7f,0xac82,0xac87,
+ 0xac88,0xac8d,0xac8e,0xac8f,0xac91,0xac92,0xac93,0xac95,0xac96,0xac97,
+ 0xac98,0xac99,0xac9a,0xac9b,0xac9e,0xaca2,0xaca3,0xaca4,0xaca5,0xaca6,
+ 0xaca7,0xacab,0xacad,0xacae,0xacb1,0xacb2,0xacb3,0xacb4,0xacb5,0xacb6,
+ 0xacb7,0xacba,0xacbe,0xacbf,0xacc0,0xacc2,0xacc3,0xacc5,0xacc6,0xacc7,
+ 0xacc9,0xacca,0xaccb,0xaccd,0xacce,0xaccf,0xacd0,0xacd1,0xacd2,0xacd3,
+ 0xacd4,0xacd6,0xacd8,0xacd9,0xacda,0xacdb,0xacdc,0xacdd,0xacde,0xacdf,
+ 0xace2,0xace3,0xace5,0xace6,0xace9,0xaceb,0xaced,0xacee,0xacf2,0xacf4,
+ 0xacf7,0xacf8,0xacf9,0xacfa,0xacfb,0xacfe,0xacff,0xad01,0xad02,0xad03,
+ 0xad05,0xad07,0xad08,0xad09,0xad0a,0xad0b,0xad0e,0xad10,0xad12,0xad13
+ },
+ { /* ku 02 */
+ 0xad14,0xad15,0xad16,0xad17,0xad19,0xad1a,0xad1b,0xad1d,0xad1e,0xad1f,
+ 0xad21,0xad22,0xad23,0xad24,0xad25,0xad26,0xad27,0xad28,0xad2a,0xad2b,
+ 0xad2e,0xad2f,0xad30,0xad31,0xad32,0xad33,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xad36,0xad37,0xad39,0xad3a,0xad3b,0xad3d,0xad3e,0xad3f,
+ 0xad40,0xad41,0xad42,0xad43,0xad46,0xad48,0xad4a,0xad4b,0xad4c,0xad4d,
+ 0xad4e,0xad4f,0xad51,0xad52,0xad53,0xad55,0xad56,0xad57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xad59,0xad5a,0xad5b,0xad5c,0xad5d,0xad5e,
+ 0xad5f,0xad60,0xad62,0xad64,0xad65,0xad66,0xad67,0xad68,0xad69,0xad6a,
+ 0xad6b,0xad6e,0xad6f,0xad71,0xad72,0xad77,0xad78,0xad79,0xad7a,0xad7e,
+ 0xad80,0xad83,0xad84,0xad85,0xad86,0xad87,0xad8a,0xad8b,0xad8d,0xad8e,
+ 0xad8f,0xad91,0xad92,0xad93,0xad94,0xad95,0xad96,0xad97,0xad98,0xad99,
+ 0xad9a,0xad9b,0xad9e,0xad9f,0xada0,0xada1,0xada2,0xada3,0xada5,0xada6,
+ 0xada7,0xada8,0xada9,0xadaa,0xadab,0xadac,0xadad,0xadae,0xadaf,0xadb0,
+ 0xadb1,0xadb2,0xadb3,0xadb4,0xadb5,0xadb6,0xadb8,0xadb9,0xadba,0xadbb,
+ 0xadbc,0xadbd,0xadbe,0xadbf,0xadc2,0xadc3,0xadc5,0xadc6,0xadc7,0xadc9,
+ 0xadca,0xadcb,0xadcc,0xadcd,0xadce,0xadcf,0xadd2,0xadd4,0xadd5,0xadd6,
+ 0xadd7,0xadd8,0xadd9,0xadda,0xaddb,0xaddd,0xadde,0xaddf,0xade1,0xade2,
+ 0xade3,0xade5,0xade6,0xade7,0xade8,0xade9,0xadea,0xadeb,0xadec,0xaded,
+ 0xadee,0xadef,0xadf0,0xadf1,0xadf2,0xadf3,0xadf4,0xadf5,0xadf6,0xadf7
+ },
+ { /* ku 03 */
+ 0xadfa,0xadfb,0xadfd,0xadfe,0xae02,0xae03,0xae04,0xae05,0xae06,0xae07,
+ 0xae0a,0xae0c,0xae0e,0xae0f,0xae10,0xae11,0xae12,0xae13,0xae15,0xae16,
+ 0xae17,0xae18,0xae19,0xae1a,0xae1b,0xae1c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xae1d,0xae1e,0xae1f,0xae20,0xae21,0xae22,0xae23,0xae24,
+ 0xae25,0xae26,0xae27,0xae28,0xae29,0xae2a,0xae2b,0xae2c,0xae2d,0xae2e,
+ 0xae2f,0xae32,0xae33,0xae35,0xae36,0xae39,0xae3b,0xae3c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xae3d,0xae3e,0xae3f,0xae42,0xae44,0xae47,
+ 0xae48,0xae49,0xae4b,0xae4f,0xae51,0xae52,0xae53,0xae55,0xae57,0xae58,
+ 0xae59,0xae5a,0xae5b,0xae5e,0xae62,0xae63,0xae64,0xae66,0xae67,0xae6a,
+ 0xae6b,0xae6d,0xae6e,0xae6f,0xae71,0xae72,0xae73,0xae74,0xae75,0xae76,
+ 0xae77,0xae7a,0xae7e,0xae7f,0xae80,0xae81,0xae82,0xae83,0xae86,0xae87,
+ 0xae88,0xae89,0xae8a,0xae8b,0xae8d,0xae8e,0xae8f,0xae90,0xae91,0xae92,
+ 0xae93,0xae94,0xae95,0xae96,0xae97,0xae98,0xae99,0xae9a,0xae9b,0xae9c,
+ 0xae9d,0xae9e,0xae9f,0xaea0,0xaea1,0xaea2,0xaea3,0xaea4,0xaea5,0xaea6,
+ 0xaea7,0xaea8,0xaea9,0xaeaa,0xaeab,0xaeac,0xaead,0xaeae,0xaeaf,0xaeb0,
+ 0xaeb1,0xaeb2,0xaeb3,0xaeb4,0xaeb5,0xaeb6,0xaeb7,0xaeb8,0xaeb9,0xaeba,
+ 0xaebb,0xaebf,0xaec1,0xaec2,0xaec3,0xaec5,0xaec6,0xaec7,0xaec8,0xaec9,
+ 0xaeca,0xaecb,0xaece,0xaed2,0xaed3,0xaed4,0xaed5,0xaed6,0xaed7,0xaeda,
+ 0xaedb,0xaedd,0xaede,0xaedf,0xaee0,0xaee1,0xaee2,0xaee3,0xaee4,0xaee5
+ },
+ { /* ku 04 */
+ 0xaee6,0xaee7,0xaee9,0xaeea,0xaeec,0xaeee,0xaeef,0xaef0,0xaef1,0xaef2,
+ 0xaef3,0xaef5,0xaef6,0xaef7,0xaef9,0xaefa,0xaefb,0xaefd,0xaefe,0xaeff,
+ 0xaf00,0xaf01,0xaf02,0xaf03,0xaf04,0xaf05,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xaf06,0xaf09,0xaf0a,0xaf0b,0xaf0c,0xaf0e,0xaf0f,0xaf11,
+ 0xaf12,0xaf13,0xaf14,0xaf15,0xaf16,0xaf17,0xaf18,0xaf19,0xaf1a,0xaf1b,
+ 0xaf1c,0xaf1d,0xaf1e,0xaf1f,0xaf20,0xaf21,0xaf22,0xaf23,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xaf24,0xaf25,0xaf26,0xaf27,0xaf28,0xaf29,
+ 0xaf2a,0xaf2b,0xaf2e,0xaf2f,0xaf31,0xaf33,0xaf35,0xaf36,0xaf37,0xaf38,
+ 0xaf39,0xaf3a,0xaf3b,0xaf3e,0xaf40,0xaf44,0xaf45,0xaf46,0xaf47,0xaf4a,
+ 0xaf4b,0xaf4c,0xaf4d,0xaf4e,0xaf4f,0xaf51,0xaf52,0xaf53,0xaf54,0xaf55,
+ 0xaf56,0xaf57,0xaf58,0xaf59,0xaf5a,0xaf5b,0xaf5e,0xaf5f,0xaf60,0xaf61,
+ 0xaf62,0xaf63,0xaf66,0xaf67,0xaf68,0xaf69,0xaf6a,0xaf6b,0xaf6c,0xaf6d,
+ 0xaf6e,0xaf6f,0xaf70,0xaf71,0xaf72,0xaf73,0xaf74,0xaf75,0xaf76,0xaf77,
+ 0xaf78,0xaf7a,0xaf7b,0xaf7c,0xaf7d,0xaf7e,0xaf7f,0xaf81,0xaf82,0xaf83,
+ 0xaf85,0xaf86,0xaf87,0xaf89,0xaf8a,0xaf8b,0xaf8c,0xaf8d,0xaf8e,0xaf8f,
+ 0xaf92,0xaf93,0xaf94,0xaf96,0xaf97,0xaf98,0xaf99,0xaf9a,0xaf9b,0xaf9d,
+ 0xaf9e,0xaf9f,0xafa0,0xafa1,0xafa2,0xafa3,0xafa4,0xafa5,0xafa6,0xafa7,
+ 0xafa8,0xafa9,0xafaa,0xafab,0xafac,0xafad,0xafae,0xafaf,0xafb0,0xafb1,
+ 0xafb2,0xafb3,0xafb4,0xafb5,0xafb6,0xafb7,0xafba,0xafbb,0xafbd,0xafbe
+ },
+ { /* ku 05 */
+ 0xafbf,0xafc1,0xafc2,0xafc3,0xafc4,0xafc5,0xafc6,0xafca,0xafcc,0xafcf,
+ 0xafd0,0xafd1,0xafd2,0xafd3,0xafd5,0xafd6,0xafd7,0xafd8,0xafd9,0xafda,
+ 0xafdb,0xafdd,0xafde,0xafdf,0xafe0,0xafe1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xafe2,0xafe3,0xafe4,0xafe5,0xafe6,0xafe7,0xafea,0xafeb,
+ 0xafec,0xafed,0xafee,0xafef,0xaff2,0xaff3,0xaff5,0xaff6,0xaff7,0xaff9,
+ 0xaffa,0xaffb,0xaffc,0xaffd,0xaffe,0xafff,0xb002,0xb003,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb005,0xb006,0xb007,0xb008,0xb009,0xb00a,
+ 0xb00b,0xb00d,0xb00e,0xb00f,0xb011,0xb012,0xb013,0xb015,0xb016,0xb017,
+ 0xb018,0xb019,0xb01a,0xb01b,0xb01e,0xb01f,0xb020,0xb021,0xb022,0xb023,
+ 0xb024,0xb025,0xb026,0xb027,0xb029,0xb02a,0xb02b,0xb02c,0xb02d,0xb02e,
+ 0xb02f,0xb030,0xb031,0xb032,0xb033,0xb034,0xb035,0xb036,0xb037,0xb038,
+ 0xb039,0xb03a,0xb03b,0xb03c,0xb03d,0xb03e,0xb03f,0xb040,0xb041,0xb042,
+ 0xb043,0xb046,0xb047,0xb049,0xb04b,0xb04d,0xb04f,0xb050,0xb051,0xb052,
+ 0xb056,0xb058,0xb05a,0xb05b,0xb05c,0xb05e,0xb05f,0xb060,0xb061,0xb062,
+ 0xb063,0xb064,0xb065,0xb066,0xb067,0xb068,0xb069,0xb06a,0xb06b,0xb06c,
+ 0xb06d,0xb06e,0xb06f,0xb070,0xb071,0xb072,0xb073,0xb074,0xb075,0xb076,
+ 0xb077,0xb078,0xb079,0xb07a,0xb07b,0xb07e,0xb07f,0xb081,0xb082,0xb083,
+ 0xb085,0xb086,0xb087,0xb088,0xb089,0xb08a,0xb08b,0xb08e,0xb090,0xb092,
+ 0xb093,0xb094,0xb095,0xb096,0xb097,0xb09b,0xb09d,0xb09e,0xb0a3,0xb0a4
+ },
+ { /* ku 06 */
+ 0xb0a5,0xb0a6,0xb0a7,0xb0aa,0xb0b0,0xb0b2,0xb0b6,0xb0b7,0xb0b9,0xb0ba,
+ 0xb0bb,0xb0bd,0xb0be,0xb0bf,0xb0c0,0xb0c1,0xb0c2,0xb0c3,0xb0c6,0xb0ca,
+ 0xb0cb,0xb0cc,0xb0cd,0xb0ce,0xb0cf,0xb0d2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb0d3,0xb0d5,0xb0d6,0xb0d7,0xb0d9,0xb0da,0xb0db,0xb0dc,
+ 0xb0dd,0xb0de,0xb0df,0xb0e1,0xb0e2,0xb0e3,0xb0e4,0xb0e6,0xb0e7,0xb0e8,
+ 0xb0e9,0xb0ea,0xb0eb,0xb0ec,0xb0ed,0xb0ee,0xb0ef,0xb0f0,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb0f1,0xb0f2,0xb0f3,0xb0f4,0xb0f5,0xb0f6,
+ 0xb0f7,0xb0f8,0xb0f9,0xb0fa,0xb0fb,0xb0fc,0xb0fd,0xb0fe,0xb0ff,0xb100,
+ 0xb101,0xb102,0xb103,0xb104,0xb105,0xb106,0xb107,0xb10a,0xb10d,0xb10e,
+ 0xb10f,0xb111,0xb114,0xb115,0xb116,0xb117,0xb11a,0xb11e,0xb11f,0xb120,
+ 0xb121,0xb122,0xb126,0xb127,0xb129,0xb12a,0xb12b,0xb12d,0xb12e,0xb12f,
+ 0xb130,0xb131,0xb132,0xb133,0xb136,0xb13a,0xb13b,0xb13c,0xb13d,0xb13e,
+ 0xb13f,0xb142,0xb143,0xb145,0xb146,0xb147,0xb149,0xb14a,0xb14b,0xb14c,
+ 0xb14d,0xb14e,0xb14f,0xb152,0xb153,0xb156,0xb157,0xb159,0xb15a,0xb15b,
+ 0xb15d,0xb15e,0xb15f,0xb161,0xb162,0xb163,0xb164,0xb165,0xb166,0xb167,
+ 0xb168,0xb169,0xb16a,0xb16b,0xb16c,0xb16d,0xb16e,0xb16f,0xb170,0xb171,
+ 0xb172,0xb173,0xb174,0xb175,0xb176,0xb177,0xb17a,0xb17b,0xb17d,0xb17e,
+ 0xb17f,0xb181,0xb183,0xb184,0xb185,0xb186,0xb187,0xb18a,0xb18c,0xb18e,
+ 0xb18f,0xb190,0xb191,0xb195,0xb196,0xb197,0xb199,0xb19a,0xb19b,0xb19d
+ },
+ { /* ku 07 */
+ 0xb19e,0xb19f,0xb1a0,0xb1a1,0xb1a2,0xb1a3,0xb1a4,0xb1a5,0xb1a6,0xb1a7,
+ 0xb1a9,0xb1aa,0xb1ab,0xb1ac,0xb1ad,0xb1ae,0xb1af,0xb1b0,0xb1b1,0xb1b2,
+ 0xb1b3,0xb1b4,0xb1b5,0xb1b6,0xb1b7,0xb1b8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb1b9,0xb1ba,0xb1bb,0xb1bc,0xb1bd,0xb1be,0xb1bf,0xb1c0,
+ 0xb1c1,0xb1c2,0xb1c3,0xb1c4,0xb1c5,0xb1c6,0xb1c7,0xb1c8,0xb1c9,0xb1ca,
+ 0xb1cb,0xb1cd,0xb1ce,0xb1cf,0xb1d1,0xb1d2,0xb1d3,0xb1d5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb1d6,0xb1d7,0xb1d8,0xb1d9,0xb1da,0xb1db,
+ 0xb1de,0xb1e0,0xb1e1,0xb1e2,0xb1e3,0xb1e4,0xb1e5,0xb1e6,0xb1e7,0xb1ea,
+ 0xb1eb,0xb1ed,0xb1ee,0xb1ef,0xb1f1,0xb1f2,0xb1f3,0xb1f4,0xb1f5,0xb1f6,
+ 0xb1f7,0xb1f8,0xb1fa,0xb1fc,0xb1fe,0xb1ff,0xb200,0xb201,0xb202,0xb203,
+ 0xb206,0xb207,0xb209,0xb20a,0xb20d,0xb20e,0xb20f,0xb210,0xb211,0xb212,
+ 0xb213,0xb216,0xb218,0xb21a,0xb21b,0xb21c,0xb21d,0xb21e,0xb21f,0xb221,
+ 0xb222,0xb223,0xb224,0xb225,0xb226,0xb227,0xb228,0xb229,0xb22a,0xb22b,
+ 0xb22c,0xb22d,0xb22e,0xb22f,0xb230,0xb231,0xb232,0xb233,0xb235,0xb236,
+ 0xb237,0xb238,0xb239,0xb23a,0xb23b,0xb23d,0xb23e,0xb23f,0xb240,0xb241,
+ 0xb242,0xb243,0xb244,0xb245,0xb246,0xb247,0xb248,0xb249,0xb24a,0xb24b,
+ 0xb24c,0xb24d,0xb24e,0xb24f,0xb250,0xb251,0xb252,0xb253,0xb254,0xb255,
+ 0xb256,0xb257,0xb259,0xb25a,0xb25b,0xb25d,0xb25e,0xb25f,0xb261,0xb262,
+ 0xb263,0xb264,0xb265,0xb266,0xb267,0xb26a,0xb26b,0xb26c,0xb26d,0xb26e
+ },
+ { /* ku 08 */
+ 0xb26f,0xb270,0xb271,0xb272,0xb273,0xb276,0xb277,0xb278,0xb279,0xb27a,
+ 0xb27b,0xb27d,0xb27e,0xb27f,0xb280,0xb281,0xb282,0xb283,0xb286,0xb287,
+ 0xb288,0xb28a,0xb28b,0xb28c,0xb28d,0xb28e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb28f,0xb292,0xb293,0xb295,0xb296,0xb297,0xb29b,0xb29c,
+ 0xb29d,0xb29e,0xb29f,0xb2a2,0xb2a4,0xb2a7,0xb2a8,0xb2a9,0xb2ab,0xb2ad,
+ 0xb2ae,0xb2af,0xb2b1,0xb2b2,0xb2b3,0xb2b5,0xb2b6,0xb2b7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb2b8,0xb2b9,0xb2ba,0xb2bb,0xb2bc,0xb2bd,
+ 0xb2be,0xb2bf,0xb2c0,0xb2c1,0xb2c2,0xb2c3,0xb2c4,0xb2c5,0xb2c6,0xb2c7,
+ 0xb2ca,0xb2cb,0xb2cd,0xb2ce,0xb2cf,0xb2d1,0xb2d3,0xb2d4,0xb2d5,0xb2d6,
+ 0xb2d7,0xb2da,0xb2dc,0xb2de,0xb2df,0xb2e0,0xb2e1,0xb2e3,0xb2e7,0xb2e9,
+ 0xb2ea,0xb2f0,0xb2f1,0xb2f2,0xb2f6,0xb2fc,0xb2fd,0xb2fe,0xb302,0xb303,
+ 0xb305,0xb306,0xb307,0xb309,0xb30a,0xb30b,0xb30c,0xb30d,0xb30e,0xb30f,
+ 0xb312,0xb316,0xb317,0xb318,0xb319,0xb31a,0xb31b,0xb31d,0xb31e,0xb31f,
+ 0xb320,0xb321,0xb322,0xb323,0xb324,0xb325,0xb326,0xb327,0xb328,0xb329,
+ 0xb32a,0xb32b,0xb32c,0xb32d,0xb32e,0xb32f,0xb330,0xb331,0xb332,0xb333,
+ 0xb334,0xb335,0xb336,0xb337,0xb338,0xb339,0xb33a,0xb33b,0xb33c,0xb33d,
+ 0xb33e,0xb33f,0xb340,0xb341,0xb342,0xb343,0xb344,0xb345,0xb346,0xb347,
+ 0xb348,0xb349,0xb34a,0xb34b,0xb34c,0xb34d,0xb34e,0xb34f,0xb350,0xb351,
+ 0xb352,0xb353,0xb357,0xb359,0xb35a,0xb35d,0xb360,0xb361,0xb362,0xb363
+ },
+ { /* ku 09 */
+ 0xb366,0xb368,0xb36a,0xb36c,0xb36d,0xb36f,0xb372,0xb373,0xb375,0xb376,
+ 0xb377,0xb379,0xb37a,0xb37b,0xb37c,0xb37d,0xb37e,0xb37f,0xb382,0xb386,
+ 0xb387,0xb388,0xb389,0xb38a,0xb38b,0xb38d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb38e,0xb38f,0xb391,0xb392,0xb393,0xb395,0xb396,0xb397,
+ 0xb398,0xb399,0xb39a,0xb39b,0xb39c,0xb39d,0xb39e,0xb39f,0xb3a2,0xb3a3,
+ 0xb3a4,0xb3a5,0xb3a6,0xb3a7,0xb3a9,0xb3aa,0xb3ab,0xb3ad,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb3ae,0xb3af,0xb3b0,0xb3b1,0xb3b2,0xb3b3,
+ 0xb3b4,0xb3b5,0xb3b6,0xb3b7,0xb3b8,0xb3b9,0xb3ba,0xb3bb,0xb3bc,0xb3bd,
+ 0xb3be,0xb3bf,0xb3c0,0xb3c1,0xb3c2,0xb3c3,0xb3c6,0xb3c7,0xb3c9,0xb3ca,
+ 0xb3cd,0xb3cf,0xb3d1,0xb3d2,0xb3d3,0xb3d6,0xb3d8,0xb3da,0xb3dc,0xb3de,
+ 0xb3df,0xb3e1,0xb3e2,0xb3e3,0xb3e5,0xb3e6,0xb3e7,0xb3e9,0xb3ea,0xb3eb,
+ 0xb3ec,0xb3ed,0xb3ee,0xb3ef,0xb3f0,0xb3f1,0xb3f2,0xb3f3,0xb3f4,0xb3f5,
+ 0xb3f6,0xb3f7,0xb3f8,0xb3f9,0xb3fa,0xb3fb,0xb3fd,0xb3fe,0xb3ff,0xb400,
+ 0xb401,0xb402,0xb403,0xb404,0xb405,0xb406,0xb407,0xb408,0xb409,0xb40a,
+ 0xb40b,0xb40c,0xb40d,0xb40e,0xb40f,0xb411,0xb412,0xb413,0xb414,0xb415,
+ 0xb416,0xb417,0xb419,0xb41a,0xb41b,0xb41d,0xb41e,0xb41f,0xb421,0xb422,
+ 0xb423,0xb424,0xb425,0xb426,0xb427,0xb42a,0xb42c,0xb42d,0xb42e,0xb42f,
+ 0xb430,0xb431,0xb432,0xb433,0xb435,0xb436,0xb437,0xb438,0xb439,0xb43a,
+ 0xb43b,0xb43c,0xb43d,0xb43e,0xb43f,0xb440,0xb441,0xb442,0xb443,0xb444
+ },
+ { /* ku 0a */
+ 0xb445,0xb446,0xb447,0xb448,0xb449,0xb44a,0xb44b,0xb44c,0xb44d,0xb44e,
+ 0xb44f,0xb452,0xb453,0xb455,0xb456,0xb457,0xb459,0xb45a,0xb45b,0xb45c,
+ 0xb45d,0xb45e,0xb45f,0xb462,0xb464,0xb466,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb467,0xb468,0xb469,0xb46a,0xb46b,0xb46d,0xb46e,0xb46f,
+ 0xb470,0xb471,0xb472,0xb473,0xb474,0xb475,0xb476,0xb477,0xb478,0xb479,
+ 0xb47a,0xb47b,0xb47c,0xb47d,0xb47e,0xb47f,0xb481,0xb482,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb483,0xb484,0xb485,0xb486,0xb487,0xb489,
+ 0xb48a,0xb48b,0xb48c,0xb48d,0xb48e,0xb48f,0xb490,0xb491,0xb492,0xb493,
+ 0xb494,0xb495,0xb496,0xb497,0xb498,0xb499,0xb49a,0xb49b,0xb49c,0xb49e,
+ 0xb49f,0xb4a0,0xb4a1,0xb4a2,0xb4a3,0xb4a5,0xb4a6,0xb4a7,0xb4a9,0xb4aa,
+ 0xb4ab,0xb4ad,0xb4ae,0xb4af,0xb4b0,0xb4b1,0xb4b2,0xb4b3,0xb4b4,0xb4b6,
+ 0xb4b8,0xb4ba,0xb4bb,0xb4bc,0xb4bd,0xb4be,0xb4bf,0xb4c1,0xb4c2,0xb4c3,
+ 0xb4c5,0xb4c6,0xb4c7,0xb4c9,0xb4ca,0xb4cb,0xb4cc,0xb4cd,0xb4ce,0xb4cf,
+ 0xb4d1,0xb4d2,0xb4d3,0xb4d4,0xb4d6,0xb4d7,0xb4d8,0xb4d9,0xb4da,0xb4db,
+ 0xb4de,0xb4df,0xb4e1,0xb4e2,0xb4e5,0xb4e7,0xb4e8,0xb4e9,0xb4ea,0xb4eb,
+ 0xb4ee,0xb4f0,0xb4f2,0xb4f3,0xb4f4,0xb4f5,0xb4f6,0xb4f7,0xb4f9,0xb4fa,
+ 0xb4fb,0xb4fc,0xb4fd,0xb4fe,0xb4ff,0xb500,0xb501,0xb502,0xb503,0xb504,
+ 0xb505,0xb506,0xb507,0xb508,0xb509,0xb50a,0xb50b,0xb50c,0xb50d,0xb50e,
+ 0xb50f,0xb510,0xb511,0xb512,0xb513,0xb516,0xb517,0xb519,0xb51a,0xb51d
+ },
+ { /* ku 0b */
+ 0xb51e,0xb51f,0xb520,0xb521,0xb522,0xb523,0xb526,0xb52b,0xb52c,0xb52d,
+ 0xb52e,0xb52f,0xb532,0xb533,0xb535,0xb536,0xb537,0xb539,0xb53a,0xb53b,
+ 0xb53c,0xb53d,0xb53e,0xb53f,0xb542,0xb546,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb547,0xb548,0xb549,0xb54a,0xb54e,0xb54f,0xb551,0xb552,
+ 0xb553,0xb555,0xb556,0xb557,0xb558,0xb559,0xb55a,0xb55b,0xb55e,0xb562,
+ 0xb563,0xb564,0xb565,0xb566,0xb567,0xb568,0xb569,0xb56a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb56b,0xb56c,0xb56d,0xb56e,0xb56f,0xb570,
+ 0xb571,0xb572,0xb573,0xb574,0xb575,0xb576,0xb577,0xb578,0xb579,0xb57a,
+ 0xb57b,0xb57c,0xb57d,0xb57e,0xb57f,0xb580,0xb581,0xb582,0xb583,0xb584,
+ 0xb585,0xb586,0xb587,0xb588,0xb589,0xb58a,0xb58b,0xb58c,0xb58d,0xb58e,
+ 0xb58f,0xb590,0xb591,0xb592,0xb593,0xb594,0xb595,0xb596,0xb597,0xb598,
+ 0xb599,0xb59a,0xb59b,0xb59c,0xb59d,0xb59e,0xb59f,0xb5a2,0xb5a3,0xb5a5,
+ 0xb5a6,0xb5a7,0xb5a9,0xb5ac,0xb5ad,0xb5ae,0xb5af,0xb5b2,0xb5b6,0xb5b7,
+ 0xb5b8,0xb5b9,0xb5ba,0xb5be,0xb5bf,0xb5c1,0xb5c2,0xb5c3,0xb5c5,0xb5c6,
+ 0xb5c7,0xb5c8,0xb5c9,0xb5ca,0xb5cb,0xb5ce,0xb5d2,0xb5d3,0xb5d4,0xb5d5,
+ 0xb5d6,0xb5d7,0xb5d9,0xb5da,0xb5db,0xb5dc,0xb5dd,0xb5de,0xb5df,0xb5e0,
+ 0xb5e1,0xb5e2,0xb5e3,0xb5e4,0xb5e5,0xb5e6,0xb5e7,0xb5e8,0xb5e9,0xb5ea,
+ 0xb5eb,0xb5ed,0xb5ee,0xb5ef,0xb5f0,0xb5f1,0xb5f2,0xb5f3,0xb5f4,0xb5f5,
+ 0xb5f6,0xb5f7,0xb5f8,0xb5f9,0xb5fa,0xb5fb,0xb5fc,0xb5fd,0xb5fe,0xb5ff
+ },
+ { /* ku 0c */
+ 0xb600,0xb601,0xb602,0xb603,0xb604,0xb605,0xb606,0xb607,0xb608,0xb609,
+ 0xb60a,0xb60b,0xb60c,0xb60d,0xb60e,0xb60f,0xb612,0xb613,0xb615,0xb616,
+ 0xb617,0xb619,0xb61a,0xb61b,0xb61c,0xb61d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb61e,0xb61f,0xb620,0xb621,0xb622,0xb623,0xb624,0xb626,
+ 0xb627,0xb628,0xb629,0xb62a,0xb62b,0xb62d,0xb62e,0xb62f,0xb630,0xb631,
+ 0xb632,0xb633,0xb635,0xb636,0xb637,0xb638,0xb639,0xb63a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb63b,0xb63c,0xb63d,0xb63e,0xb63f,0xb640,
+ 0xb641,0xb642,0xb643,0xb644,0xb645,0xb646,0xb647,0xb649,0xb64a,0xb64b,
+ 0xb64c,0xb64d,0xb64e,0xb64f,0xb650,0xb651,0xb652,0xb653,0xb654,0xb655,
+ 0xb656,0xb657,0xb658,0xb659,0xb65a,0xb65b,0xb65c,0xb65d,0xb65e,0xb65f,
+ 0xb660,0xb661,0xb662,0xb663,0xb665,0xb666,0xb667,0xb669,0xb66a,0xb66b,
+ 0xb66c,0xb66d,0xb66e,0xb66f,0xb670,0xb671,0xb672,0xb673,0xb674,0xb675,
+ 0xb676,0xb677,0xb678,0xb679,0xb67a,0xb67b,0xb67c,0xb67d,0xb67e,0xb67f,
+ 0xb680,0xb681,0xb682,0xb683,0xb684,0xb685,0xb686,0xb687,0xb688,0xb689,
+ 0xb68a,0xb68b,0xb68c,0xb68d,0xb68e,0xb68f,0xb690,0xb691,0xb692,0xb693,
+ 0xb694,0xb695,0xb696,0xb697,0xb698,0xb699,0xb69a,0xb69b,0xb69e,0xb69f,
+ 0xb6a1,0xb6a2,0xb6a3,0xb6a5,0xb6a6,0xb6a7,0xb6a8,0xb6a9,0xb6aa,0xb6ad,
+ 0xb6ae,0xb6af,0xb6b0,0xb6b2,0xb6b3,0xb6b4,0xb6b5,0xb6b6,0xb6b7,0xb6b8,
+ 0xb6b9,0xb6ba,0xb6bb,0xb6bc,0xb6bd,0xb6be,0xb6bf,0xb6c0,0xb6c1,0xb6c2
+ },
+ { /* ku 0d */
+ 0xb6c3,0xb6c4,0xb6c5,0xb6c6,0xb6c7,0xb6c8,0xb6c9,0xb6ca,0xb6cb,0xb6cc,
+ 0xb6cd,0xb6ce,0xb6cf,0xb6d0,0xb6d1,0xb6d2,0xb6d3,0xb6d5,0xb6d6,0xb6d7,
+ 0xb6d8,0xb6d9,0xb6da,0xb6db,0xb6dc,0xb6dd,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb6de,0xb6df,0xb6e0,0xb6e1,0xb6e2,0xb6e3,0xb6e4,0xb6e5,
+ 0xb6e6,0xb6e7,0xb6e8,0xb6e9,0xb6ea,0xb6eb,0xb6ec,0xb6ed,0xb6ee,0xb6ef,
+ 0xb6f1,0xb6f2,0xb6f3,0xb6f5,0xb6f6,0xb6f7,0xb6f9,0xb6fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb6fb,0xb6fc,0xb6fd,0xb6fe,0xb6ff,0xb702,
+ 0xb703,0xb704,0xb706,0xb707,0xb708,0xb709,0xb70a,0xb70b,0xb70c,0xb70d,
+ 0xb70e,0xb70f,0xb710,0xb711,0xb712,0xb713,0xb714,0xb715,0xb716,0xb717,
+ 0xb718,0xb719,0xb71a,0xb71b,0xb71c,0xb71d,0xb71e,0xb71f,0xb720,0xb721,
+ 0xb722,0xb723,0xb724,0xb725,0xb726,0xb727,0xb72a,0xb72b,0xb72d,0xb72e,
+ 0xb731,0xb732,0xb733,0xb734,0xb735,0xb736,0xb737,0xb73a,0xb73c,0xb73d,
+ 0xb73e,0xb73f,0xb740,0xb741,0xb742,0xb743,0xb745,0xb746,0xb747,0xb749,
+ 0xb74a,0xb74b,0xb74d,0xb74e,0xb74f,0xb750,0xb751,0xb752,0xb753,0xb756,
+ 0xb757,0xb758,0xb759,0xb75a,0xb75b,0xb75c,0xb75d,0xb75e,0xb75f,0xb761,
+ 0xb762,0xb763,0xb765,0xb766,0xb767,0xb769,0xb76a,0xb76b,0xb76c,0xb76d,
+ 0xb76e,0xb76f,0xb772,0xb774,0xb776,0xb777,0xb778,0xb779,0xb77a,0xb77b,
+ 0xb77e,0xb77f,0xb781,0xb782,0xb783,0xb785,0xb786,0xb787,0xb788,0xb789,
+ 0xb78a,0xb78b,0xb78e,0xb793,0xb794,0xb795,0xb79a,0xb79b,0xb79d,0xb79e
+ },
+ { /* ku 0e */
+ 0xb79f,0xb7a1,0xb7a2,0xb7a3,0xb7a4,0xb7a5,0xb7a6,0xb7a7,0xb7aa,0xb7ae,
+ 0xb7af,0xb7b0,0xb7b1,0xb7b2,0xb7b3,0xb7b6,0xb7b7,0xb7b9,0xb7ba,0xb7bb,
+ 0xb7bc,0xb7bd,0xb7be,0xb7bf,0xb7c0,0xb7c1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb7c2,0xb7c3,0xb7c4,0xb7c5,0xb7c6,0xb7c8,0xb7ca,0xb7cb,
+ 0xb7cc,0xb7cd,0xb7ce,0xb7cf,0xb7d0,0xb7d1,0xb7d2,0xb7d3,0xb7d4,0xb7d5,
+ 0xb7d6,0xb7d7,0xb7d8,0xb7d9,0xb7da,0xb7db,0xb7dc,0xb7dd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb7de,0xb7df,0xb7e0,0xb7e1,0xb7e2,0xb7e3,
+ 0xb7e4,0xb7e5,0xb7e6,0xb7e7,0xb7e8,0xb7e9,0xb7ea,0xb7eb,0xb7ee,0xb7ef,
+ 0xb7f1,0xb7f2,0xb7f3,0xb7f5,0xb7f6,0xb7f7,0xb7f8,0xb7f9,0xb7fa,0xb7fb,
+ 0xb7fe,0xb802,0xb803,0xb804,0xb805,0xb806,0xb80a,0xb80b,0xb80d,0xb80e,
+ 0xb80f,0xb811,0xb812,0xb813,0xb814,0xb815,0xb816,0xb817,0xb81a,0xb81c,
+ 0xb81e,0xb81f,0xb820,0xb821,0xb822,0xb823,0xb826,0xb827,0xb829,0xb82a,
+ 0xb82b,0xb82d,0xb82e,0xb82f,0xb830,0xb831,0xb832,0xb833,0xb836,0xb83a,
+ 0xb83b,0xb83c,0xb83d,0xb83e,0xb83f,0xb841,0xb842,0xb843,0xb845,0xb846,
+ 0xb847,0xb848,0xb849,0xb84a,0xb84b,0xb84c,0xb84d,0xb84e,0xb84f,0xb850,
+ 0xb852,0xb854,0xb855,0xb856,0xb857,0xb858,0xb859,0xb85a,0xb85b,0xb85e,
+ 0xb85f,0xb861,0xb862,0xb863,0xb865,0xb866,0xb867,0xb868,0xb869,0xb86a,
+ 0xb86b,0xb86e,0xb870,0xb872,0xb873,0xb874,0xb875,0xb876,0xb877,0xb879,
+ 0xb87a,0xb87b,0xb87d,0xb87e,0xb87f,0xb880,0xb881,0xb882,0xb883,0xb884
+ },
+ { /* ku 0f */
+ 0xb885,0xb886,0xb887,0xb888,0xb889,0xb88a,0xb88b,0xb88c,0xb88e,0xb88f,
+ 0xb890,0xb891,0xb892,0xb893,0xb894,0xb895,0xb896,0xb897,0xb898,0xb899,
+ 0xb89a,0xb89b,0xb89c,0xb89d,0xb89e,0xb89f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb8a0,0xb8a1,0xb8a2,0xb8a3,0xb8a4,0xb8a5,0xb8a6,0xb8a7,
+ 0xb8a9,0xb8aa,0xb8ab,0xb8ac,0xb8ad,0xb8ae,0xb8af,0xb8b1,0xb8b2,0xb8b3,
+ 0xb8b5,0xb8b6,0xb8b7,0xb8b9,0xb8ba,0xb8bb,0xb8bc,0xb8bd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb8be,0xb8bf,0xb8c2,0xb8c4,0xb8c6,0xb8c7,
+ 0xb8c8,0xb8c9,0xb8ca,0xb8cb,0xb8cd,0xb8ce,0xb8cf,0xb8d1,0xb8d2,0xb8d3,
+ 0xb8d5,0xb8d6,0xb8d7,0xb8d8,0xb8d9,0xb8da,0xb8db,0xb8dc,0xb8de,0xb8e0,
+ 0xb8e2,0xb8e3,0xb8e4,0xb8e5,0xb8e6,0xb8e7,0xb8ea,0xb8eb,0xb8ed,0xb8ee,
+ 0xb8ef,0xb8f1,0xb8f2,0xb8f3,0xb8f4,0xb8f5,0xb8f6,0xb8f7,0xb8fa,0xb8fc,
+ 0xb8fe,0xb8ff,0xb900,0xb901,0xb902,0xb903,0xb905,0xb906,0xb907,0xb908,
+ 0xb909,0xb90a,0xb90b,0xb90c,0xb90d,0xb90e,0xb90f,0xb910,0xb911,0xb912,
+ 0xb913,0xb914,0xb915,0xb916,0xb917,0xb919,0xb91a,0xb91b,0xb91c,0xb91d,
+ 0xb91e,0xb91f,0xb921,0xb922,0xb923,0xb924,0xb925,0xb926,0xb927,0xb928,
+ 0xb929,0xb92a,0xb92b,0xb92c,0xb92d,0xb92e,0xb92f,0xb930,0xb931,0xb932,
+ 0xb933,0xb934,0xb935,0xb936,0xb937,0xb938,0xb939,0xb93a,0xb93b,0xb93e,
+ 0xb93f,0xb941,0xb942,0xb943,0xb945,0xb946,0xb947,0xb948,0xb949,0xb94a,
+ 0xb94b,0xb94d,0xb94e,0xb950,0xb952,0xb953,0xb954,0xb955,0xb956,0xb957
+ },
+ { /* ku 10 */
+ 0xb95a,0xb95b,0xb95d,0xb95e,0xb95f,0xb961,0xb962,0xb963,0xb964,0xb965,
+ 0xb966,0xb967,0xb96a,0xb96c,0xb96e,0xb96f,0xb970,0xb971,0xb972,0xb973,
+ 0xb976,0xb977,0xb979,0xb97a,0xb97b,0xb97d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb97e,0xb97f,0xb980,0xb981,0xb982,0xb983,0xb986,0xb988,
+ 0xb98b,0xb98c,0xb98f,0xb990,0xb991,0xb992,0xb993,0xb994,0xb995,0xb996,
+ 0xb997,0xb998,0xb999,0xb99a,0xb99b,0xb99c,0xb99d,0xb99e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb99f,0xb9a0,0xb9a1,0xb9a2,0xb9a3,0xb9a4,
+ 0xb9a5,0xb9a6,0xb9a7,0xb9a8,0xb9a9,0xb9aa,0xb9ab,0xb9ae,0xb9af,0xb9b1,
+ 0xb9b2,0xb9b3,0xb9b5,0xb9b6,0xb9b7,0xb9b8,0xb9b9,0xb9ba,0xb9bb,0xb9be,
+ 0xb9c0,0xb9c2,0xb9c3,0xb9c4,0xb9c5,0xb9c6,0xb9c7,0xb9ca,0xb9cb,0xb9cd,
+ 0xb9d3,0xb9d4,0xb9d5,0xb9d6,0xb9d7,0xb9da,0xb9dc,0xb9df,0xb9e0,0xb9e2,
+ 0xb9e6,0xb9e7,0xb9e9,0xb9ea,0xb9eb,0xb9ed,0xb9ee,0xb9ef,0xb9f0,0xb9f1,
+ 0xb9f2,0xb9f3,0xb9f6,0xb9fb,0xb9fc,0xb9fd,0xb9fe,0xb9ff,0xba02,0xba03,
+ 0xba04,0xba05,0xba06,0xba07,0xba09,0xba0a,0xba0b,0xba0c,0xba0d,0xba0e,
+ 0xba0f,0xba10,0xba11,0xba12,0xba13,0xba14,0xba16,0xba17,0xba18,0xba19,
+ 0xba1a,0xba1b,0xba1c,0xba1d,0xba1e,0xba1f,0xba20,0xba21,0xba22,0xba23,
+ 0xba24,0xba25,0xba26,0xba27,0xba28,0xba29,0xba2a,0xba2b,0xba2c,0xba2d,
+ 0xba2e,0xba2f,0xba30,0xba31,0xba32,0xba33,0xba34,0xba35,0xba36,0xba37,
+ 0xba3a,0xba3b,0xba3d,0xba3e,0xba3f,0xba41,0xba43,0xba44,0xba45,0xba46
+ },
+ { /* ku 11 */
+ 0xba47,0xba4a,0xba4c,0xba4f,0xba50,0xba51,0xba52,0xba56,0xba57,0xba59,
+ 0xba5a,0xba5b,0xba5d,0xba5e,0xba5f,0xba60,0xba61,0xba62,0xba63,0xba66,
+ 0xba6a,0xba6b,0xba6c,0xba6d,0xba6e,0xba6f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xba72,0xba73,0xba75,0xba76,0xba77,0xba79,0xba7a,0xba7b,
+ 0xba7c,0xba7d,0xba7e,0xba7f,0xba80,0xba81,0xba82,0xba86,0xba88,0xba89,
+ 0xba8a,0xba8b,0xba8d,0xba8e,0xba8f,0xba90,0xba91,0xba92,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xba93,0xba94,0xba95,0xba96,0xba97,0xba98,
+ 0xba99,0xba9a,0xba9b,0xba9c,0xba9d,0xba9e,0xba9f,0xbaa0,0xbaa1,0xbaa2,
+ 0xbaa3,0xbaa4,0xbaa5,0xbaa6,0xbaa7,0xbaaa,0xbaad,0xbaae,0xbaaf,0xbab1,
+ 0xbab3,0xbab4,0xbab5,0xbab6,0xbab7,0xbaba,0xbabc,0xbabe,0xbabf,0xbac0,
+ 0xbac1,0xbac2,0xbac3,0xbac5,0xbac6,0xbac7,0xbac9,0xbaca,0xbacb,0xbacc,
+ 0xbacd,0xbace,0xbacf,0xbad0,0xbad1,0xbad2,0xbad3,0xbad4,0xbad5,0xbad6,
+ 0xbad7,0xbada,0xbadb,0xbadc,0xbadd,0xbade,0xbadf,0xbae0,0xbae1,0xbae2,
+ 0xbae3,0xbae4,0xbae5,0xbae6,0xbae7,0xbae8,0xbae9,0xbaea,0xbaeb,0xbaec,
+ 0xbaed,0xbaee,0xbaef,0xbaf0,0xbaf1,0xbaf2,0xbaf3,0xbaf4,0xbaf5,0xbaf6,
+ 0xbaf7,0xbaf8,0xbaf9,0xbafa,0xbafb,0xbafd,0xbafe,0xbaff,0xbb01,0xbb02,
+ 0xbb03,0xbb05,0xbb06,0xbb07,0xbb08,0xbb09,0xbb0a,0xbb0b,0xbb0c,0xbb0e,
+ 0xbb10,0xbb12,0xbb13,0xbb14,0xbb15,0xbb16,0xbb17,0xbb19,0xbb1a,0xbb1b,
+ 0xbb1d,0xbb1e,0xbb1f,0xbb21,0xbb22,0xbb23,0xbb24,0xbb25,0xbb26,0xbb27
+ },
+ { /* ku 12 */
+ 0xbb28,0xbb2a,0xbb2c,0xbb2d,0xbb2e,0xbb2f,0xbb30,0xbb31,0xbb32,0xbb33,
+ 0xbb37,0xbb39,0xbb3a,0xbb3f,0xbb40,0xbb41,0xbb42,0xbb43,0xbb46,0xbb48,
+ 0xbb4a,0xbb4b,0xbb4c,0xbb4e,0xbb51,0xbb52,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbb53,0xbb55,0xbb56,0xbb57,0xbb59,0xbb5a,0xbb5b,0xbb5c,
+ 0xbb5d,0xbb5e,0xbb5f,0xbb60,0xbb62,0xbb64,0xbb65,0xbb66,0xbb67,0xbb68,
+ 0xbb69,0xbb6a,0xbb6b,0xbb6d,0xbb6e,0xbb6f,0xbb70,0xbb71,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbb72,0xbb73,0xbb74,0xbb75,0xbb76,0xbb77,
+ 0xbb78,0xbb79,0xbb7a,0xbb7b,0xbb7c,0xbb7d,0xbb7e,0xbb7f,0xbb80,0xbb81,
+ 0xbb82,0xbb83,0xbb84,0xbb85,0xbb86,0xbb87,0xbb89,0xbb8a,0xbb8b,0xbb8d,
+ 0xbb8e,0xbb8f,0xbb91,0xbb92,0xbb93,0xbb94,0xbb95,0xbb96,0xbb97,0xbb98,
+ 0xbb99,0xbb9a,0xbb9b,0xbb9c,0xbb9d,0xbb9e,0xbb9f,0xbba0,0xbba1,0xbba2,
+ 0xbba3,0xbba5,0xbba6,0xbba7,0xbba9,0xbbaa,0xbbab,0xbbad,0xbbae,0xbbaf,
+ 0xbbb0,0xbbb1,0xbbb2,0xbbb3,0xbbb5,0xbbb6,0xbbb8,0xbbb9,0xbbba,0xbbbb,
+ 0xbbbc,0xbbbd,0xbbbe,0xbbbf,0xbbc1,0xbbc2,0xbbc3,0xbbc5,0xbbc6,0xbbc7,
+ 0xbbc9,0xbbca,0xbbcb,0xbbcc,0xbbcd,0xbbce,0xbbcf,0xbbd1,0xbbd2,0xbbd4,
+ 0xbbd5,0xbbd6,0xbbd7,0xbbd8,0xbbd9,0xbbda,0xbbdb,0xbbdc,0xbbdd,0xbbde,
+ 0xbbdf,0xbbe0,0xbbe1,0xbbe2,0xbbe3,0xbbe4,0xbbe5,0xbbe6,0xbbe7,0xbbe8,
+ 0xbbe9,0xbbea,0xbbeb,0xbbec,0xbbed,0xbbee,0xbbef,0xbbf0,0xbbf1,0xbbf2,
+ 0xbbf3,0xbbf4,0xbbf5,0xbbf6,0xbbf7,0xbbfa,0xbbfb,0xbbfd,0xbbfe,0xbc01
+ },
+ { /* ku 13 */
+ 0xbc03,0xbc04,0xbc05,0xbc06,0xbc07,0xbc0a,0xbc0e,0xbc10,0xbc12,0xbc13,
+ 0xbc19,0xbc1a,0xbc20,0xbc21,0xbc22,0xbc23,0xbc26,0xbc28,0xbc2a,0xbc2b,
+ 0xbc2c,0xbc2e,0xbc2f,0xbc32,0xbc33,0xbc35,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbc36,0xbc37,0xbc39,0xbc3a,0xbc3b,0xbc3c,0xbc3d,0xbc3e,
+ 0xbc3f,0xbc42,0xbc46,0xbc47,0xbc48,0xbc4a,0xbc4b,0xbc4e,0xbc4f,0xbc51,
+ 0xbc52,0xbc53,0xbc54,0xbc55,0xbc56,0xbc57,0xbc58,0xbc59,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbc5a,0xbc5b,0xbc5c,0xbc5e,0xbc5f,0xbc60,
+ 0xbc61,0xbc62,0xbc63,0xbc64,0xbc65,0xbc66,0xbc67,0xbc68,0xbc69,0xbc6a,
+ 0xbc6b,0xbc6c,0xbc6d,0xbc6e,0xbc6f,0xbc70,0xbc71,0xbc72,0xbc73,0xbc74,
+ 0xbc75,0xbc76,0xbc77,0xbc78,0xbc79,0xbc7a,0xbc7b,0xbc7c,0xbc7d,0xbc7e,
+ 0xbc7f,0xbc80,0xbc81,0xbc82,0xbc83,0xbc86,0xbc87,0xbc89,0xbc8a,0xbc8d,
+ 0xbc8f,0xbc90,0xbc91,0xbc92,0xbc93,0xbc96,0xbc98,0xbc9b,0xbc9c,0xbc9d,
+ 0xbc9e,0xbc9f,0xbca2,0xbca3,0xbca5,0xbca6,0xbca9,0xbcaa,0xbcab,0xbcac,
+ 0xbcad,0xbcae,0xbcaf,0xbcb2,0xbcb6,0xbcb7,0xbcb8,0xbcb9,0xbcba,0xbcbb,
+ 0xbcbe,0xbcbf,0xbcc1,0xbcc2,0xbcc3,0xbcc5,0xbcc6,0xbcc7,0xbcc8,0xbcc9,
+ 0xbcca,0xbccb,0xbccc,0xbcce,0xbcd2,0xbcd3,0xbcd4,0xbcd6,0xbcd7,0xbcd9,
+ 0xbcda,0xbcdb,0xbcdd,0xbcde,0xbcdf,0xbce0,0xbce1,0xbce2,0xbce3,0xbce4,
+ 0xbce5,0xbce6,0xbce7,0xbce8,0xbce9,0xbcea,0xbceb,0xbcec,0xbced,0xbcee,
+ 0xbcef,0xbcf0,0xbcf1,0xbcf2,0xbcf3,0xbcf7,0xbcf9,0xbcfa,0xbcfb,0xbcfd
+ },
+ { /* ku 14 */
+ 0xbcfe,0xbcff,0xbd00,0xbd01,0xbd02,0xbd03,0xbd06,0xbd08,0xbd0a,0xbd0b,
+ 0xbd0c,0xbd0d,0xbd0e,0xbd0f,0xbd11,0xbd12,0xbd13,0xbd15,0xbd16,0xbd17,
+ 0xbd18,0xbd19,0xbd1a,0xbd1b,0xbd1c,0xbd1d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbd1e,0xbd1f,0xbd20,0xbd21,0xbd22,0xbd23,0xbd25,0xbd26,
+ 0xbd27,0xbd28,0xbd29,0xbd2a,0xbd2b,0xbd2d,0xbd2e,0xbd2f,0xbd30,0xbd31,
+ 0xbd32,0xbd33,0xbd34,0xbd35,0xbd36,0xbd37,0xbd38,0xbd39,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbd3a,0xbd3b,0xbd3c,0xbd3d,0xbd3e,0xbd3f,
+ 0xbd41,0xbd42,0xbd43,0xbd44,0xbd45,0xbd46,0xbd47,0xbd4a,0xbd4b,0xbd4d,
+ 0xbd4e,0xbd4f,0xbd51,0xbd52,0xbd53,0xbd54,0xbd55,0xbd56,0xbd57,0xbd5a,
+ 0xbd5b,0xbd5c,0xbd5d,0xbd5e,0xbd5f,0xbd60,0xbd61,0xbd62,0xbd63,0xbd65,
+ 0xbd66,0xbd67,0xbd69,0xbd6a,0xbd6b,0xbd6c,0xbd6d,0xbd6e,0xbd6f,0xbd70,
+ 0xbd71,0xbd72,0xbd73,0xbd74,0xbd75,0xbd76,0xbd77,0xbd78,0xbd79,0xbd7a,
+ 0xbd7b,0xbd7c,0xbd7d,0xbd7e,0xbd7f,0xbd82,0xbd83,0xbd85,0xbd86,0xbd8b,
+ 0xbd8c,0xbd8d,0xbd8e,0xbd8f,0xbd92,0xbd94,0xbd96,0xbd97,0xbd98,0xbd9b,
+ 0xbd9d,0xbd9e,0xbd9f,0xbda0,0xbda1,0xbda2,0xbda3,0xbda5,0xbda6,0xbda7,
+ 0xbda8,0xbda9,0xbdaa,0xbdab,0xbdac,0xbdad,0xbdae,0xbdaf,0xbdb1,0xbdb2,
+ 0xbdb3,0xbdb4,0xbdb5,0xbdb6,0xbdb7,0xbdb9,0xbdba,0xbdbb,0xbdbc,0xbdbd,
+ 0xbdbe,0xbdbf,0xbdc0,0xbdc1,0xbdc2,0xbdc3,0xbdc4,0xbdc5,0xbdc6,0xbdc7,
+ 0xbdc8,0xbdc9,0xbdca,0xbdcb,0xbdcc,0xbdcd,0xbdce,0xbdcf,0xbdd0,0xbdd1
+ },
+ { /* ku 15 */
+ 0xbdd2,0xbdd3,0xbdd6,0xbdd7,0xbdd9,0xbdda,0xbddb,0xbddd,0xbdde,0xbddf,
+ 0xbde0,0xbde1,0xbde2,0xbde3,0xbde4,0xbde5,0xbde6,0xbde7,0xbde8,0xbdea,
+ 0xbdeb,0xbdec,0xbded,0xbdee,0xbdef,0xbdf1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbdf2,0xbdf3,0xbdf5,0xbdf6,0xbdf7,0xbdf9,0xbdfa,0xbdfb,
+ 0xbdfc,0xbdfd,0xbdfe,0xbdff,0xbe01,0xbe02,0xbe04,0xbe06,0xbe07,0xbe08,
+ 0xbe09,0xbe0a,0xbe0b,0xbe0e,0xbe0f,0xbe11,0xbe12,0xbe13,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbe15,0xbe16,0xbe17,0xbe18,0xbe19,0xbe1a,
+ 0xbe1b,0xbe1e,0xbe20,0xbe21,0xbe22,0xbe23,0xbe24,0xbe25,0xbe26,0xbe27,
+ 0xbe28,0xbe29,0xbe2a,0xbe2b,0xbe2c,0xbe2d,0xbe2e,0xbe2f,0xbe30,0xbe31,
+ 0xbe32,0xbe33,0xbe34,0xbe35,0xbe36,0xbe37,0xbe38,0xbe39,0xbe3a,0xbe3b,
+ 0xbe3c,0xbe3d,0xbe3e,0xbe3f,0xbe40,0xbe41,0xbe42,0xbe43,0xbe46,0xbe47,
+ 0xbe49,0xbe4a,0xbe4b,0xbe4d,0xbe4f,0xbe50,0xbe51,0xbe52,0xbe53,0xbe56,
+ 0xbe58,0xbe5c,0xbe5d,0xbe5e,0xbe5f,0xbe62,0xbe63,0xbe65,0xbe66,0xbe67,
+ 0xbe69,0xbe6b,0xbe6c,0xbe6d,0xbe6e,0xbe6f,0xbe72,0xbe76,0xbe77,0xbe78,
+ 0xbe79,0xbe7a,0xbe7e,0xbe7f,0xbe81,0xbe82,0xbe83,0xbe85,0xbe86,0xbe87,
+ 0xbe88,0xbe89,0xbe8a,0xbe8b,0xbe8e,0xbe92,0xbe93,0xbe94,0xbe95,0xbe96,
+ 0xbe97,0xbe9a,0xbe9b,0xbe9c,0xbe9d,0xbe9e,0xbe9f,0xbea0,0xbea1,0xbea2,
+ 0xbea3,0xbea4,0xbea5,0xbea6,0xbea7,0xbea9,0xbeaa,0xbeab,0xbeac,0xbead,
+ 0xbeae,0xbeaf,0xbeb0,0xbeb1,0xbeb2,0xbeb3,0xbeb4,0xbeb5,0xbeb6,0xbeb7
+ },
+ { /* ku 16 */
+ 0xbeb8,0xbeb9,0xbeba,0xbebb,0xbebc,0xbebd,0xbebe,0xbebf,0xbec0,0xbec1,
+ 0xbec2,0xbec3,0xbec4,0xbec5,0xbec6,0xbec7,0xbec8,0xbec9,0xbeca,0xbecb,
+ 0xbecc,0xbecd,0xbece,0xbecf,0xbed2,0xbed3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbed5,0xbed6,0xbed9,0xbeda,0xbedb,0xbedc,0xbedd,0xbede,
+ 0xbedf,0xbee1,0xbee2,0xbee6,0xbee7,0xbee8,0xbee9,0xbeea,0xbeeb,0xbeed,
+ 0xbeee,0xbeef,0xbef0,0xbef1,0xbef2,0xbef3,0xbef4,0xbef5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbef6,0xbef7,0xbef8,0xbef9,0xbefa,0xbefb,
+ 0xbefc,0xbefd,0xbefe,0xbeff,0xbf00,0xbf02,0xbf03,0xbf04,0xbf05,0xbf06,
+ 0xbf07,0xbf0a,0xbf0b,0xbf0c,0xbf0d,0xbf0e,0xbf0f,0xbf10,0xbf11,0xbf12,
+ 0xbf13,0xbf14,0xbf15,0xbf16,0xbf17,0xbf1a,0xbf1e,0xbf1f,0xbf20,0xbf21,
+ 0xbf22,0xbf23,0xbf24,0xbf25,0xbf26,0xbf27,0xbf28,0xbf29,0xbf2a,0xbf2b,
+ 0xbf2c,0xbf2d,0xbf2e,0xbf2f,0xbf30,0xbf31,0xbf32,0xbf33,0xbf34,0xbf35,
+ 0xbf36,0xbf37,0xbf38,0xbf39,0xbf3a,0xbf3b,0xbf3c,0xbf3d,0xbf3e,0xbf3f,
+ 0xbf42,0xbf43,0xbf45,0xbf46,0xbf47,0xbf49,0xbf4a,0xbf4b,0xbf4c,0xbf4d,
+ 0xbf4e,0xbf4f,0xbf52,0xbf53,0xbf54,0xbf56,0xbf57,0xbf58,0xbf59,0xbf5a,
+ 0xbf5b,0xbf5c,0xbf5d,0xbf5e,0xbf5f,0xbf60,0xbf61,0xbf62,0xbf63,0xbf64,
+ 0xbf65,0xbf66,0xbf67,0xbf68,0xbf69,0xbf6a,0xbf6b,0xbf6c,0xbf6d,0xbf6e,
+ 0xbf6f,0xbf70,0xbf71,0xbf72,0xbf73,0xbf74,0xbf75,0xbf76,0xbf77,0xbf78,
+ 0xbf79,0xbf7a,0xbf7b,0xbf7c,0xbf7d,0xbf7e,0xbf7f,0xbf80,0xbf81,0xbf82
+ },
+ { /* ku 17 */
+ 0xbf83,0xbf84,0xbf85,0xbf86,0xbf87,0xbf88,0xbf89,0xbf8a,0xbf8b,0xbf8c,
+ 0xbf8d,0xbf8e,0xbf8f,0xbf90,0xbf91,0xbf92,0xbf93,0xbf95,0xbf96,0xbf97,
+ 0xbf98,0xbf99,0xbf9a,0xbf9b,0xbf9c,0xbf9d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbf9e,0xbf9f,0xbfa0,0xbfa1,0xbfa2,0xbfa3,0xbfa4,0xbfa5,
+ 0xbfa6,0xbfa7,0xbfa8,0xbfa9,0xbfaa,0xbfab,0xbfac,0xbfad,0xbfae,0xbfaf,
+ 0xbfb1,0xbfb2,0xbfb3,0xbfb4,0xbfb5,0xbfb6,0xbfb7,0xbfb8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbfb9,0xbfba,0xbfbb,0xbfbc,0xbfbd,0xbfbe,
+ 0xbfbf,0xbfc0,0xbfc1,0xbfc2,0xbfc3,0xbfc4,0xbfc6,0xbfc7,0xbfc8,0xbfc9,
+ 0xbfca,0xbfcb,0xbfce,0xbfcf,0xbfd1,0xbfd2,0xbfd3,0xbfd5,0xbfd6,0xbfd7,
+ 0xbfd8,0xbfd9,0xbfda,0xbfdb,0xbfdd,0xbfde,0xbfe0,0xbfe2,0xbfe3,0xbfe4,
+ 0xbfe5,0xbfe6,0xbfe7,0xbfe8,0xbfe9,0xbfea,0xbfeb,0xbfec,0xbfed,0xbfee,
+ 0xbfef,0xbff0,0xbff1,0xbff2,0xbff3,0xbff4,0xbff5,0xbff6,0xbff7,0xbff8,
+ 0xbff9,0xbffa,0xbffb,0xbffc,0xbffd,0xbffe,0xbfff,0xc000,0xc001,0xc002,
+ 0xc003,0xc004,0xc005,0xc006,0xc007,0xc008,0xc009,0xc00a,0xc00b,0xc00c,
+ 0xc00d,0xc00e,0xc00f,0xc010,0xc011,0xc012,0xc013,0xc014,0xc015,0xc016,
+ 0xc017,0xc018,0xc019,0xc01a,0xc01b,0xc01c,0xc01d,0xc01e,0xc01f,0xc020,
+ 0xc021,0xc022,0xc023,0xc024,0xc025,0xc026,0xc027,0xc028,0xc029,0xc02a,
+ 0xc02b,0xc02c,0xc02d,0xc02e,0xc02f,0xc030,0xc031,0xc032,0xc033,0xc034,
+ 0xc035,0xc036,0xc037,0xc038,0xc039,0xc03a,0xc03b,0xc03d,0xc03e,0xc03f
+ },
+ { /* ku 18 */
+ 0xc040,0xc041,0xc042,0xc043,0xc044,0xc045,0xc046,0xc047,0xc048,0xc049,
+ 0xc04a,0xc04b,0xc04c,0xc04d,0xc04e,0xc04f,0xc050,0xc052,0xc053,0xc054,
+ 0xc055,0xc056,0xc057,0xc059,0xc05a,0xc05b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc05d,0xc05e,0xc05f,0xc061,0xc062,0xc063,0xc064,0xc065,
+ 0xc066,0xc067,0xc06a,0xc06b,0xc06c,0xc06d,0xc06e,0xc06f,0xc070,0xc071,
+ 0xc072,0xc073,0xc074,0xc075,0xc076,0xc077,0xc078,0xc079,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc07a,0xc07b,0xc07c,0xc07d,0xc07e,0xc07f,
+ 0xc080,0xc081,0xc082,0xc083,0xc084,0xc085,0xc086,0xc087,0xc088,0xc089,
+ 0xc08a,0xc08b,0xc08c,0xc08d,0xc08e,0xc08f,0xc092,0xc093,0xc095,0xc096,
+ 0xc097,0xc099,0xc09a,0xc09b,0xc09c,0xc09d,0xc09e,0xc09f,0xc0a2,0xc0a4,
+ 0xc0a6,0xc0a7,0xc0a8,0xc0a9,0xc0aa,0xc0ab,0xc0ae,0xc0b1,0xc0b2,0xc0b7,
+ 0xc0b8,0xc0b9,0xc0ba,0xc0bb,0xc0be,0xc0c2,0xc0c3,0xc0c4,0xc0c6,0xc0c7,
+ 0xc0ca,0xc0cb,0xc0cd,0xc0ce,0xc0cf,0xc0d1,0xc0d2,0xc0d3,0xc0d4,0xc0d5,
+ 0xc0d6,0xc0d7,0xc0da,0xc0de,0xc0df,0xc0e0,0xc0e1,0xc0e2,0xc0e3,0xc0e6,
+ 0xc0e7,0xc0e9,0xc0ea,0xc0eb,0xc0ed,0xc0ee,0xc0ef,0xc0f0,0xc0f1,0xc0f2,
+ 0xc0f3,0xc0f6,0xc0f8,0xc0fa,0xc0fb,0xc0fc,0xc0fd,0xc0fe,0xc0ff,0xc101,
+ 0xc102,0xc103,0xc105,0xc106,0xc107,0xc109,0xc10a,0xc10b,0xc10c,0xc10d,
+ 0xc10e,0xc10f,0xc111,0xc112,0xc113,0xc114,0xc116,0xc117,0xc118,0xc119,
+ 0xc11a,0xc11b,0xc121,0xc122,0xc125,0xc128,0xc129,0xc12a,0xc12b,0xc12e
+ },
+ { /* ku 19 */
+ 0xc132,0xc133,0xc134,0xc135,0xc137,0xc13a,0xc13b,0xc13d,0xc13e,0xc13f,
+ 0xc141,0xc142,0xc143,0xc144,0xc145,0xc146,0xc147,0xc14a,0xc14e,0xc14f,
+ 0xc150,0xc151,0xc152,0xc153,0xc156,0xc157,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc159,0xc15a,0xc15b,0xc15d,0xc15e,0xc15f,0xc160,0xc161,
+ 0xc162,0xc163,0xc166,0xc16a,0xc16b,0xc16c,0xc16d,0xc16e,0xc16f,0xc171,
+ 0xc172,0xc173,0xc175,0xc176,0xc177,0xc179,0xc17a,0xc17b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc17c,0xc17d,0xc17e,0xc17f,0xc180,0xc181,
+ 0xc182,0xc183,0xc184,0xc186,0xc187,0xc188,0xc189,0xc18a,0xc18b,0xc18f,
+ 0xc191,0xc192,0xc193,0xc195,0xc197,0xc198,0xc199,0xc19a,0xc19b,0xc19e,
+ 0xc1a0,0xc1a2,0xc1a3,0xc1a4,0xc1a6,0xc1a7,0xc1aa,0xc1ab,0xc1ad,0xc1ae,
+ 0xc1af,0xc1b1,0xc1b2,0xc1b3,0xc1b4,0xc1b5,0xc1b6,0xc1b7,0xc1b8,0xc1b9,
+ 0xc1ba,0xc1bb,0xc1bc,0xc1be,0xc1bf,0xc1c0,0xc1c1,0xc1c2,0xc1c3,0xc1c5,
+ 0xc1c6,0xc1c7,0xc1c9,0xc1ca,0xc1cb,0xc1cd,0xc1ce,0xc1cf,0xc1d0,0xc1d1,
+ 0xc1d2,0xc1d3,0xc1d5,0xc1d6,0xc1d9,0xc1da,0xc1db,0xc1dc,0xc1dd,0xc1de,
+ 0xc1df,0xc1e1,0xc1e2,0xc1e3,0xc1e5,0xc1e6,0xc1e7,0xc1e9,0xc1ea,0xc1eb,
+ 0xc1ec,0xc1ed,0xc1ee,0xc1ef,0xc1f2,0xc1f4,0xc1f5,0xc1f6,0xc1f7,0xc1f8,
+ 0xc1f9,0xc1fa,0xc1fb,0xc1fe,0xc1ff,0xc201,0xc202,0xc203,0xc205,0xc206,
+ 0xc207,0xc208,0xc209,0xc20a,0xc20b,0xc20e,0xc210,0xc212,0xc213,0xc214,
+ 0xc215,0xc216,0xc217,0xc21a,0xc21b,0xc21d,0xc21e,0xc221,0xc222,0xc223
+ },
+ { /* ku 1a */
+ 0xc224,0xc225,0xc226,0xc227,0xc22a,0xc22c,0xc22e,0xc230,0xc233,0xc235,
+ 0xc236,0xc237,0xc238,0xc239,0xc23a,0xc23b,0xc23c,0xc23d,0xc23e,0xc23f,
+ 0xc240,0xc241,0xc242,0xc243,0xc244,0xc245,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc246,0xc247,0xc249,0xc24a,0xc24b,0xc24c,0xc24d,0xc24e,
+ 0xc24f,0xc252,0xc253,0xc255,0xc256,0xc257,0xc259,0xc25a,0xc25b,0xc25c,
+ 0xc25d,0xc25e,0xc25f,0xc261,0xc262,0xc263,0xc264,0xc266,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc267,0xc268,0xc269,0xc26a,0xc26b,0xc26e,
+ 0xc26f,0xc271,0xc272,0xc273,0xc275,0xc276,0xc277,0xc278,0xc279,0xc27a,
+ 0xc27b,0xc27e,0xc280,0xc282,0xc283,0xc284,0xc285,0xc286,0xc287,0xc28a,
+ 0xc28b,0xc28c,0xc28d,0xc28e,0xc28f,0xc291,0xc292,0xc293,0xc294,0xc295,
+ 0xc296,0xc297,0xc299,0xc29a,0xc29c,0xc29e,0xc29f,0xc2a0,0xc2a1,0xc2a2,
+ 0xc2a3,0xc2a6,0xc2a7,0xc2a9,0xc2aa,0xc2ab,0xc2ae,0xc2af,0xc2b0,0xc2b1,
+ 0xc2b2,0xc2b3,0xc2b6,0xc2b8,0xc2ba,0xc2bb,0xc2bc,0xc2bd,0xc2be,0xc2bf,
+ 0xc2c0,0xc2c1,0xc2c2,0xc2c3,0xc2c4,0xc2c5,0xc2c6,0xc2c7,0xc2c8,0xc2c9,
+ 0xc2ca,0xc2cb,0xc2cc,0xc2cd,0xc2ce,0xc2cf,0xc2d0,0xc2d1,0xc2d2,0xc2d3,
+ 0xc2d4,0xc2d5,0xc2d6,0xc2d7,0xc2d8,0xc2d9,0xc2da,0xc2db,0xc2de,0xc2df,
+ 0xc2e1,0xc2e2,0xc2e5,0xc2e6,0xc2e7,0xc2e8,0xc2e9,0xc2ea,0xc2ee,0xc2f0,
+ 0xc2f2,0xc2f3,0xc2f4,0xc2f5,0xc2f7,0xc2fa,0xc2fd,0xc2fe,0xc2ff,0xc301,
+ 0xc302,0xc303,0xc304,0xc305,0xc306,0xc307,0xc30a,0xc30b,0xc30e,0xc30f
+ },
+ { /* ku 1b */
+ 0xc310,0xc311,0xc312,0xc316,0xc317,0xc319,0xc31a,0xc31b,0xc31d,0xc31e,
+ 0xc31f,0xc320,0xc321,0xc322,0xc323,0xc326,0xc327,0xc32a,0xc32b,0xc32c,
+ 0xc32d,0xc32e,0xc32f,0xc330,0xc331,0xc332,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc333,0xc334,0xc335,0xc336,0xc337,0xc338,0xc339,0xc33a,
+ 0xc33b,0xc33c,0xc33d,0xc33e,0xc33f,0xc340,0xc341,0xc342,0xc343,0xc344,
+ 0xc346,0xc347,0xc348,0xc349,0xc34a,0xc34b,0xc34c,0xc34d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc34e,0xc34f,0xc350,0xc351,0xc352,0xc353,
+ 0xc354,0xc355,0xc356,0xc357,0xc358,0xc359,0xc35a,0xc35b,0xc35c,0xc35d,
+ 0xc35e,0xc35f,0xc360,0xc361,0xc362,0xc363,0xc364,0xc365,0xc366,0xc367,
+ 0xc36a,0xc36b,0xc36d,0xc36e,0xc36f,0xc371,0xc373,0xc374,0xc375,0xc376,
+ 0xc377,0xc37a,0xc37b,0xc37e,0xc37f,0xc380,0xc381,0xc382,0xc383,0xc385,
+ 0xc386,0xc387,0xc389,0xc38a,0xc38b,0xc38d,0xc38e,0xc38f,0xc390,0xc391,
+ 0xc392,0xc393,0xc394,0xc395,0xc396,0xc397,0xc398,0xc399,0xc39a,0xc39b,
+ 0xc39c,0xc39d,0xc39e,0xc39f,0xc3a0,0xc3a1,0xc3a2,0xc3a3,0xc3a4,0xc3a5,
+ 0xc3a6,0xc3a7,0xc3a8,0xc3a9,0xc3aa,0xc3ab,0xc3ac,0xc3ad,0xc3ae,0xc3af,
+ 0xc3b0,0xc3b1,0xc3b2,0xc3b3,0xc3b4,0xc3b5,0xc3b6,0xc3b7,0xc3b8,0xc3b9,
+ 0xc3ba,0xc3bb,0xc3bc,0xc3bd,0xc3be,0xc3bf,0xc3c1,0xc3c2,0xc3c3,0xc3c4,
+ 0xc3c5,0xc3c6,0xc3c7,0xc3c8,0xc3c9,0xc3ca,0xc3cb,0xc3cc,0xc3cd,0xc3ce,
+ 0xc3cf,0xc3d0,0xc3d1,0xc3d2,0xc3d3,0xc3d4,0xc3d5,0xc3d6,0xc3d7,0xc3da
+ },
+ { /* ku 1c */
+ 0xc3db,0xc3dd,0xc3de,0xc3e1,0xc3e3,0xc3e4,0xc3e5,0xc3e6,0xc3e7,0xc3ea,
+ 0xc3eb,0xc3ec,0xc3ee,0xc3ef,0xc3f0,0xc3f1,0xc3f2,0xc3f3,0xc3f6,0xc3f7,
+ 0xc3f9,0xc3fa,0xc3fb,0xc3fc,0xc3fd,0xc3fe,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc3ff,0xc400,0xc401,0xc402,0xc403,0xc404,0xc405,0xc406,
+ 0xc407,0xc409,0xc40a,0xc40b,0xc40c,0xc40d,0xc40e,0xc40f,0xc411,0xc412,
+ 0xc413,0xc414,0xc415,0xc416,0xc417,0xc418,0xc419,0xc41a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc41b,0xc41c,0xc41d,0xc41e,0xc41f,0xc420,
+ 0xc421,0xc422,0xc423,0xc425,0xc426,0xc427,0xc428,0xc429,0xc42a,0xc42b,
+ 0xc42d,0xc42e,0xc42f,0xc431,0xc432,0xc433,0xc435,0xc436,0xc437,0xc438,
+ 0xc439,0xc43a,0xc43b,0xc43e,0xc43f,0xc440,0xc441,0xc442,0xc443,0xc444,
+ 0xc445,0xc446,0xc447,0xc449,0xc44a,0xc44b,0xc44c,0xc44d,0xc44e,0xc44f,
+ 0xc450,0xc451,0xc452,0xc453,0xc454,0xc455,0xc456,0xc457,0xc458,0xc459,
+ 0xc45a,0xc45b,0xc45c,0xc45d,0xc45e,0xc45f,0xc460,0xc461,0xc462,0xc463,
+ 0xc466,0xc467,0xc469,0xc46a,0xc46b,0xc46d,0xc46e,0xc46f,0xc470,0xc471,
+ 0xc472,0xc473,0xc476,0xc477,0xc478,0xc47a,0xc47b,0xc47c,0xc47d,0xc47e,
+ 0xc47f,0xc481,0xc482,0xc483,0xc484,0xc485,0xc486,0xc487,0xc488,0xc489,
+ 0xc48a,0xc48b,0xc48c,0xc48d,0xc48e,0xc48f,0xc490,0xc491,0xc492,0xc493,
+ 0xc495,0xc496,0xc497,0xc498,0xc499,0xc49a,0xc49b,0xc49d,0xc49e,0xc49f,
+ 0xc4a0,0xc4a1,0xc4a2,0xc4a3,0xc4a4,0xc4a5,0xc4a6,0xc4a7,0xc4a8,0xc4a9
+ },
+ { /* ku 1d */
+ 0xc4aa,0xc4ab,0xc4ac,0xc4ad,0xc4ae,0xc4af,0xc4b0,0xc4b1,0xc4b2,0xc4b3,
+ 0xc4b4,0xc4b5,0xc4b6,0xc4b7,0xc4b9,0xc4ba,0xc4bb,0xc4bd,0xc4be,0xc4bf,
+ 0xc4c0,0xc4c1,0xc4c2,0xc4c3,0xc4c4,0xc4c5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc4c6,0xc4c7,0xc4c8,0xc4c9,0xc4ca,0xc4cb,0xc4cc,0xc4cd,
+ 0xc4ce,0xc4cf,0xc4d0,0xc4d1,0xc4d2,0xc4d3,0xc4d4,0xc4d5,0xc4d6,0xc4d7,
+ 0xc4d8,0xc4d9,0xc4da,0xc4db,0xc4dc,0xc4dd,0xc4de,0xc4df,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc4e0,0xc4e1,0xc4e2,0xc4e3,0xc4e4,0xc4e5,
+ 0xc4e6,0xc4e7,0xc4e8,0xc4ea,0xc4eb,0xc4ec,0xc4ed,0xc4ee,0xc4ef,0xc4f2,
+ 0xc4f3,0xc4f5,0xc4f6,0xc4f7,0xc4f9,0xc4fb,0xc4fc,0xc4fd,0xc4fe,0xc502,
+ 0xc503,0xc504,0xc505,0xc506,0xc507,0xc508,0xc509,0xc50a,0xc50b,0xc50d,
+ 0xc50e,0xc50f,0xc511,0xc512,0xc513,0xc515,0xc516,0xc517,0xc518,0xc519,
+ 0xc51a,0xc51b,0xc51d,0xc51e,0xc51f,0xc520,0xc521,0xc522,0xc523,0xc524,
+ 0xc525,0xc526,0xc527,0xc52a,0xc52b,0xc52d,0xc52e,0xc52f,0xc531,0xc532,
+ 0xc533,0xc534,0xc535,0xc536,0xc537,0xc53a,0xc53c,0xc53e,0xc53f,0xc540,
+ 0xc541,0xc542,0xc543,0xc546,0xc547,0xc54b,0xc54f,0xc550,0xc551,0xc552,
+ 0xc556,0xc55a,0xc55b,0xc55c,0xc55f,0xc562,0xc563,0xc565,0xc566,0xc567,
+ 0xc569,0xc56a,0xc56b,0xc56c,0xc56d,0xc56e,0xc56f,0xc572,0xc576,0xc577,
+ 0xc578,0xc579,0xc57a,0xc57b,0xc57e,0xc57f,0xc581,0xc582,0xc583,0xc585,
+ 0xc586,0xc588,0xc589,0xc58a,0xc58b,0xc58e,0xc590,0xc592,0xc593,0xc594
+ },
+ { /* ku 1e */
+ 0xc596,0xc599,0xc59a,0xc59b,0xc59d,0xc59e,0xc59f,0xc5a1,0xc5a2,0xc5a3,
+ 0xc5a4,0xc5a5,0xc5a6,0xc5a7,0xc5a8,0xc5aa,0xc5ab,0xc5ac,0xc5ad,0xc5ae,
+ 0xc5af,0xc5b0,0xc5b1,0xc5b2,0xc5b3,0xc5b6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc5b7,0xc5ba,0xc5bf,0xc5c0,0xc5c1,0xc5c2,0xc5c3,0xc5cb,
+ 0xc5cd,0xc5cf,0xc5d2,0xc5d3,0xc5d5,0xc5d6,0xc5d7,0xc5d9,0xc5da,0xc5db,
+ 0xc5dc,0xc5dd,0xc5de,0xc5df,0xc5e2,0xc5e4,0xc5e6,0xc5e7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc5e8,0xc5e9,0xc5ea,0xc5eb,0xc5ef,0xc5f1,
+ 0xc5f2,0xc5f3,0xc5f5,0xc5f8,0xc5f9,0xc5fa,0xc5fb,0xc602,0xc603,0xc604,
+ 0xc609,0xc60a,0xc60b,0xc60d,0xc60e,0xc60f,0xc611,0xc612,0xc613,0xc614,
+ 0xc615,0xc616,0xc617,0xc61a,0xc61d,0xc61e,0xc61f,0xc620,0xc621,0xc622,
+ 0xc623,0xc626,0xc627,0xc629,0xc62a,0xc62b,0xc62f,0xc631,0xc632,0xc636,
+ 0xc638,0xc63a,0xc63c,0xc63d,0xc63e,0xc63f,0xc642,0xc643,0xc645,0xc646,
+ 0xc647,0xc649,0xc64a,0xc64b,0xc64c,0xc64d,0xc64e,0xc64f,0xc652,0xc656,
+ 0xc657,0xc658,0xc659,0xc65a,0xc65b,0xc65e,0xc65f,0xc661,0xc662,0xc663,
+ 0xc664,0xc665,0xc666,0xc667,0xc668,0xc669,0xc66a,0xc66b,0xc66d,0xc66e,
+ 0xc670,0xc672,0xc673,0xc674,0xc675,0xc676,0xc677,0xc67a,0xc67b,0xc67d,
+ 0xc67e,0xc67f,0xc681,0xc682,0xc683,0xc684,0xc685,0xc686,0xc687,0xc68a,
+ 0xc68c,0xc68e,0xc68f,0xc690,0xc691,0xc692,0xc693,0xc696,0xc697,0xc699,
+ 0xc69a,0xc69b,0xc69d,0xc69e,0xc69f,0xc6a0,0xc6a1,0xc6a2,0xc6a3,0xc6a6
+ },
+ { /* ku 1f */
+ 0xc6a8,0xc6aa,0xc6ab,0xc6ac,0xc6ad,0xc6ae,0xc6af,0xc6b2,0xc6b3,0xc6b5,
+ 0xc6b6,0xc6b7,0xc6bb,0xc6bc,0xc6bd,0xc6be,0xc6bf,0xc6c2,0xc6c4,0xc6c6,
+ 0xc6c7,0xc6c8,0xc6c9,0xc6ca,0xc6cb,0xc6ce,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc6cf,0xc6d1,0xc6d2,0xc6d3,0xc6d5,0xc6d6,0xc6d7,0xc6d8,
+ 0xc6d9,0xc6da,0xc6db,0xc6de,0xc6df,0xc6e2,0xc6e3,0xc6e4,0xc6e5,0xc6e6,
+ 0xc6e7,0xc6ea,0xc6eb,0xc6ed,0xc6ee,0xc6ef,0xc6f1,0xc6f2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc6f3,0xc6f4,0xc6f5,0xc6f6,0xc6f7,0xc6fa,
+ 0xc6fb,0xc6fc,0xc6fe,0xc6ff,0xc700,0xc701,0xc702,0xc703,0xc706,0xc707,
+ 0xc709,0xc70a,0xc70b,0xc70d,0xc70e,0xc70f,0xc710,0xc711,0xc712,0xc713,
+ 0xc716,0xc718,0xc71a,0xc71b,0xc71c,0xc71d,0xc71e,0xc71f,0xc722,0xc723,
+ 0xc725,0xc726,0xc727,0xc729,0xc72a,0xc72b,0xc72c,0xc72d,0xc72e,0xc72f,
+ 0xc732,0xc734,0xc736,0xc738,0xc739,0xc73a,0xc73b,0xc73e,0xc73f,0xc741,
+ 0xc742,0xc743,0xc745,0xc746,0xc747,0xc748,0xc749,0xc74b,0xc74e,0xc750,
+ 0xc759,0xc75a,0xc75b,0xc75d,0xc75e,0xc75f,0xc761,0xc762,0xc763,0xc764,
+ 0xc765,0xc766,0xc767,0xc769,0xc76a,0xc76c,0xc76d,0xc76e,0xc76f,0xc770,
+ 0xc771,0xc772,0xc773,0xc776,0xc777,0xc779,0xc77a,0xc77b,0xc77f,0xc780,
+ 0xc781,0xc782,0xc786,0xc78b,0xc78c,0xc78d,0xc78f,0xc792,0xc793,0xc795,
+ 0xc799,0xc79b,0xc79c,0xc79d,0xc79e,0xc79f,0xc7a2,0xc7a7,0xc7a8,0xc7a9,
+ 0xc7aa,0xc7ab,0xc7ae,0xc7af,0xc7b1,0xc7b2,0xc7b3,0xc7b5,0xc7b6,0xc7b7
+ },
+ { /* ku 20 */
+ 0xc7b8,0xc7b9,0xc7ba,0xc7bb,0xc7be,0xc7c2,0xc7c3,0xc7c4,0xc7c5,0xc7c6,
+ 0xc7c7,0xc7ca,0xc7cb,0xc7cd,0xc7cf,0xc7d1,0xc7d2,0xc7d3,0xc7d4,0xc7d5,
+ 0xc7d6,0xc7d7,0xc7d9,0xc7da,0xc7db,0xc7dc,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc7de,0xc7df,0xc7e0,0xc7e1,0xc7e2,0xc7e3,0xc7e5,0xc7e6,
+ 0xc7e7,0xc7e9,0xc7ea,0xc7eb,0xc7ed,0xc7ee,0xc7ef,0xc7f0,0xc7f1,0xc7f2,
+ 0xc7f3,0xc7f4,0xc7f5,0xc7f6,0xc7f7,0xc7f8,0xc7f9,0xc7fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc7fb,0xc7fc,0xc7fd,0xc7fe,0xc7ff,0xc802,
+ 0xc803,0xc805,0xc806,0xc807,0xc809,0xc80b,0xc80c,0xc80d,0xc80e,0xc80f,
+ 0xc812,0xc814,0xc817,0xc818,0xc819,0xc81a,0xc81b,0xc81e,0xc81f,0xc821,
+ 0xc822,0xc823,0xc825,0xc826,0xc827,0xc828,0xc829,0xc82a,0xc82b,0xc82e,
+ 0xc830,0xc832,0xc833,0xc834,0xc835,0xc836,0xc837,0xc839,0xc83a,0xc83b,
+ 0xc83d,0xc83e,0xc83f,0xc841,0xc842,0xc843,0xc844,0xc845,0xc846,0xc847,
+ 0xc84a,0xc84b,0xc84e,0xc84f,0xc850,0xc851,0xc852,0xc853,0xc855,0xc856,
+ 0xc857,0xc858,0xc859,0xc85a,0xc85b,0xc85c,0xc85d,0xc85e,0xc85f,0xc860,
+ 0xc861,0xc862,0xc863,0xc864,0xc865,0xc866,0xc867,0xc868,0xc869,0xc86a,
+ 0xc86b,0xc86c,0xc86d,0xc86e,0xc86f,0xc872,0xc873,0xc875,0xc876,0xc877,
+ 0xc879,0xc87b,0xc87c,0xc87d,0xc87e,0xc87f,0xc882,0xc884,0xc888,0xc889,
+ 0xc88a,0xc88e,0xc88f,0xc890,0xc891,0xc892,0xc893,0xc895,0xc896,0xc897,
+ 0xc898,0xc899,0xc89a,0xc89b,0xc89c,0xc89e,0xc8a0,0xc8a2,0xc8a3,0xc8a4
+ },
+ { /* ku 21 */
+ 0xc8a5,0xc8a6,0xc8a7,0xc8a9,0xc8aa,0xc8ab,0xc8ac,0xc8ad,0xc8ae,0xc8af,
+ 0xc8b0,0xc8b1,0xc8b2,0xc8b3,0xc8b4,0xc8b5,0xc8b6,0xc8b7,0xc8b8,0xc8b9,
+ 0xc8ba,0xc8bb,0xc8be,0xc8bf,0xc8c0,0xc8c1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc8c2,0xc8c3,0xc8c5,0xc8c6,0xc8c7,0xc8c9,0xc8ca,0xc8cb,
+ 0xc8cd,0xc8ce,0xc8cf,0xc8d0,0xc8d1,0xc8d2,0xc8d3,0xc8d6,0xc8d8,0xc8da,
+ 0xc8db,0xc8dc,0xc8dd,0xc8de,0xc8df,0xc8e2,0xc8e3,0xc8e5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc8e6,0xc8e7,0xc8e8,0xc8e9,0xc8ea,0xc8eb,
+ 0xc8ec,0xc8ed,0xc8ee,0xc8ef,0xc8f0,0xc8f1,0xc8f2,0xc8f3,0xc8f4,0xc8f6,
+ 0xc8f7,0xc8f8,0xc8f9,0xc8fa,0xc8fb,0xc8fe,0xc8ff,0xc901,0xc902,0xc903,
+ 0xc907,0xc908,0xc909,0xc90a,0xc90b,0xc90e,0x3000,0x3001,0x3002,0x00b7,
+ 0x2025,0x2026,0x00a8,0x3003,0x00ad,0x2015,0x2225,0xff3c,0x223c,0x2018,
+ 0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,0x3009,0x300a,0x300b,0x300c,
+ 0x300d,0x300e,0x300f,0x3010,0x3011,0x00b1,0x00d7,0x00f7,0x2260,0x2264,
+ 0x2265,0x221e,0x2234,0x00b0,0x2032,0x2033,0x2103,0x212b,0xffe0,0xffe1,
+ 0xffe5,0x2642,0x2640,0x2220,0x22a5,0x2312,0x2202,0x2207,0x2261,0x2252,
+ 0x00a7,0x203b,0x2606,0x2605,0x25cb,0x25cf,0x25ce,0x25c7,0x25c6,0x25a1,
+ 0x25a0,0x25b3,0x25b2,0x25bd,0x25bc,0x2192,0x2190,0x2191,0x2193,0x2194,
+ 0x3013,0x226a,0x226b,0x221a,0x223d,0x221d,0x2235,0x222b,0x222c,0x2208,
+ 0x220b,0x2286,0x2287,0x2282,0x2283,0x222a,0x2229,0x2227,0x2228,0xffe2
+ },
+ { /* ku 22 */
+ 0xc910,0xc912,0xc913,0xc914,0xc915,0xc916,0xc917,0xc919,0xc91a,0xc91b,
+ 0xc91c,0xc91d,0xc91e,0xc91f,0xc920,0xc921,0xc922,0xc923,0xc924,0xc925,
+ 0xc926,0xc927,0xc928,0xc929,0xc92a,0xc92b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc92d,0xc92e,0xc92f,0xc930,0xc931,0xc932,0xc933,0xc935,
+ 0xc936,0xc937,0xc938,0xc939,0xc93a,0xc93b,0xc93c,0xc93d,0xc93e,0xc93f,
+ 0xc940,0xc941,0xc942,0xc943,0xc944,0xc945,0xc946,0xc947,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc948,0xc949,0xc94a,0xc94b,0xc94c,0xc94d,
+ 0xc94e,0xc94f,0xc952,0xc953,0xc955,0xc956,0xc957,0xc959,0xc95a,0xc95b,
+ 0xc95c,0xc95d,0xc95e,0xc95f,0xc962,0xc964,0xc965,0xc966,0xc967,0xc968,
+ 0xc969,0xc96a,0xc96b,0xc96d,0xc96e,0xc96f,0x21d2,0x21d4,0x2200,0x2203,
+ 0x00b4,0xff5e,0x02c7,0x02d8,0x02dd,0x02da,0x02d9,0x00b8,0x02db,0x00a1,
+ 0x00bf,0x02d0,0x222e,0x2211,0x220f,0x00a4,0x2109,0x2030,0x25c1,0x25c0,
+ 0x25b7,0x25b6,0x2664,0x2660,0x2661,0x2665,0x2667,0x2663,0x2299,0x25c8,
+ 0x25a3,0x25d0,0x25d1,0x2592,0x25a4,0x25a5,0x25a8,0x25a7,0x25a6,0x25a9,
+ 0x2668,0x260f,0x260e,0x261c,0x261e,0x00b6,0x2020,0x2021,0x2195,0x2197,
+ 0x2199,0x2196,0x2198,0x266d,0x2669,0x266a,0x266c,0x327f,0x321c,0x2116,
+ 0x33c7,0x2122,0x33c2,0x33d8,0x2121,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ 0xc971,0xc972,0xc973,0xc975,0xc976,0xc977,0xc978,0xc979,0xc97a,0xc97b,
+ 0xc97d,0xc97e,0xc97f,0xc980,0xc981,0xc982,0xc983,0xc984,0xc985,0xc986,
+ 0xc987,0xc98a,0xc98b,0xc98d,0xc98e,0xc98f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc991,0xc992,0xc993,0xc994,0xc995,0xc996,0xc997,0xc99a,
+ 0xc99c,0xc99e,0xc99f,0xc9a0,0xc9a1,0xc9a2,0xc9a3,0xc9a4,0xc9a5,0xc9a6,
+ 0xc9a7,0xc9a8,0xc9a9,0xc9aa,0xc9ab,0xc9ac,0xc9ad,0xc9ae,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc9af,0xc9b0,0xc9b1,0xc9b2,0xc9b3,0xc9b4,
+ 0xc9b5,0xc9b6,0xc9b7,0xc9b8,0xc9b9,0xc9ba,0xc9bb,0xc9bc,0xc9bd,0xc9be,
+ 0xc9bf,0xc9c2,0xc9c3,0xc9c5,0xc9c6,0xc9c9,0xc9cb,0xc9cc,0xc9cd,0xc9ce,
+ 0xc9cf,0xc9d2,0xc9d4,0xc9d7,0xc9d8,0xc9db,0xff01,0xff02,0xff03,0xff04,
+ 0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,0xff0b,0xff0c,0xff0d,0xff0e,
+ 0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,0xff18,
+ 0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,0xff1f,0xff20,0xff21,0xff22,
+ 0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,0xff2c,
+ 0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,0xff36,
+ 0xff37,0xff38,0xff39,0xff3a,0xff3b,0xffe6,0xff3d,0xff3e,0xff3f,0xff40,
+ 0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,0xff4a,
+ 0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,0xff54,
+ 0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,0xff5b,0xff5c,0xff5d,0xffe3
+ },
+ { /* ku 24 */
+ 0xc9de,0xc9df,0xc9e1,0xc9e3,0xc9e5,0xc9e6,0xc9e8,0xc9e9,0xc9ea,0xc9eb,
+ 0xc9ee,0xc9f2,0xc9f3,0xc9f4,0xc9f5,0xc9f6,0xc9f7,0xc9fa,0xc9fb,0xc9fd,
+ 0xc9fe,0xc9ff,0xca01,0xca02,0xca03,0xca04,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xca05,0xca06,0xca07,0xca0a,0xca0e,0xca0f,0xca10,0xca11,
+ 0xca12,0xca13,0xca15,0xca16,0xca17,0xca19,0xca1a,0xca1b,0xca1c,0xca1d,
+ 0xca1e,0xca1f,0xca20,0xca21,0xca22,0xca23,0xca24,0xca25,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xca26,0xca27,0xca28,0xca2a,0xca2b,0xca2c,
+ 0xca2d,0xca2e,0xca2f,0xca30,0xca31,0xca32,0xca33,0xca34,0xca35,0xca36,
+ 0xca37,0xca38,0xca39,0xca3a,0xca3b,0xca3c,0xca3d,0xca3e,0xca3f,0xca40,
+ 0xca41,0xca42,0xca43,0xca44,0xca45,0xca46,0x3131,0x3132,0x3133,0x3134,
+ 0x3135,0x3136,0x3137,0x3138,0x3139,0x313a,0x313b,0x313c,0x313d,0x313e,
+ 0x313f,0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,0x3148,
+ 0x3149,0x314a,0x314b,0x314c,0x314d,0x314e,0x314f,0x3150,0x3151,0x3152,
+ 0x3153,0x3154,0x3155,0x3156,0x3157,0x3158,0x3159,0x315a,0x315b,0x315c,
+ 0x315d,0x315e,0x315f,0x3160,0x3161,0x3162,0x3163,0x3164,0x3165,0x3166,
+ 0x3167,0x3168,0x3169,0x316a,0x316b,0x316c,0x316d,0x316e,0x316f,0x3170,
+ 0x3171,0x3172,0x3173,0x3174,0x3175,0x3176,0x3177,0x3178,0x3179,0x317a,
+ 0x317b,0x317c,0x317d,0x317e,0x317f,0x3180,0x3181,0x3182,0x3183,0x3184,
+ 0x3185,0x3186,0x3187,0x3188,0x3189,0x318a,0x318b,0x318c,0x318d,0x318e
+ },
+ { /* ku 25 */
+ 0xca47,0xca48,0xca49,0xca4a,0xca4b,0xca4e,0xca4f,0xca51,0xca52,0xca53,
+ 0xca55,0xca56,0xca57,0xca58,0xca59,0xca5a,0xca5b,0xca5e,0xca62,0xca63,
+ 0xca64,0xca65,0xca66,0xca67,0xca69,0xca6a,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xca6b,0xca6c,0xca6d,0xca6e,0xca6f,0xca70,0xca71,0xca72,
+ 0xca73,0xca74,0xca75,0xca76,0xca77,0xca78,0xca79,0xca7a,0xca7b,0xca7c,
+ 0xca7e,0xca7f,0xca80,0xca81,0xca82,0xca83,0xca85,0xca86,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xca87,0xca88,0xca89,0xca8a,0xca8b,0xca8c,
+ 0xca8d,0xca8e,0xca8f,0xca90,0xca91,0xca92,0xca93,0xca94,0xca95,0xca96,
+ 0xca97,0xca99,0xca9a,0xca9b,0xca9c,0xca9d,0xca9e,0xca9f,0xcaa0,0xcaa1,
+ 0xcaa2,0xcaa3,0xcaa4,0xcaa5,0xcaa6,0xcaa7,0x2170,0x2171,0x2172,0x2173,
+ 0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,
+ 0x2169,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0391,0x0392,
+ 0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,
+ 0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,
+ 0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,
+ 0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ 0xcaa8,0xcaa9,0xcaaa,0xcaab,0xcaac,0xcaad,0xcaae,0xcaaf,0xcab0,0xcab1,
+ 0xcab2,0xcab3,0xcab4,0xcab5,0xcab6,0xcab7,0xcab8,0xcab9,0xcaba,0xcabb,
+ 0xcabe,0xcabf,0xcac1,0xcac2,0xcac3,0xcac5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcac6,0xcac7,0xcac8,0xcac9,0xcaca,0xcacb,0xcace,0xcad0,
+ 0xcad2,0xcad4,0xcad5,0xcad6,0xcad7,0xcada,0xcadb,0xcadc,0xcadd,0xcade,
+ 0xcadf,0xcae1,0xcae2,0xcae3,0xcae4,0xcae5,0xcae6,0xcae7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcae8,0xcae9,0xcaea,0xcaeb,0xcaed,0xcaee,
+ 0xcaef,0xcaf0,0xcaf1,0xcaf2,0xcaf3,0xcaf5,0xcaf6,0xcaf7,0xcaf8,0xcaf9,
+ 0xcafa,0xcafb,0xcafc,0xcafd,0xcafe,0xcaff,0xcb00,0xcb01,0xcb02,0xcb03,
+ 0xcb04,0xcb05,0xcb06,0xcb07,0xcb09,0xcb0a,0x2500,0x2502,0x250c,0x2510,
+ 0x2518,0x2514,0x251c,0x252c,0x2524,0x2534,0x253c,0x2501,0x2503,0x250f,
+ 0x2513,0x251b,0x2517,0x2523,0x2533,0x252b,0x253b,0x254b,0x2520,0x252f,
+ 0x2528,0x2537,0x253f,0x251d,0x2530,0x2525,0x2538,0x2542,0x2512,0x2511,
+ 0x251a,0x2519,0x2516,0x2515,0x250e,0x250d,0x251e,0x251f,0x2521,0x2522,
+ 0x2526,0x2527,0x2529,0x252a,0x252d,0x252e,0x2531,0x2532,0x2535,0x2536,
+ 0x2539,0x253a,0x253d,0x253e,0x2540,0x2541,0x2543,0x2544,0x2545,0x2546,
+ 0x2547,0x2548,0x2549,0x254a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ 0xcb0b,0xcb0c,0xcb0d,0xcb0e,0xcb0f,0xcb11,0xcb12,0xcb13,0xcb15,0xcb16,
+ 0xcb17,0xcb19,0xcb1a,0xcb1b,0xcb1c,0xcb1d,0xcb1e,0xcb1f,0xcb22,0xcb23,
+ 0xcb24,0xcb25,0xcb26,0xcb27,0xcb28,0xcb29,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcb2a,0xcb2b,0xcb2c,0xcb2d,0xcb2e,0xcb2f,0xcb30,0xcb31,
+ 0xcb32,0xcb33,0xcb34,0xcb35,0xcb36,0xcb37,0xcb38,0xcb39,0xcb3a,0xcb3b,
+ 0xcb3c,0xcb3d,0xcb3e,0xcb3f,0xcb40,0xcb42,0xcb43,0xcb44,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcb45,0xcb46,0xcb47,0xcb4a,0xcb4b,0xcb4d,
+ 0xcb4e,0xcb4f,0xcb51,0xcb52,0xcb53,0xcb54,0xcb55,0xcb56,0xcb57,0xcb5a,
+ 0xcb5b,0xcb5c,0xcb5e,0xcb5f,0xcb60,0xcb61,0xcb62,0xcb63,0xcb65,0xcb66,
+ 0xcb67,0xcb68,0xcb69,0xcb6a,0xcb6b,0xcb6c,0x3395,0x3396,0x3397,0x2113,
+ 0x3398,0x33c4,0x33a3,0x33a4,0x33a5,0x33a6,0x3399,0x339a,0x339b,0x339c,
+ 0x339d,0x339e,0x339f,0x33a0,0x33a1,0x33a2,0x33ca,0x338d,0x338e,0x338f,
+ 0x33cf,0x3388,0x3389,0x33c8,0x33a7,0x33a8,0x33b0,0x33b1,0x33b2,0x33b3,
+ 0x33b4,0x33b5,0x33b6,0x33b7,0x33b8,0x33b9,0x3380,0x3381,0x3382,0x3383,
+ 0x3384,0x33ba,0x33bb,0x33bc,0x33bd,0x33be,0x33bf,0x3390,0x3391,0x3392,
+ 0x3393,0x3394,0x2126,0x33c0,0x33c1,0x338a,0x338b,0x338c,0x33d6,0x33c5,
+ 0x33ad,0x33ae,0x33af,0x33db,0x33a9,0x33aa,0x33ab,0x33ac,0x33dd,0x33d0,
+ 0x33d3,0x33c3,0x33c9,0x33dc,0x33c6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ 0xcb6d,0xcb6e,0xcb6f,0xcb70,0xcb71,0xcb72,0xcb73,0xcb74,0xcb75,0xcb76,
+ 0xcb77,0xcb7a,0xcb7b,0xcb7c,0xcb7d,0xcb7e,0xcb7f,0xcb80,0xcb81,0xcb82,
+ 0xcb83,0xcb84,0xcb85,0xcb86,0xcb87,0xcb88,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcb89,0xcb8a,0xcb8b,0xcb8c,0xcb8d,0xcb8e,0xcb8f,0xcb90,
+ 0xcb91,0xcb92,0xcb93,0xcb94,0xcb95,0xcb96,0xcb97,0xcb98,0xcb99,0xcb9a,
+ 0xcb9b,0xcb9d,0xcb9e,0xcb9f,0xcba0,0xcba1,0xcba2,0xcba3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcba4,0xcba5,0xcba6,0xcba7,0xcba8,0xcba9,
+ 0xcbaa,0xcbab,0xcbac,0xcbad,0xcbae,0xcbaf,0xcbb0,0xcbb1,0xcbb2,0xcbb3,
+ 0xcbb4,0xcbb5,0xcbb6,0xcbb7,0xcbb9,0xcbba,0xcbbb,0xcbbc,0xcbbd,0xcbbe,
+ 0xcbbf,0xcbc0,0xcbc1,0xcbc2,0xcbc3,0xcbc4,0x00c6,0x00d0,0x00aa,0x0126,
+ UBOGON,0x0132,UBOGON,0x013f,0x0141,0x00d8,0x0152,0x00ba,0x00de,0x0166,
+ 0x014a,UBOGON,0x3260,0x3261,0x3262,0x3263,0x3264,0x3265,0x3266,0x3267,
+ 0x3268,0x3269,0x326a,0x326b,0x326c,0x326d,0x326e,0x326f,0x3270,0x3271,
+ 0x3272,0x3273,0x3274,0x3275,0x3276,0x3277,0x3278,0x3279,0x327a,0x327b,
+ 0x24d0,0x24d1,0x24d2,0x24d3,0x24d4,0x24d5,0x24d6,0x24d7,0x24d8,0x24d9,
+ 0x24da,0x24db,0x24dc,0x24dd,0x24de,0x24df,0x24e0,0x24e1,0x24e2,0x24e3,
+ 0x24e4,0x24e5,0x24e6,0x24e7,0x24e8,0x24e9,0x2460,0x2461,0x2462,0x2463,
+ 0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,0x246a,0x246b,0x246c,0x246d,
+ 0x246e,0x00bd,0x2153,0x2154,0x00bc,0x00be,0x215b,0x215c,0x215d,0x215e
+ },
+ { /* ku 29 */
+ 0xcbc5,0xcbc6,0xcbc7,0xcbc8,0xcbc9,0xcbca,0xcbcb,0xcbcc,0xcbcd,0xcbce,
+ 0xcbcf,0xcbd0,0xcbd1,0xcbd2,0xcbd3,0xcbd5,0xcbd6,0xcbd7,0xcbd8,0xcbd9,
+ 0xcbda,0xcbdb,0xcbdc,0xcbdd,0xcbde,0xcbdf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcbe0,0xcbe1,0xcbe2,0xcbe3,0xcbe5,0xcbe6,0xcbe8,0xcbea,
+ 0xcbeb,0xcbec,0xcbed,0xcbee,0xcbef,0xcbf0,0xcbf1,0xcbf2,0xcbf3,0xcbf4,
+ 0xcbf5,0xcbf6,0xcbf7,0xcbf8,0xcbf9,0xcbfa,0xcbfb,0xcbfc,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcbfd,0xcbfe,0xcbff,0xcc00,0xcc01,0xcc02,
+ 0xcc03,0xcc04,0xcc05,0xcc06,0xcc07,0xcc08,0xcc09,0xcc0a,0xcc0b,0xcc0e,
+ 0xcc0f,0xcc11,0xcc12,0xcc13,0xcc15,0xcc16,0xcc17,0xcc18,0xcc19,0xcc1a,
+ 0xcc1b,0xcc1e,0xcc1f,0xcc20,0xcc23,0xcc24,0x00e6,0x0111,0x00f0,0x0127,
+ 0x0131,0x0133,0x0138,0x0140,0x0142,0x00f8,0x0153,0x00df,0x00fe,0x0167,
+ 0x014b,0x0149,0x3200,0x3201,0x3202,0x3203,0x3204,0x3205,0x3206,0x3207,
+ 0x3208,0x3209,0x320a,0x320b,0x320c,0x320d,0x320e,0x320f,0x3210,0x3211,
+ 0x3212,0x3213,0x3214,0x3215,0x3216,0x3217,0x3218,0x3219,0x321a,0x321b,
+ 0x249c,0x249d,0x249e,0x249f,0x24a0,0x24a1,0x24a2,0x24a3,0x24a4,0x24a5,
+ 0x24a6,0x24a7,0x24a8,0x24a9,0x24aa,0x24ab,0x24ac,0x24ad,0x24ae,0x24af,
+ 0x24b0,0x24b1,0x24b2,0x24b3,0x24b4,0x24b5,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,
+ 0x2482,0x00b9,0x00b2,0x00b3,0x2074,0x207f,0x2081,0x2082,0x2083,0x2084
+ },
+ { /* ku 2a */
+ 0xcc25,0xcc26,0xcc2a,0xcc2b,0xcc2d,0xcc2f,0xcc31,0xcc32,0xcc33,0xcc34,
+ 0xcc35,0xcc36,0xcc37,0xcc3a,0xcc3f,0xcc40,0xcc41,0xcc42,0xcc43,0xcc46,
+ 0xcc47,0xcc49,0xcc4a,0xcc4b,0xcc4d,0xcc4e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcc4f,0xcc50,0xcc51,0xcc52,0xcc53,0xcc56,0xcc5a,0xcc5b,
+ 0xcc5c,0xcc5d,0xcc5e,0xcc5f,0xcc61,0xcc62,0xcc63,0xcc65,0xcc67,0xcc69,
+ 0xcc6a,0xcc6b,0xcc6c,0xcc6d,0xcc6e,0xcc6f,0xcc71,0xcc72,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcc73,0xcc74,0xcc76,0xcc77,0xcc78,0xcc79,
+ 0xcc7a,0xcc7b,0xcc7c,0xcc7d,0xcc7e,0xcc7f,0xcc80,0xcc81,0xcc82,0xcc83,
+ 0xcc84,0xcc85,0xcc86,0xcc87,0xcc88,0xcc89,0xcc8a,0xcc8b,0xcc8c,0xcc8d,
+ 0xcc8e,0xcc8f,0xcc90,0xcc91,0xcc92,0xcc93,0x3041,0x3042,0x3043,0x3044,
+ 0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,0x304e,
+ 0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,
+ 0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,0x3062,
+ 0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,0x306c,
+ 0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,
+ 0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,0x3080,
+ 0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308a,
+ 0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ 0xcc94,0xcc95,0xcc96,0xcc97,0xcc9a,0xcc9b,0xcc9d,0xcc9e,0xcc9f,0xcca1,
+ 0xcca2,0xcca3,0xcca4,0xcca5,0xcca6,0xcca7,0xccaa,0xccae,0xccaf,0xccb0,
+ 0xccb1,0xccb2,0xccb3,0xccb6,0xccb7,0xccb9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xccba,0xccbb,0xccbd,0xccbe,0xccbf,0xccc0,0xccc1,0xccc2,
+ 0xccc3,0xccc6,0xccc8,0xccca,0xcccb,0xcccc,0xcccd,0xccce,0xcccf,0xccd1,
+ 0xccd2,0xccd3,0xccd5,0xccd6,0xccd7,0xccd8,0xccd9,0xccda,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xccdb,0xccdc,0xccdd,0xccde,0xccdf,0xcce0,
+ 0xcce1,0xcce2,0xcce3,0xcce5,0xcce6,0xcce7,0xcce8,0xcce9,0xccea,0xcceb,
+ 0xcced,0xccee,0xccef,0xccf1,0xccf2,0xccf3,0xccf4,0xccf5,0xccf6,0xccf7,
+ 0xccf8,0xccf9,0xccfa,0xccfb,0xccfc,0xccfd,0x30a1,0x30a2,0x30a3,0x30a4,
+ 0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,0x30ae,
+ 0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,0x30b8,
+ 0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,0x30c2,
+ 0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,0x30cc,
+ 0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,0x30d6,
+ 0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,0x30e0,
+ 0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,0x30e7,0x30e8,0x30e9,0x30ea,
+ 0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,0x30f1,0x30f2,0x30f3,0x30f4,
+ 0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ 0xccfe,0xccff,0xcd00,0xcd02,0xcd03,0xcd04,0xcd05,0xcd06,0xcd07,0xcd0a,
+ 0xcd0b,0xcd0d,0xcd0e,0xcd0f,0xcd11,0xcd12,0xcd13,0xcd14,0xcd15,0xcd16,
+ 0xcd17,0xcd1a,0xcd1c,0xcd1e,0xcd1f,0xcd20,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcd21,0xcd22,0xcd23,0xcd25,0xcd26,0xcd27,0xcd29,0xcd2a,
+ 0xcd2b,0xcd2d,0xcd2e,0xcd2f,0xcd30,0xcd31,0xcd32,0xcd33,0xcd34,0xcd35,
+ 0xcd36,0xcd37,0xcd38,0xcd3a,0xcd3b,0xcd3c,0xcd3d,0xcd3e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcd3f,0xcd40,0xcd41,0xcd42,0xcd43,0xcd44,
+ 0xcd45,0xcd46,0xcd47,0xcd48,0xcd49,0xcd4a,0xcd4b,0xcd4c,0xcd4d,0xcd4e,
+ 0xcd4f,0xcd50,0xcd51,0xcd52,0xcd53,0xcd54,0xcd55,0xcd56,0xcd57,0xcd58,
+ 0xcd59,0xcd5a,0xcd5b,0xcd5d,0xcd5e,0xcd5f,0x0410,0x0411,0x0412,0x0413,
+ 0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,
+ 0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,
+ 0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,
+ 0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,
+ 0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ 0xcd61,0xcd62,0xcd63,0xcd65,0xcd66,0xcd67,0xcd68,0xcd69,0xcd6a,0xcd6b,
+ 0xcd6e,0xcd70,0xcd72,0xcd73,0xcd74,0xcd75,0xcd76,0xcd77,0xcd79,0xcd7a,
+ 0xcd7b,0xcd7c,0xcd7d,0xcd7e,0xcd7f,0xcd80,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcd81,0xcd82,0xcd83,0xcd84,0xcd85,0xcd86,0xcd87,0xcd89,
+ 0xcd8a,0xcd8b,0xcd8c,0xcd8d,0xcd8e,0xcd8f,0xcd90,0xcd91,0xcd92,0xcd93,
+ 0xcd96,0xcd97,0xcd99,0xcd9a,0xcd9b,0xcd9d,0xcd9e,0xcd9f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcda0,0xcda1,0xcda2,0xcda3,0xcda6,0xcda8,
+ 0xcdaa,0xcdab,0xcdac,0xcdad,0xcdae,0xcdaf,0xcdb1,0xcdb2,0xcdb3,0xcdb4,
+ 0xcdb5,0xcdb6,0xcdb7,0xcdb8,0xcdb9,0xcdba,0xcdbb,0xcdbc,0xcdbd,0xcdbe,
+ 0xcdbf,0xcdc0,0xcdc1,0xcdc2,0xcdc3,0xcdc5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ 0xcdc6,0xcdc7,0xcdc8,0xcdc9,0xcdca,0xcdcb,0xcdcd,0xcdce,0xcdcf,0xcdd1,
+ 0xcdd2,0xcdd3,0xcdd4,0xcdd5,0xcdd6,0xcdd7,0xcdd8,0xcdd9,0xcdda,0xcddb,
+ 0xcddc,0xcddd,0xcdde,0xcddf,0xcde0,0xcde1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcde2,0xcde3,0xcde4,0xcde5,0xcde6,0xcde7,0xcde9,0xcdea,
+ 0xcdeb,0xcded,0xcdee,0xcdef,0xcdf1,0xcdf2,0xcdf3,0xcdf4,0xcdf5,0xcdf6,
+ 0xcdf7,0xcdfa,0xcdfc,0xcdfe,0xcdff,0xce00,0xce01,0xce02,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xce03,0xce05,0xce06,0xce07,0xce09,0xce0a,
+ 0xce0b,0xce0d,0xce0e,0xce0f,0xce10,0xce11,0xce12,0xce13,0xce15,0xce16,
+ 0xce17,0xce18,0xce1a,0xce1b,0xce1c,0xce1d,0xce1e,0xce1f,0xce22,0xce23,
+ 0xce25,0xce26,0xce27,0xce29,0xce2a,0xce2b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ 0xce2c,0xce2d,0xce2e,0xce2f,0xce32,0xce34,0xce36,0xce37,0xce38,0xce39,
+ 0xce3a,0xce3b,0xce3c,0xce3d,0xce3e,0xce3f,0xce40,0xce41,0xce42,0xce43,
+ 0xce44,0xce45,0xce46,0xce47,0xce48,0xce49,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xce4a,0xce4b,0xce4c,0xce4d,0xce4e,0xce4f,0xce50,0xce51,
+ 0xce52,0xce53,0xce54,0xce55,0xce56,0xce57,0xce5a,0xce5b,0xce5d,0xce5e,
+ 0xce62,0xce63,0xce64,0xce65,0xce66,0xce67,0xce6a,0xce6c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xce6e,0xce6f,0xce70,0xce71,0xce72,0xce73,
+ 0xce76,0xce77,0xce79,0xce7a,0xce7b,0xce7d,0xce7e,0xce7f,0xce80,0xce81,
+ 0xce82,0xce83,0xce86,0xce88,0xce8a,0xce8b,0xce8c,0xce8d,0xce8e,0xce8f,
+ 0xce92,0xce93,0xce95,0xce96,0xce97,0xce99,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0xce9a,0xce9b,0xce9c,0xce9d,0xce9e,0xce9f,0xcea2,0xcea6,0xcea7,0xcea8,
+ 0xcea9,0xceaa,0xceab,0xceae,0xceaf,0xceb0,0xceb1,0xceb2,0xceb3,0xceb4,
+ 0xceb5,0xceb6,0xceb7,0xceb8,0xceb9,0xceba,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcebb,0xcebc,0xcebd,0xcebe,0xcebf,0xcec0,0xcec2,0xcec3,
+ 0xcec4,0xcec5,0xcec6,0xcec7,0xcec8,0xcec9,0xceca,0xcecb,0xcecc,0xcecd,
+ 0xcece,0xcecf,0xced0,0xced1,0xced2,0xced3,0xced4,0xced5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xced6,0xced7,0xced8,0xced9,0xceda,0xcedb,
+ 0xcedc,0xcedd,0xcede,0xcedf,0xcee0,0xcee1,0xcee2,0xcee3,0xcee6,0xcee7,
+ 0xcee9,0xceea,0xceed,0xceee,0xceef,0xcef0,0xcef1,0xcef2,0xcef3,0xcef6,
+ 0xcefa,0xcefb,0xcefc,0xcefd,0xcefe,0xceff,0xac00,0xac01,0xac04,0xac07,
+ 0xac08,0xac09,0xac0a,0xac10,0xac11,0xac12,0xac13,0xac14,0xac15,0xac16,
+ 0xac17,0xac19,0xac1a,0xac1b,0xac1c,0xac1d,0xac20,0xac24,0xac2c,0xac2d,
+ 0xac2f,0xac30,0xac31,0xac38,0xac39,0xac3c,0xac40,0xac4b,0xac4d,0xac54,
+ 0xac58,0xac5c,0xac70,0xac71,0xac74,0xac77,0xac78,0xac7a,0xac80,0xac81,
+ 0xac83,0xac84,0xac85,0xac86,0xac89,0xac8a,0xac8b,0xac8c,0xac90,0xac94,
+ 0xac9c,0xac9d,0xac9f,0xaca0,0xaca1,0xaca8,0xaca9,0xacaa,0xacac,0xacaf,
+ 0xacb0,0xacb8,0xacb9,0xacbb,0xacbc,0xacbd,0xacc1,0xacc4,0xacc8,0xaccc,
+ 0xacd5,0xacd7,0xace0,0xace1,0xace4,0xace7,0xace8,0xacea,0xacec,0xacef,
+ 0xacf0,0xacf1,0xacf3,0xacf5,0xacf6,0xacfc,0xacfd,0xad00,0xad04,0xad06
+ },
+ { /* ku 31 */
+ 0xcf02,0xcf03,0xcf05,0xcf06,0xcf07,0xcf09,0xcf0a,0xcf0b,0xcf0c,0xcf0d,
+ 0xcf0e,0xcf0f,0xcf12,0xcf14,0xcf16,0xcf17,0xcf18,0xcf19,0xcf1a,0xcf1b,
+ 0xcf1d,0xcf1e,0xcf1f,0xcf21,0xcf22,0xcf23,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcf25,0xcf26,0xcf27,0xcf28,0xcf29,0xcf2a,0xcf2b,0xcf2e,
+ 0xcf32,0xcf33,0xcf34,0xcf35,0xcf36,0xcf37,0xcf39,0xcf3a,0xcf3b,0xcf3c,
+ 0xcf3d,0xcf3e,0xcf3f,0xcf40,0xcf41,0xcf42,0xcf43,0xcf44,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcf45,0xcf46,0xcf47,0xcf48,0xcf49,0xcf4a,
+ 0xcf4b,0xcf4c,0xcf4d,0xcf4e,0xcf4f,0xcf50,0xcf51,0xcf52,0xcf53,0xcf56,
+ 0xcf57,0xcf59,0xcf5a,0xcf5b,0xcf5d,0xcf5e,0xcf5f,0xcf60,0xcf61,0xcf62,
+ 0xcf63,0xcf66,0xcf68,0xcf6a,0xcf6b,0xcf6c,0xad0c,0xad0d,0xad0f,0xad11,
+ 0xad18,0xad1c,0xad20,0xad29,0xad2c,0xad2d,0xad34,0xad35,0xad38,0xad3c,
+ 0xad44,0xad45,0xad47,0xad49,0xad50,0xad54,0xad58,0xad61,0xad63,0xad6c,
+ 0xad6d,0xad70,0xad73,0xad74,0xad75,0xad76,0xad7b,0xad7c,0xad7d,0xad7f,
+ 0xad81,0xad82,0xad88,0xad89,0xad8c,0xad90,0xad9c,0xad9d,0xada4,0xadb7,
+ 0xadc0,0xadc1,0xadc4,0xadc8,0xadd0,0xadd1,0xadd3,0xaddc,0xade0,0xade4,
+ 0xadf8,0xadf9,0xadfc,0xadff,0xae00,0xae01,0xae08,0xae09,0xae0b,0xae0d,
+ 0xae14,0xae30,0xae31,0xae34,0xae37,0xae38,0xae3a,0xae40,0xae41,0xae43,
+ 0xae45,0xae46,0xae4a,0xae4c,0xae4d,0xae4e,0xae50,0xae54,0xae56,0xae5c,
+ 0xae5d,0xae5f,0xae60,0xae61,0xae65,0xae68,0xae69,0xae6c,0xae70,0xae78
+ },
+ { /* ku 32 */
+ 0xcf6d,0xcf6e,0xcf6f,0xcf72,0xcf73,0xcf75,0xcf76,0xcf77,0xcf79,0xcf7a,
+ 0xcf7b,0xcf7c,0xcf7d,0xcf7e,0xcf7f,0xcf81,0xcf82,0xcf83,0xcf84,0xcf86,
+ 0xcf87,0xcf88,0xcf89,0xcf8a,0xcf8b,0xcf8d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcf8e,0xcf8f,0xcf90,0xcf91,0xcf92,0xcf93,0xcf94,0xcf95,
+ 0xcf96,0xcf97,0xcf98,0xcf99,0xcf9a,0xcf9b,0xcf9c,0xcf9d,0xcf9e,0xcf9f,
+ 0xcfa0,0xcfa2,0xcfa3,0xcfa4,0xcfa5,0xcfa6,0xcfa7,0xcfa9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcfaa,0xcfab,0xcfac,0xcfad,0xcfae,0xcfaf,
+ 0xcfb1,0xcfb2,0xcfb3,0xcfb4,0xcfb5,0xcfb6,0xcfb7,0xcfb8,0xcfb9,0xcfba,
+ 0xcfbb,0xcfbc,0xcfbd,0xcfbe,0xcfbf,0xcfc0,0xcfc1,0xcfc2,0xcfc3,0xcfc5,
+ 0xcfc6,0xcfc7,0xcfc8,0xcfc9,0xcfca,0xcfcb,0xae79,0xae7b,0xae7c,0xae7d,
+ 0xae84,0xae85,0xae8c,0xaebc,0xaebd,0xaebe,0xaec0,0xaec4,0xaecc,0xaecd,
+ 0xaecf,0xaed0,0xaed1,0xaed8,0xaed9,0xaedc,0xaee8,0xaeeb,0xaeed,0xaef4,
+ 0xaef8,0xaefc,0xaf07,0xaf08,0xaf0d,0xaf10,0xaf2c,0xaf2d,0xaf30,0xaf32,
+ 0xaf34,0xaf3c,0xaf3d,0xaf3f,0xaf41,0xaf42,0xaf43,0xaf48,0xaf49,0xaf50,
+ 0xaf5c,0xaf5d,0xaf64,0xaf65,0xaf79,0xaf80,0xaf84,0xaf88,0xaf90,0xaf91,
+ 0xaf95,0xaf9c,0xafb8,0xafb9,0xafbc,0xafc0,0xafc7,0xafc8,0xafc9,0xafcb,
+ 0xafcd,0xafce,0xafd4,0xafdc,0xafe8,0xafe9,0xaff0,0xaff1,0xaff4,0xaff8,
+ 0xb000,0xb001,0xb004,0xb00c,0xb010,0xb014,0xb01c,0xb01d,0xb028,0xb044,
+ 0xb045,0xb048,0xb04a,0xb04c,0xb04e,0xb053,0xb054,0xb055,0xb057,0xb059
+ },
+ { /* ku 33 */
+ 0xcfcc,0xcfcd,0xcfce,0xcfcf,0xcfd0,0xcfd1,0xcfd2,0xcfd3,0xcfd4,0xcfd5,
+ 0xcfd6,0xcfd7,0xcfd8,0xcfd9,0xcfda,0xcfdb,0xcfdc,0xcfdd,0xcfde,0xcfdf,
+ 0xcfe2,0xcfe3,0xcfe5,0xcfe6,0xcfe7,0xcfe9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcfea,0xcfeb,0xcfec,0xcfed,0xcfee,0xcfef,0xcff2,0xcff4,
+ 0xcff6,0xcff7,0xcff8,0xcff9,0xcffa,0xcffb,0xcffd,0xcffe,0xcfff,0xd001,
+ 0xd002,0xd003,0xd005,0xd006,0xd007,0xd008,0xd009,0xd00a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd00b,0xd00c,0xd00d,0xd00e,0xd00f,0xd010,
+ 0xd012,0xd013,0xd014,0xd015,0xd016,0xd017,0xd019,0xd01a,0xd01b,0xd01c,
+ 0xd01d,0xd01e,0xd01f,0xd020,0xd021,0xd022,0xd023,0xd024,0xd025,0xd026,
+ 0xd027,0xd028,0xd029,0xd02a,0xd02b,0xd02c,0xb05d,0xb07c,0xb07d,0xb080,
+ 0xb084,0xb08c,0xb08d,0xb08f,0xb091,0xb098,0xb099,0xb09a,0xb09c,0xb09f,
+ 0xb0a0,0xb0a1,0xb0a2,0xb0a8,0xb0a9,0xb0ab,0xb0ac,0xb0ad,0xb0ae,0xb0af,
+ 0xb0b1,0xb0b3,0xb0b4,0xb0b5,0xb0b8,0xb0bc,0xb0c4,0xb0c5,0xb0c7,0xb0c8,
+ 0xb0c9,0xb0d0,0xb0d1,0xb0d4,0xb0d8,0xb0e0,0xb0e5,0xb108,0xb109,0xb10b,
+ 0xb10c,0xb110,0xb112,0xb113,0xb118,0xb119,0xb11b,0xb11c,0xb11d,0xb123,
+ 0xb124,0xb125,0xb128,0xb12c,0xb134,0xb135,0xb137,0xb138,0xb139,0xb140,
+ 0xb141,0xb144,0xb148,0xb150,0xb151,0xb154,0xb155,0xb158,0xb15c,0xb160,
+ 0xb178,0xb179,0xb17c,0xb180,0xb182,0xb188,0xb189,0xb18b,0xb18d,0xb192,
+ 0xb193,0xb194,0xb198,0xb19c,0xb1a8,0xb1cc,0xb1d0,0xb1d4,0xb1dc,0xb1dd
+ },
+ { /* ku 34 */
+ 0xd02e,0xd02f,0xd030,0xd031,0xd032,0xd033,0xd036,0xd037,0xd039,0xd03a,
+ 0xd03b,0xd03d,0xd03e,0xd03f,0xd040,0xd041,0xd042,0xd043,0xd046,0xd048,
+ 0xd04a,0xd04b,0xd04c,0xd04d,0xd04e,0xd04f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd051,0xd052,0xd053,0xd055,0xd056,0xd057,0xd059,0xd05a,
+ 0xd05b,0xd05c,0xd05d,0xd05e,0xd05f,0xd061,0xd062,0xd063,0xd064,0xd065,
+ 0xd066,0xd067,0xd068,0xd069,0xd06a,0xd06b,0xd06e,0xd06f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd071,0xd072,0xd073,0xd075,0xd076,0xd077,
+ 0xd078,0xd079,0xd07a,0xd07b,0xd07e,0xd07f,0xd080,0xd082,0xd083,0xd084,
+ 0xd085,0xd086,0xd087,0xd088,0xd089,0xd08a,0xd08b,0xd08c,0xd08d,0xd08e,
+ 0xd08f,0xd090,0xd091,0xd092,0xd093,0xd094,0xb1df,0xb1e8,0xb1e9,0xb1ec,
+ 0xb1f0,0xb1f9,0xb1fb,0xb1fd,0xb204,0xb205,0xb208,0xb20b,0xb20c,0xb214,
+ 0xb215,0xb217,0xb219,0xb220,0xb234,0xb23c,0xb258,0xb25c,0xb260,0xb268,
+ 0xb269,0xb274,0xb275,0xb27c,0xb284,0xb285,0xb289,0xb290,0xb291,0xb294,
+ 0xb298,0xb299,0xb29a,0xb2a0,0xb2a1,0xb2a3,0xb2a5,0xb2a6,0xb2aa,0xb2ac,
+ 0xb2b0,0xb2b4,0xb2c8,0xb2c9,0xb2cc,0xb2d0,0xb2d2,0xb2d8,0xb2d9,0xb2db,
+ 0xb2dd,0xb2e2,0xb2e4,0xb2e5,0xb2e6,0xb2e8,0xb2eb,0xb2ec,0xb2ed,0xb2ee,
+ 0xb2ef,0xb2f3,0xb2f4,0xb2f5,0xb2f7,0xb2f8,0xb2f9,0xb2fa,0xb2fb,0xb2ff,
+ 0xb300,0xb301,0xb304,0xb308,0xb310,0xb311,0xb313,0xb314,0xb315,0xb31c,
+ 0xb354,0xb355,0xb356,0xb358,0xb35b,0xb35c,0xb35e,0xb35f,0xb364,0xb365
+ },
+ { /* ku 35 */
+ 0xd095,0xd096,0xd097,0xd098,0xd099,0xd09a,0xd09b,0xd09c,0xd09d,0xd09e,
+ 0xd09f,0xd0a0,0xd0a1,0xd0a2,0xd0a3,0xd0a6,0xd0a7,0xd0a9,0xd0aa,0xd0ab,
+ 0xd0ad,0xd0ae,0xd0af,0xd0b0,0xd0b1,0xd0b2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd0b3,0xd0b6,0xd0b8,0xd0ba,0xd0bb,0xd0bc,0xd0bd,0xd0be,
+ 0xd0bf,0xd0c2,0xd0c3,0xd0c5,0xd0c6,0xd0c7,0xd0ca,0xd0cb,0xd0cc,0xd0cd,
+ 0xd0ce,0xd0cf,0xd0d2,0xd0d6,0xd0d7,0xd0d8,0xd0d9,0xd0da,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd0db,0xd0de,0xd0df,0xd0e1,0xd0e2,0xd0e3,
+ 0xd0e5,0xd0e6,0xd0e7,0xd0e8,0xd0e9,0xd0ea,0xd0eb,0xd0ee,0xd0f2,0xd0f3,
+ 0xd0f4,0xd0f5,0xd0f6,0xd0f7,0xd0f9,0xd0fa,0xd0fb,0xd0fc,0xd0fd,0xd0fe,
+ 0xd0ff,0xd100,0xd101,0xd102,0xd103,0xd104,0xb367,0xb369,0xb36b,0xb36e,
+ 0xb370,0xb371,0xb374,0xb378,0xb380,0xb381,0xb383,0xb384,0xb385,0xb38c,
+ 0xb390,0xb394,0xb3a0,0xb3a1,0xb3a8,0xb3ac,0xb3c4,0xb3c5,0xb3c8,0xb3cb,
+ 0xb3cc,0xb3ce,0xb3d0,0xb3d4,0xb3d5,0xb3d7,0xb3d9,0xb3db,0xb3dd,0xb3e0,
+ 0xb3e4,0xb3e8,0xb3fc,0xb410,0xb418,0xb41c,0xb420,0xb428,0xb429,0xb42b,
+ 0xb434,0xb450,0xb451,0xb454,0xb458,0xb460,0xb461,0xb463,0xb465,0xb46c,
+ 0xb480,0xb488,0xb49d,0xb4a4,0xb4a8,0xb4ac,0xb4b5,0xb4b7,0xb4b9,0xb4c0,
+ 0xb4c4,0xb4c8,0xb4d0,0xb4d5,0xb4dc,0xb4dd,0xb4e0,0xb4e3,0xb4e4,0xb4e6,
+ 0xb4ec,0xb4ed,0xb4ef,0xb4f1,0xb4f8,0xb514,0xb515,0xb518,0xb51b,0xb51c,
+ 0xb524,0xb525,0xb527,0xb528,0xb529,0xb52a,0xb530,0xb531,0xb534,0xb538
+ },
+ { /* ku 36 */
+ 0xd105,0xd106,0xd107,0xd108,0xd109,0xd10a,0xd10b,0xd10c,0xd10e,0xd10f,
+ 0xd110,0xd111,0xd112,0xd113,0xd114,0xd115,0xd116,0xd117,0xd118,0xd119,
+ 0xd11a,0xd11b,0xd11c,0xd11d,0xd11e,0xd11f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd120,0xd121,0xd122,0xd123,0xd124,0xd125,0xd126,0xd127,
+ 0xd128,0xd129,0xd12a,0xd12b,0xd12c,0xd12d,0xd12e,0xd12f,0xd132,0xd133,
+ 0xd135,0xd136,0xd137,0xd139,0xd13b,0xd13c,0xd13d,0xd13e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd13f,0xd142,0xd146,0xd147,0xd148,0xd149,
+ 0xd14a,0xd14b,0xd14e,0xd14f,0xd151,0xd152,0xd153,0xd155,0xd156,0xd157,
+ 0xd158,0xd159,0xd15a,0xd15b,0xd15e,0xd160,0xd162,0xd163,0xd164,0xd165,
+ 0xd166,0xd167,0xd169,0xd16a,0xd16b,0xd16d,0xb540,0xb541,0xb543,0xb544,
+ 0xb545,0xb54b,0xb54c,0xb54d,0xb550,0xb554,0xb55c,0xb55d,0xb55f,0xb560,
+ 0xb561,0xb5a0,0xb5a1,0xb5a4,0xb5a8,0xb5aa,0xb5ab,0xb5b0,0xb5b1,0xb5b3,
+ 0xb5b4,0xb5b5,0xb5bb,0xb5bc,0xb5bd,0xb5c0,0xb5c4,0xb5cc,0xb5cd,0xb5cf,
+ 0xb5d0,0xb5d1,0xb5d8,0xb5ec,0xb610,0xb611,0xb614,0xb618,0xb625,0xb62c,
+ 0xb634,0xb648,0xb664,0xb668,0xb69c,0xb69d,0xb6a0,0xb6a4,0xb6ab,0xb6ac,
+ 0xb6b1,0xb6d4,0xb6f0,0xb6f4,0xb6f8,0xb700,0xb701,0xb705,0xb728,0xb729,
+ 0xb72c,0xb72f,0xb730,0xb738,0xb739,0xb73b,0xb744,0xb748,0xb74c,0xb754,
+ 0xb755,0xb760,0xb764,0xb768,0xb770,0xb771,0xb773,0xb775,0xb77c,0xb77d,
+ 0xb780,0xb784,0xb78c,0xb78d,0xb78f,0xb790,0xb791,0xb792,0xb796,0xb797
+ },
+ { /* ku 37 */
+ 0xd16e,0xd16f,0xd170,0xd171,0xd172,0xd173,0xd174,0xd175,0xd176,0xd177,
+ 0xd178,0xd179,0xd17a,0xd17b,0xd17d,0xd17e,0xd17f,0xd180,0xd181,0xd182,
+ 0xd183,0xd185,0xd186,0xd187,0xd189,0xd18a,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd18b,0xd18c,0xd18d,0xd18e,0xd18f,0xd190,0xd191,0xd192,
+ 0xd193,0xd194,0xd195,0xd196,0xd197,0xd198,0xd199,0xd19a,0xd19b,0xd19c,
+ 0xd19d,0xd19e,0xd19f,0xd1a2,0xd1a3,0xd1a5,0xd1a6,0xd1a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd1a9,0xd1aa,0xd1ab,0xd1ac,0xd1ad,0xd1ae,
+ 0xd1af,0xd1b2,0xd1b4,0xd1b6,0xd1b7,0xd1b8,0xd1b9,0xd1bb,0xd1bd,0xd1be,
+ 0xd1bf,0xd1c1,0xd1c2,0xd1c3,0xd1c4,0xd1c5,0xd1c6,0xd1c7,0xd1c8,0xd1c9,
+ 0xd1ca,0xd1cb,0xd1cc,0xd1cd,0xd1ce,0xd1cf,0xb798,0xb799,0xb79c,0xb7a0,
+ 0xb7a8,0xb7a9,0xb7ab,0xb7ac,0xb7ad,0xb7b4,0xb7b5,0xb7b8,0xb7c7,0xb7c9,
+ 0xb7ec,0xb7ed,0xb7f0,0xb7f4,0xb7fc,0xb7fd,0xb7ff,0xb800,0xb801,0xb807,
+ 0xb808,0xb809,0xb80c,0xb810,0xb818,0xb819,0xb81b,0xb81d,0xb824,0xb825,
+ 0xb828,0xb82c,0xb834,0xb835,0xb837,0xb838,0xb839,0xb840,0xb844,0xb851,
+ 0xb853,0xb85c,0xb85d,0xb860,0xb864,0xb86c,0xb86d,0xb86f,0xb871,0xb878,
+ 0xb87c,0xb88d,0xb8a8,0xb8b0,0xb8b4,0xb8b8,0xb8c0,0xb8c1,0xb8c3,0xb8c5,
+ 0xb8cc,0xb8d0,0xb8d4,0xb8dd,0xb8df,0xb8e1,0xb8e8,0xb8e9,0xb8ec,0xb8f0,
+ 0xb8f8,0xb8f9,0xb8fb,0xb8fd,0xb904,0xb918,0xb920,0xb93c,0xb93d,0xb940,
+ 0xb944,0xb94c,0xb94f,0xb951,0xb958,0xb959,0xb95c,0xb960,0xb968,0xb969
+ },
+ { /* ku 38 */
+ 0xd1d0,0xd1d1,0xd1d2,0xd1d3,0xd1d4,0xd1d5,0xd1d6,0xd1d7,0xd1d9,0xd1da,
+ 0xd1db,0xd1dc,0xd1dd,0xd1de,0xd1df,0xd1e0,0xd1e1,0xd1e2,0xd1e3,0xd1e4,
+ 0xd1e5,0xd1e6,0xd1e7,0xd1e8,0xd1e9,0xd1ea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd1eb,0xd1ec,0xd1ed,0xd1ee,0xd1ef,0xd1f0,0xd1f1,0xd1f2,
+ 0xd1f3,0xd1f5,0xd1f6,0xd1f7,0xd1f9,0xd1fa,0xd1fb,0xd1fc,0xd1fd,0xd1fe,
+ 0xd1ff,0xd200,0xd201,0xd202,0xd203,0xd204,0xd205,0xd206,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd208,0xd20a,0xd20b,0xd20c,0xd20d,0xd20e,
+ 0xd20f,0xd211,0xd212,0xd213,0xd214,0xd215,0xd216,0xd217,0xd218,0xd219,
+ 0xd21a,0xd21b,0xd21c,0xd21d,0xd21e,0xd21f,0xd220,0xd221,0xd222,0xd223,
+ 0xd224,0xd225,0xd226,0xd227,0xd228,0xd229,0xb96b,0xb96d,0xb974,0xb975,
+ 0xb978,0xb97c,0xb984,0xb985,0xb987,0xb989,0xb98a,0xb98d,0xb98e,0xb9ac,
+ 0xb9ad,0xb9b0,0xb9b4,0xb9bc,0xb9bd,0xb9bf,0xb9c1,0xb9c8,0xb9c9,0xb9cc,
+ 0xb9ce,0xb9cf,0xb9d0,0xb9d1,0xb9d2,0xb9d8,0xb9d9,0xb9db,0xb9dd,0xb9de,
+ 0xb9e1,0xb9e3,0xb9e4,0xb9e5,0xb9e8,0xb9ec,0xb9f4,0xb9f5,0xb9f7,0xb9f8,
+ 0xb9f9,0xb9fa,0xba00,0xba01,0xba08,0xba15,0xba38,0xba39,0xba3c,0xba40,
+ 0xba42,0xba48,0xba49,0xba4b,0xba4d,0xba4e,0xba53,0xba54,0xba55,0xba58,
+ 0xba5c,0xba64,0xba65,0xba67,0xba68,0xba69,0xba70,0xba71,0xba74,0xba78,
+ 0xba83,0xba84,0xba85,0xba87,0xba8c,0xbaa8,0xbaa9,0xbaab,0xbaac,0xbab0,
+ 0xbab2,0xbab8,0xbab9,0xbabb,0xbabd,0xbac4,0xbac8,0xbad8,0xbad9,0xbafc
+ },
+ { /* ku 39 */
+ 0xd22a,0xd22b,0xd22e,0xd22f,0xd231,0xd232,0xd233,0xd235,0xd236,0xd237,
+ 0xd238,0xd239,0xd23a,0xd23b,0xd23e,0xd240,0xd242,0xd243,0xd244,0xd245,
+ 0xd246,0xd247,0xd249,0xd24a,0xd24b,0xd24c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd24d,0xd24e,0xd24f,0xd250,0xd251,0xd252,0xd253,0xd254,
+ 0xd255,0xd256,0xd257,0xd258,0xd259,0xd25a,0xd25b,0xd25d,0xd25e,0xd25f,
+ 0xd260,0xd261,0xd262,0xd263,0xd265,0xd266,0xd267,0xd268,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd269,0xd26a,0xd26b,0xd26c,0xd26d,0xd26e,
+ 0xd26f,0xd270,0xd271,0xd272,0xd273,0xd274,0xd275,0xd276,0xd277,0xd278,
+ 0xd279,0xd27a,0xd27b,0xd27c,0xd27d,0xd27e,0xd27f,0xd282,0xd283,0xd285,
+ 0xd286,0xd287,0xd289,0xd28a,0xd28b,0xd28c,0xbb00,0xbb04,0xbb0d,0xbb0f,
+ 0xbb11,0xbb18,0xbb1c,0xbb20,0xbb29,0xbb2b,0xbb34,0xbb35,0xbb36,0xbb38,
+ 0xbb3b,0xbb3c,0xbb3d,0xbb3e,0xbb44,0xbb45,0xbb47,0xbb49,0xbb4d,0xbb4f,
+ 0xbb50,0xbb54,0xbb58,0xbb61,0xbb63,0xbb6c,0xbb88,0xbb8c,0xbb90,0xbba4,
+ 0xbba8,0xbbac,0xbbb4,0xbbb7,0xbbc0,0xbbc4,0xbbc8,0xbbd0,0xbbd3,0xbbf8,
+ 0xbbf9,0xbbfc,0xbbff,0xbc00,0xbc02,0xbc08,0xbc09,0xbc0b,0xbc0c,0xbc0d,
+ 0xbc0f,0xbc11,0xbc14,0xbc15,0xbc16,0xbc17,0xbc18,0xbc1b,0xbc1c,0xbc1d,
+ 0xbc1e,0xbc1f,0xbc24,0xbc25,0xbc27,0xbc29,0xbc2d,0xbc30,0xbc31,0xbc34,
+ 0xbc38,0xbc40,0xbc41,0xbc43,0xbc44,0xbc45,0xbc49,0xbc4c,0xbc4d,0xbc50,
+ 0xbc5d,0xbc84,0xbc85,0xbc88,0xbc8b,0xbc8c,0xbc8e,0xbc94,0xbc95,0xbc97
+ },
+ { /* ku 3a */
+ 0xd28d,0xd28e,0xd28f,0xd292,0xd293,0xd294,0xd296,0xd297,0xd298,0xd299,
+ 0xd29a,0xd29b,0xd29d,0xd29e,0xd29f,0xd2a1,0xd2a2,0xd2a3,0xd2a5,0xd2a6,
+ 0xd2a7,0xd2a8,0xd2a9,0xd2aa,0xd2ab,0xd2ad,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd2ae,0xd2af,0xd2b0,0xd2b2,0xd2b3,0xd2b4,0xd2b5,0xd2b6,
+ 0xd2b7,0xd2ba,0xd2bb,0xd2bd,0xd2be,0xd2c1,0xd2c3,0xd2c4,0xd2c5,0xd2c6,
+ 0xd2c7,0xd2ca,0xd2cc,0xd2cd,0xd2ce,0xd2cf,0xd2d0,0xd2d1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd2d2,0xd2d3,0xd2d5,0xd2d6,0xd2d7,0xd2d9,
+ 0xd2da,0xd2db,0xd2dd,0xd2de,0xd2df,0xd2e0,0xd2e1,0xd2e2,0xd2e3,0xd2e6,
+ 0xd2e7,0xd2e8,0xd2e9,0xd2ea,0xd2eb,0xd2ec,0xd2ed,0xd2ee,0xd2ef,0xd2f2,
+ 0xd2f3,0xd2f5,0xd2f6,0xd2f7,0xd2f9,0xd2fa,0xbc99,0xbc9a,0xbca0,0xbca1,
+ 0xbca4,0xbca7,0xbca8,0xbcb0,0xbcb1,0xbcb3,0xbcb4,0xbcb5,0xbcbc,0xbcbd,
+ 0xbcc0,0xbcc4,0xbccd,0xbccf,0xbcd0,0xbcd1,0xbcd5,0xbcd8,0xbcdc,0xbcf4,
+ 0xbcf5,0xbcf6,0xbcf8,0xbcfc,0xbd04,0xbd05,0xbd07,0xbd09,0xbd10,0xbd14,
+ 0xbd24,0xbd2c,0xbd40,0xbd48,0xbd49,0xbd4c,0xbd50,0xbd58,0xbd59,0xbd64,
+ 0xbd68,0xbd80,0xbd81,0xbd84,0xbd87,0xbd88,0xbd89,0xbd8a,0xbd90,0xbd91,
+ 0xbd93,0xbd95,0xbd99,0xbd9a,0xbd9c,0xbda4,0xbdb0,0xbdb8,0xbdd4,0xbdd5,
+ 0xbdd8,0xbddc,0xbde9,0xbdf0,0xbdf4,0xbdf8,0xbe00,0xbe03,0xbe05,0xbe0c,
+ 0xbe0d,0xbe10,0xbe14,0xbe1c,0xbe1d,0xbe1f,0xbe44,0xbe45,0xbe48,0xbe4c,
+ 0xbe4e,0xbe54,0xbe55,0xbe57,0xbe59,0xbe5a,0xbe5b,0xbe60,0xbe61,0xbe64
+ },
+ { /* ku 3b */
+ 0xd2fb,0xd2fc,0xd2fd,0xd2fe,0xd2ff,0xd302,0xd304,0xd306,0xd307,0xd308,
+ 0xd309,0xd30a,0xd30b,0xd30f,0xd311,0xd312,0xd313,0xd315,0xd317,0xd318,
+ 0xd319,0xd31a,0xd31b,0xd31e,0xd322,0xd323,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd324,0xd326,0xd327,0xd32a,0xd32b,0xd32d,0xd32e,0xd32f,
+ 0xd331,0xd332,0xd333,0xd334,0xd335,0xd336,0xd337,0xd33a,0xd33e,0xd33f,
+ 0xd340,0xd341,0xd342,0xd343,0xd346,0xd347,0xd348,0xd349,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd34a,0xd34b,0xd34c,0xd34d,0xd34e,0xd34f,
+ 0xd350,0xd351,0xd352,0xd353,0xd354,0xd355,0xd356,0xd357,0xd358,0xd359,
+ 0xd35a,0xd35b,0xd35c,0xd35d,0xd35e,0xd35f,0xd360,0xd361,0xd362,0xd363,
+ 0xd364,0xd365,0xd366,0xd367,0xd368,0xd369,0xbe68,0xbe6a,0xbe70,0xbe71,
+ 0xbe73,0xbe74,0xbe75,0xbe7b,0xbe7c,0xbe7d,0xbe80,0xbe84,0xbe8c,0xbe8d,
+ 0xbe8f,0xbe90,0xbe91,0xbe98,0xbe99,0xbea8,0xbed0,0xbed1,0xbed4,0xbed7,
+ 0xbed8,0xbee0,0xbee3,0xbee4,0xbee5,0xbeec,0xbf01,0xbf08,0xbf09,0xbf18,
+ 0xbf19,0xbf1b,0xbf1c,0xbf1d,0xbf40,0xbf41,0xbf44,0xbf48,0xbf50,0xbf51,
+ 0xbf55,0xbf94,0xbfb0,0xbfc5,0xbfcc,0xbfcd,0xbfd0,0xbfd4,0xbfdc,0xbfdf,
+ 0xbfe1,0xc03c,0xc051,0xc058,0xc05c,0xc060,0xc068,0xc069,0xc090,0xc091,
+ 0xc094,0xc098,0xc0a0,0xc0a1,0xc0a3,0xc0a5,0xc0ac,0xc0ad,0xc0af,0xc0b0,
+ 0xc0b3,0xc0b4,0xc0b5,0xc0b6,0xc0bc,0xc0bd,0xc0bf,0xc0c0,0xc0c1,0xc0c5,
+ 0xc0c8,0xc0c9,0xc0cc,0xc0d0,0xc0d8,0xc0d9,0xc0db,0xc0dc,0xc0dd,0xc0e4
+ },
+ { /* ku 3c */
+ 0xd36a,0xd36b,0xd36c,0xd36d,0xd36e,0xd36f,0xd370,0xd371,0xd372,0xd373,
+ 0xd374,0xd375,0xd376,0xd377,0xd378,0xd379,0xd37a,0xd37b,0xd37e,0xd37f,
+ 0xd381,0xd382,0xd383,0xd385,0xd386,0xd387,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd388,0xd389,0xd38a,0xd38b,0xd38e,0xd392,0xd393,0xd394,
+ 0xd395,0xd396,0xd397,0xd39a,0xd39b,0xd39d,0xd39e,0xd39f,0xd3a1,0xd3a2,
+ 0xd3a3,0xd3a4,0xd3a5,0xd3a6,0xd3a7,0xd3aa,0xd3ac,0xd3ae,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd3af,0xd3b0,0xd3b1,0xd3b2,0xd3b3,0xd3b5,
+ 0xd3b6,0xd3b7,0xd3b9,0xd3ba,0xd3bb,0xd3bd,0xd3be,0xd3bf,0xd3c0,0xd3c1,
+ 0xd3c2,0xd3c3,0xd3c6,0xd3c7,0xd3ca,0xd3cb,0xd3cc,0xd3cd,0xd3ce,0xd3cf,
+ 0xd3d1,0xd3d2,0xd3d3,0xd3d4,0xd3d5,0xd3d6,0xc0e5,0xc0e8,0xc0ec,0xc0f4,
+ 0xc0f5,0xc0f7,0xc0f9,0xc100,0xc104,0xc108,0xc110,0xc115,0xc11c,0xc11d,
+ 0xc11e,0xc11f,0xc120,0xc123,0xc124,0xc126,0xc127,0xc12c,0xc12d,0xc12f,
+ 0xc130,0xc131,0xc136,0xc138,0xc139,0xc13c,0xc140,0xc148,0xc149,0xc14b,
+ 0xc14c,0xc14d,0xc154,0xc155,0xc158,0xc15c,0xc164,0xc165,0xc167,0xc168,
+ 0xc169,0xc170,0xc174,0xc178,0xc185,0xc18c,0xc18d,0xc18e,0xc190,0xc194,
+ 0xc196,0xc19c,0xc19d,0xc19f,0xc1a1,0xc1a5,0xc1a8,0xc1a9,0xc1ac,0xc1b0,
+ 0xc1bd,0xc1c4,0xc1c8,0xc1cc,0xc1d4,0xc1d7,0xc1d8,0xc1e0,0xc1e4,0xc1e8,
+ 0xc1f0,0xc1f1,0xc1f3,0xc1fc,0xc1fd,0xc200,0xc204,0xc20c,0xc20d,0xc20f,
+ 0xc211,0xc218,0xc219,0xc21c,0xc21f,0xc220,0xc228,0xc229,0xc22b,0xc22d
+ },
+ { /* ku 3d */
+ 0xd3d7,0xd3d9,0xd3da,0xd3db,0xd3dc,0xd3dd,0xd3de,0xd3df,0xd3e0,0xd3e2,
+ 0xd3e4,0xd3e5,0xd3e6,0xd3e7,0xd3e8,0xd3e9,0xd3ea,0xd3eb,0xd3ee,0xd3ef,
+ 0xd3f1,0xd3f2,0xd3f3,0xd3f5,0xd3f6,0xd3f7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd3f8,0xd3f9,0xd3fa,0xd3fb,0xd3fe,0xd400,0xd402,0xd403,
+ 0xd404,0xd405,0xd406,0xd407,0xd409,0xd40a,0xd40b,0xd40c,0xd40d,0xd40e,
+ 0xd40f,0xd410,0xd411,0xd412,0xd413,0xd414,0xd415,0xd416,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd417,0xd418,0xd419,0xd41a,0xd41b,0xd41c,
+ 0xd41e,0xd41f,0xd420,0xd421,0xd422,0xd423,0xd424,0xd425,0xd426,0xd427,
+ 0xd428,0xd429,0xd42a,0xd42b,0xd42c,0xd42d,0xd42e,0xd42f,0xd430,0xd431,
+ 0xd432,0xd433,0xd434,0xd435,0xd436,0xd437,0xc22f,0xc231,0xc232,0xc234,
+ 0xc248,0xc250,0xc251,0xc254,0xc258,0xc260,0xc265,0xc26c,0xc26d,0xc270,
+ 0xc274,0xc27c,0xc27d,0xc27f,0xc281,0xc288,0xc289,0xc290,0xc298,0xc29b,
+ 0xc29d,0xc2a4,0xc2a5,0xc2a8,0xc2ac,0xc2ad,0xc2b4,0xc2b5,0xc2b7,0xc2b9,
+ 0xc2dc,0xc2dd,0xc2e0,0xc2e3,0xc2e4,0xc2eb,0xc2ec,0xc2ed,0xc2ef,0xc2f1,
+ 0xc2f6,0xc2f8,0xc2f9,0xc2fb,0xc2fc,0xc300,0xc308,0xc309,0xc30c,0xc30d,
+ 0xc313,0xc314,0xc315,0xc318,0xc31c,0xc324,0xc325,0xc328,0xc329,0xc345,
+ 0xc368,0xc369,0xc36c,0xc370,0xc372,0xc378,0xc379,0xc37c,0xc37d,0xc384,
+ 0xc388,0xc38c,0xc3c0,0xc3d8,0xc3d9,0xc3dc,0xc3df,0xc3e0,0xc3e2,0xc3e8,
+ 0xc3e9,0xc3ed,0xc3f4,0xc3f5,0xc3f8,0xc408,0xc410,0xc424,0xc42c,0xc430
+ },
+ { /* ku 3e */
+ 0xd438,0xd439,0xd43a,0xd43b,0xd43c,0xd43d,0xd43e,0xd43f,0xd441,0xd442,
+ 0xd443,0xd445,0xd446,0xd447,0xd448,0xd449,0xd44a,0xd44b,0xd44c,0xd44d,
+ 0xd44e,0xd44f,0xd450,0xd451,0xd452,0xd453,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd454,0xd455,0xd456,0xd457,0xd458,0xd459,0xd45a,0xd45b,
+ 0xd45d,0xd45e,0xd45f,0xd461,0xd462,0xd463,0xd465,0xd466,0xd467,0xd468,
+ 0xd469,0xd46a,0xd46b,0xd46c,0xd46e,0xd470,0xd471,0xd472,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd473,0xd474,0xd475,0xd476,0xd477,0xd47a,
+ 0xd47b,0xd47d,0xd47e,0xd481,0xd483,0xd484,0xd485,0xd486,0xd487,0xd48a,
+ 0xd48c,0xd48e,0xd48f,0xd490,0xd491,0xd492,0xd493,0xd495,0xd496,0xd497,
+ 0xd498,0xd499,0xd49a,0xd49b,0xd49c,0xd49d,0xc434,0xc43c,0xc43d,0xc448,
+ 0xc464,0xc465,0xc468,0xc46c,0xc474,0xc475,0xc479,0xc480,0xc494,0xc49c,
+ 0xc4b8,0xc4bc,0xc4e9,0xc4f0,0xc4f1,0xc4f4,0xc4f8,0xc4fa,0xc4ff,0xc500,
+ 0xc501,0xc50c,0xc510,0xc514,0xc51c,0xc528,0xc529,0xc52c,0xc530,0xc538,
+ 0xc539,0xc53b,0xc53d,0xc544,0xc545,0xc548,0xc549,0xc54a,0xc54c,0xc54d,
+ 0xc54e,0xc553,0xc554,0xc555,0xc557,0xc558,0xc559,0xc55d,0xc55e,0xc560,
+ 0xc561,0xc564,0xc568,0xc570,0xc571,0xc573,0xc574,0xc575,0xc57c,0xc57d,
+ 0xc580,0xc584,0xc587,0xc58c,0xc58d,0xc58f,0xc591,0xc595,0xc597,0xc598,
+ 0xc59c,0xc5a0,0xc5a9,0xc5b4,0xc5b5,0xc5b8,0xc5b9,0xc5bb,0xc5bc,0xc5bd,
+ 0xc5be,0xc5c4,0xc5c5,0xc5c6,0xc5c7,0xc5c8,0xc5c9,0xc5ca,0xc5cc,0xc5ce
+ },
+ { /* ku 3f */
+ 0xd49e,0xd49f,0xd4a0,0xd4a1,0xd4a2,0xd4a3,0xd4a4,0xd4a5,0xd4a6,0xd4a7,
+ 0xd4a8,0xd4aa,0xd4ab,0xd4ac,0xd4ad,0xd4ae,0xd4af,0xd4b0,0xd4b1,0xd4b2,
+ 0xd4b3,0xd4b4,0xd4b5,0xd4b6,0xd4b7,0xd4b8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd4b9,0xd4ba,0xd4bb,0xd4bc,0xd4bd,0xd4be,0xd4bf,0xd4c0,
+ 0xd4c1,0xd4c2,0xd4c3,0xd4c4,0xd4c5,0xd4c6,0xd4c7,0xd4c8,0xd4c9,0xd4ca,
+ 0xd4cb,0xd4cd,0xd4ce,0xd4cf,0xd4d1,0xd4d2,0xd4d3,0xd4d5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd4d6,0xd4d7,0xd4d8,0xd4d9,0xd4da,0xd4db,
+ 0xd4dd,0xd4de,0xd4e0,0xd4e1,0xd4e2,0xd4e3,0xd4e4,0xd4e5,0xd4e6,0xd4e7,
+ 0xd4e9,0xd4ea,0xd4eb,0xd4ed,0xd4ee,0xd4ef,0xd4f1,0xd4f2,0xd4f3,0xd4f4,
+ 0xd4f5,0xd4f6,0xd4f7,0xd4f9,0xd4fa,0xd4fc,0xc5d0,0xc5d1,0xc5d4,0xc5d8,
+ 0xc5e0,0xc5e1,0xc5e3,0xc5e5,0xc5ec,0xc5ed,0xc5ee,0xc5f0,0xc5f4,0xc5f6,
+ 0xc5f7,0xc5fc,0xc5fd,0xc5fe,0xc5ff,0xc600,0xc601,0xc605,0xc606,0xc607,
+ 0xc608,0xc60c,0xc610,0xc618,0xc619,0xc61b,0xc61c,0xc624,0xc625,0xc628,
+ 0xc62c,0xc62d,0xc62e,0xc630,0xc633,0xc634,0xc635,0xc637,0xc639,0xc63b,
+ 0xc640,0xc641,0xc644,0xc648,0xc650,0xc651,0xc653,0xc654,0xc655,0xc65c,
+ 0xc65d,0xc660,0xc66c,0xc66f,0xc671,0xc678,0xc679,0xc67c,0xc680,0xc688,
+ 0xc689,0xc68b,0xc68d,0xc694,0xc695,0xc698,0xc69c,0xc6a4,0xc6a5,0xc6a7,
+ 0xc6a9,0xc6b0,0xc6b1,0xc6b4,0xc6b8,0xc6b9,0xc6ba,0xc6c0,0xc6c1,0xc6c3,
+ 0xc6c5,0xc6cc,0xc6cd,0xc6d0,0xc6d4,0xc6dc,0xc6dd,0xc6e0,0xc6e1,0xc6e8
+ },
+ { /* ku 40 */
+ 0xd4fe,0xd4ff,0xd500,0xd501,0xd502,0xd503,0xd505,0xd506,0xd507,0xd509,
+ 0xd50a,0xd50b,0xd50d,0xd50e,0xd50f,0xd510,0xd511,0xd512,0xd513,0xd516,
+ 0xd518,0xd519,0xd51a,0xd51b,0xd51c,0xd51d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd51e,0xd51f,0xd520,0xd521,0xd522,0xd523,0xd524,0xd525,
+ 0xd526,0xd527,0xd528,0xd529,0xd52a,0xd52b,0xd52c,0xd52d,0xd52e,0xd52f,
+ 0xd530,0xd531,0xd532,0xd533,0xd534,0xd535,0xd536,0xd537,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd538,0xd539,0xd53a,0xd53b,0xd53e,0xd53f,
+ 0xd541,0xd542,0xd543,0xd545,0xd546,0xd547,0xd548,0xd549,0xd54a,0xd54b,
+ 0xd54e,0xd550,0xd552,0xd553,0xd554,0xd555,0xd556,0xd557,0xd55a,0xd55b,
+ 0xd55d,0xd55e,0xd55f,0xd561,0xd562,0xd563,0xc6e9,0xc6ec,0xc6f0,0xc6f8,
+ 0xc6f9,0xc6fd,0xc704,0xc705,0xc708,0xc70c,0xc714,0xc715,0xc717,0xc719,
+ 0xc720,0xc721,0xc724,0xc728,0xc730,0xc731,0xc733,0xc735,0xc737,0xc73c,
+ 0xc73d,0xc740,0xc744,0xc74a,0xc74c,0xc74d,0xc74f,0xc751,0xc752,0xc753,
+ 0xc754,0xc755,0xc756,0xc757,0xc758,0xc75c,0xc760,0xc768,0xc76b,0xc774,
+ 0xc775,0xc778,0xc77c,0xc77d,0xc77e,0xc783,0xc784,0xc785,0xc787,0xc788,
+ 0xc789,0xc78a,0xc78e,0xc790,0xc791,0xc794,0xc796,0xc797,0xc798,0xc79a,
+ 0xc7a0,0xc7a1,0xc7a3,0xc7a4,0xc7a5,0xc7a6,0xc7ac,0xc7ad,0xc7b0,0xc7b4,
+ 0xc7bc,0xc7bd,0xc7bf,0xc7c0,0xc7c1,0xc7c8,0xc7c9,0xc7cc,0xc7ce,0xc7d0,
+ 0xc7d8,0xc7dd,0xc7e4,0xc7e8,0xc7ec,0xc800,0xc801,0xc804,0xc808,0xc80a
+ },
+ { /* ku 41 */
+ 0xd564,0xd566,0xd567,0xd56a,0xd56c,0xd56e,0xd56f,0xd570,0xd571,0xd572,
+ 0xd573,0xd576,0xd577,0xd579,0xd57a,0xd57b,0xd57d,0xd57e,0xd57f,0xd580,
+ 0xd581,0xd582,0xd583,0xd586,0xd58a,0xd58b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd58c,0xd58d,0xd58e,0xd58f,0xd591,0xd592,0xd593,0xd594,
+ 0xd595,0xd596,0xd597,0xd598,0xd599,0xd59a,0xd59b,0xd59c,0xd59d,0xd59e,
+ 0xd59f,0xd5a0,0xd5a1,0xd5a2,0xd5a3,0xd5a4,0xd5a6,0xd5a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd5a8,0xd5a9,0xd5aa,0xd5ab,0xd5ac,0xd5ad,
+ 0xd5ae,0xd5af,0xd5b0,0xd5b1,0xd5b2,0xd5b3,0xd5b4,0xd5b5,0xd5b6,0xd5b7,
+ 0xd5b8,0xd5b9,0xd5ba,0xd5bb,0xd5bc,0xd5bd,0xd5be,0xd5bf,0xd5c0,0xd5c1,
+ 0xd5c2,0xd5c3,0xd5c4,0xd5c5,0xd5c6,0xd5c7,0xc810,0xc811,0xc813,0xc815,
+ 0xc816,0xc81c,0xc81d,0xc820,0xc824,0xc82c,0xc82d,0xc82f,0xc831,0xc838,
+ 0xc83c,0xc840,0xc848,0xc849,0xc84c,0xc84d,0xc854,0xc870,0xc871,0xc874,
+ 0xc878,0xc87a,0xc880,0xc881,0xc883,0xc885,0xc886,0xc887,0xc88b,0xc88c,
+ 0xc88d,0xc894,0xc89d,0xc89f,0xc8a1,0xc8a8,0xc8bc,0xc8bd,0xc8c4,0xc8c8,
+ 0xc8cc,0xc8d4,0xc8d5,0xc8d7,0xc8d9,0xc8e0,0xc8e1,0xc8e4,0xc8f5,0xc8fc,
+ 0xc8fd,0xc900,0xc904,0xc905,0xc906,0xc90c,0xc90d,0xc90f,0xc911,0xc918,
+ 0xc92c,0xc934,0xc950,0xc951,0xc954,0xc958,0xc960,0xc961,0xc963,0xc96c,
+ 0xc970,0xc974,0xc97c,0xc988,0xc989,0xc98c,0xc990,0xc998,0xc999,0xc99b,
+ 0xc99d,0xc9c0,0xc9c1,0xc9c4,0xc9c7,0xc9c8,0xc9ca,0xc9d0,0xc9d1,0xc9d3
+ },
+ { /* ku 42 */
+ 0xd5ca,0xd5cb,0xd5cd,0xd5ce,0xd5cf,0xd5d1,0xd5d3,0xd5d4,0xd5d5,0xd5d6,
+ 0xd5d7,0xd5da,0xd5dc,0xd5de,0xd5df,0xd5e0,0xd5e1,0xd5e2,0xd5e3,0xd5e6,
+ 0xd5e7,0xd5e9,0xd5ea,0xd5eb,0xd5ed,0xd5ee,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd5ef,0xd5f0,0xd5f1,0xd5f2,0xd5f3,0xd5f6,0xd5f8,0xd5fa,
+ 0xd5fb,0xd5fc,0xd5fd,0xd5fe,0xd5ff,0xd602,0xd603,0xd605,0xd606,0xd607,
+ 0xd609,0xd60a,0xd60b,0xd60c,0xd60d,0xd60e,0xd60f,0xd612,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd616,0xd617,0xd618,0xd619,0xd61a,0xd61b,
+ 0xd61d,0xd61e,0xd61f,0xd621,0xd622,0xd623,0xd625,0xd626,0xd627,0xd628,
+ 0xd629,0xd62a,0xd62b,0xd62c,0xd62e,0xd62f,0xd630,0xd631,0xd632,0xd633,
+ 0xd634,0xd635,0xd636,0xd637,0xd63a,0xd63b,0xc9d5,0xc9d6,0xc9d9,0xc9da,
+ 0xc9dc,0xc9dd,0xc9e0,0xc9e2,0xc9e4,0xc9e7,0xc9ec,0xc9ed,0xc9ef,0xc9f0,
+ 0xc9f1,0xc9f8,0xc9f9,0xc9fc,0xca00,0xca08,0xca09,0xca0b,0xca0c,0xca0d,
+ 0xca14,0xca18,0xca29,0xca4c,0xca4d,0xca50,0xca54,0xca5c,0xca5d,0xca5f,
+ 0xca60,0xca61,0xca68,0xca7d,0xca84,0xca98,0xcabc,0xcabd,0xcac0,0xcac4,
+ 0xcacc,0xcacd,0xcacf,0xcad1,0xcad3,0xcad8,0xcad9,0xcae0,0xcaec,0xcaf4,
+ 0xcb08,0xcb10,0xcb14,0xcb18,0xcb20,0xcb21,0xcb41,0xcb48,0xcb49,0xcb4c,
+ 0xcb50,0xcb58,0xcb59,0xcb5d,0xcb64,0xcb78,0xcb79,0xcb9c,0xcbb8,0xcbd4,
+ 0xcbe4,0xcbe7,0xcbe9,0xcc0c,0xcc0d,0xcc10,0xcc14,0xcc1c,0xcc1d,0xcc21,
+ 0xcc22,0xcc27,0xcc28,0xcc29,0xcc2c,0xcc2e,0xcc30,0xcc38,0xcc39,0xcc3b
+ },
+ { /* ku 43 */
+ 0xd63d,0xd63e,0xd63f,0xd641,0xd642,0xd643,0xd644,0xd646,0xd647,0xd64a,
+ 0xd64c,0xd64e,0xd64f,0xd650,0xd652,0xd653,0xd656,0xd657,0xd659,0xd65a,
+ 0xd65b,0xd65d,0xd65e,0xd65f,0xd660,0xd661,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd662,0xd663,0xd664,0xd665,0xd666,0xd668,0xd66a,0xd66b,
+ 0xd66c,0xd66d,0xd66e,0xd66f,0xd672,0xd673,0xd675,0xd676,0xd677,0xd678,
+ 0xd679,0xd67a,0xd67b,0xd67c,0xd67d,0xd67e,0xd67f,0xd680,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd681,0xd682,0xd684,0xd686,0xd687,0xd688,
+ 0xd689,0xd68a,0xd68b,0xd68e,0xd68f,0xd691,0xd692,0xd693,0xd695,0xd696,
+ 0xd697,0xd698,0xd699,0xd69a,0xd69b,0xd69c,0xd69e,0xd6a0,0xd6a2,0xd6a3,
+ 0xd6a4,0xd6a5,0xd6a6,0xd6a7,0xd6a9,0xd6aa,0xcc3c,0xcc3d,0xcc3e,0xcc44,
+ 0xcc45,0xcc48,0xcc4c,0xcc54,0xcc55,0xcc57,0xcc58,0xcc59,0xcc60,0xcc64,
+ 0xcc66,0xcc68,0xcc70,0xcc75,0xcc98,0xcc99,0xcc9c,0xcca0,0xcca8,0xcca9,
+ 0xccab,0xccac,0xccad,0xccb4,0xccb5,0xccb8,0xccbc,0xccc4,0xccc5,0xccc7,
+ 0xccc9,0xccd0,0xccd4,0xcce4,0xccec,0xccf0,0xcd01,0xcd08,0xcd09,0xcd0c,
+ 0xcd10,0xcd18,0xcd19,0xcd1b,0xcd1d,0xcd24,0xcd28,0xcd2c,0xcd39,0xcd5c,
+ 0xcd60,0xcd64,0xcd6c,0xcd6d,0xcd6f,0xcd71,0xcd78,0xcd88,0xcd94,0xcd95,
+ 0xcd98,0xcd9c,0xcda4,0xcda5,0xcda7,0xcda9,0xcdb0,0xcdc4,0xcdcc,0xcdd0,
+ 0xcde8,0xcdec,0xcdf0,0xcdf8,0xcdf9,0xcdfb,0xcdfd,0xce04,0xce08,0xce0c,
+ 0xce14,0xce19,0xce20,0xce21,0xce24,0xce28,0xce30,0xce31,0xce33,0xce35
+ },
+ { /* ku 44 */
+ 0xd6ab,0xd6ad,0xd6ae,0xd6af,0xd6b1,0xd6b2,0xd6b3,0xd6b4,0xd6b5,0xd6b6,
+ 0xd6b7,0xd6b8,0xd6ba,0xd6bc,0xd6bd,0xd6be,0xd6bf,0xd6c0,0xd6c1,0xd6c2,
+ 0xd6c3,0xd6c6,0xd6c7,0xd6c9,0xd6ca,0xd6cb,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd6cd,0xd6ce,0xd6cf,0xd6d0,0xd6d2,0xd6d3,0xd6d5,0xd6d6,
+ 0xd6d8,0xd6da,0xd6db,0xd6dc,0xd6dd,0xd6de,0xd6df,0xd6e1,0xd6e2,0xd6e3,
+ 0xd6e5,0xd6e6,0xd6e7,0xd6e9,0xd6ea,0xd6eb,0xd6ec,0xd6ed,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd6ee,0xd6ef,0xd6f1,0xd6f2,0xd6f3,0xd6f4,
+ 0xd6f6,0xd6f7,0xd6f8,0xd6f9,0xd6fa,0xd6fb,0xd6fe,0xd6ff,0xd701,0xd702,
+ 0xd703,0xd705,0xd706,0xd707,0xd708,0xd709,0xd70a,0xd70b,0xd70c,0xd70d,
+ 0xd70e,0xd70f,0xd710,0xd712,0xd713,0xd714,0xce58,0xce59,0xce5c,0xce5f,
+ 0xce60,0xce61,0xce68,0xce69,0xce6b,0xce6d,0xce74,0xce75,0xce78,0xce7c,
+ 0xce84,0xce85,0xce87,0xce89,0xce90,0xce91,0xce94,0xce98,0xcea0,0xcea1,
+ 0xcea3,0xcea4,0xcea5,0xceac,0xcead,0xcec1,0xcee4,0xcee5,0xcee8,0xceeb,
+ 0xceec,0xcef4,0xcef5,0xcef7,0xcef8,0xcef9,0xcf00,0xcf01,0xcf04,0xcf08,
+ 0xcf10,0xcf11,0xcf13,0xcf15,0xcf1c,0xcf20,0xcf24,0xcf2c,0xcf2d,0xcf2f,
+ 0xcf30,0xcf31,0xcf38,0xcf54,0xcf55,0xcf58,0xcf5c,0xcf64,0xcf65,0xcf67,
+ 0xcf69,0xcf70,0xcf71,0xcf74,0xcf78,0xcf80,0xcf85,0xcf8c,0xcfa1,0xcfa8,
+ 0xcfb0,0xcfc4,0xcfe0,0xcfe1,0xcfe4,0xcfe8,0xcff0,0xcff1,0xcff3,0xcff5,
+ 0xcffc,0xd000,0xd004,0xd011,0xd018,0xd02d,0xd034,0xd035,0xd038,0xd03c
+ },
+ { /* ku 45 */
+ 0xd715,0xd716,0xd717,0xd71a,0xd71b,0xd71d,0xd71e,0xd71f,0xd721,0xd722,
+ 0xd723,0xd724,0xd725,0xd726,0xd727,0xd72a,0xd72c,0xd72e,0xd72f,0xd730,
+ 0xd731,0xd732,0xd733,0xd736,0xd737,0xd739,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd73a,0xd73b,0xd73d,0xd73e,0xd73f,0xd740,0xd741,0xd742,
+ 0xd743,0xd745,0xd746,0xd748,0xd74a,0xd74b,0xd74c,0xd74d,0xd74e,0xd74f,
+ 0xd752,0xd753,0xd755,0xd75a,0xd75b,0xd75c,0xd75d,0xd75e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd75f,0xd762,0xd764,0xd766,0xd767,0xd768,
+ 0xd76a,0xd76b,0xd76d,0xd76e,0xd76f,0xd771,0xd772,0xd773,0xd775,0xd776,
+ 0xd777,0xd778,0xd779,0xd77a,0xd77b,0xd77e,0xd77f,0xd780,0xd782,0xd783,
+ 0xd784,0xd785,0xd786,0xd787,0xd78a,0xd78b,0xd044,0xd045,0xd047,0xd049,
+ 0xd050,0xd054,0xd058,0xd060,0xd06c,0xd06d,0xd070,0xd074,0xd07c,0xd07d,
+ 0xd081,0xd0a4,0xd0a5,0xd0a8,0xd0ac,0xd0b4,0xd0b5,0xd0b7,0xd0b9,0xd0c0,
+ 0xd0c1,0xd0c4,0xd0c8,0xd0c9,0xd0d0,0xd0d1,0xd0d3,0xd0d4,0xd0d5,0xd0dc,
+ 0xd0dd,0xd0e0,0xd0e4,0xd0ec,0xd0ed,0xd0ef,0xd0f0,0xd0f1,0xd0f8,0xd10d,
+ 0xd130,0xd131,0xd134,0xd138,0xd13a,0xd140,0xd141,0xd143,0xd144,0xd145,
+ 0xd14c,0xd14d,0xd150,0xd154,0xd15c,0xd15d,0xd15f,0xd161,0xd168,0xd16c,
+ 0xd17c,0xd184,0xd188,0xd1a0,0xd1a1,0xd1a4,0xd1a8,0xd1b0,0xd1b1,0xd1b3,
+ 0xd1b5,0xd1ba,0xd1bc,0xd1c0,0xd1d8,0xd1f4,0xd1f8,0xd207,0xd209,0xd210,
+ 0xd22c,0xd22d,0xd230,0xd234,0xd23c,0xd23d,0xd23f,0xd241,0xd248,0xd25c
+ },
+ { /* ku 46 */
+ 0xd78d,0xd78e,0xd78f,0xd791,0xd792,0xd793,0xd794,0xd795,0xd796,0xd797,
+ 0xd79a,0xd79c,0xd79e,0xd79f,0xd7a0,0xd7a1,0xd7a2,0xd7a3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd264,0xd280,0xd281,0xd284,
+ 0xd288,0xd290,0xd291,0xd295,0xd29c,0xd2a0,0xd2a4,0xd2ac,0xd2b1,0xd2b8,
+ 0xd2b9,0xd2bc,0xd2bf,0xd2c0,0xd2c2,0xd2c8,0xd2c9,0xd2cb,0xd2d4,0xd2d8,
+ 0xd2dc,0xd2e4,0xd2e5,0xd2f0,0xd2f1,0xd2f4,0xd2f8,0xd300,0xd301,0xd303,
+ 0xd305,0xd30c,0xd30d,0xd30e,0xd310,0xd314,0xd316,0xd31c,0xd31d,0xd31f,
+ 0xd320,0xd321,0xd325,0xd328,0xd329,0xd32c,0xd330,0xd338,0xd339,0xd33b,
+ 0xd33c,0xd33d,0xd344,0xd345,0xd37c,0xd37d,0xd380,0xd384,0xd38c,0xd38d,
+ 0xd38f,0xd390,0xd391,0xd398,0xd399,0xd39c,0xd3a0,0xd3a8,0xd3a9,0xd3ab,
+ 0xd3ad,0xd3b4,0xd3b8,0xd3bc,0xd3c4,0xd3c5,0xd3c8,0xd3c9,0xd3d0,0xd3d8,
+ 0xd3e1,0xd3e3,0xd3ec,0xd3ed,0xd3f0,0xd3f4,0xd3fc,0xd3fd,0xd3ff,0xd401
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd408,0xd41d,0xd440,0xd444,
+ 0xd45c,0xd460,0xd464,0xd46d,0xd46f,0xd478,0xd479,0xd47c,0xd47f,0xd480,
+ 0xd482,0xd488,0xd489,0xd48b,0xd48d,0xd494,0xd4a9,0xd4cc,0xd4d0,0xd4d4,
+ 0xd4dc,0xd4df,0xd4e8,0xd4ec,0xd4f0,0xd4f8,0xd4fb,0xd4fd,0xd504,0xd508,
+ 0xd50c,0xd514,0xd515,0xd517,0xd53c,0xd53d,0xd540,0xd544,0xd54c,0xd54d,
+ 0xd54f,0xd551,0xd558,0xd559,0xd55c,0xd560,0xd565,0xd568,0xd569,0xd56b,
+ 0xd56d,0xd574,0xd575,0xd578,0xd57c,0xd584,0xd585,0xd587,0xd588,0xd589,
+ 0xd590,0xd5a5,0xd5c8,0xd5c9,0xd5cc,0xd5d0,0xd5d2,0xd5d8,0xd5d9,0xd5db,
+ 0xd5dd,0xd5e4,0xd5e5,0xd5e8,0xd5ec,0xd5f4,0xd5f5,0xd5f7,0xd5f9,0xd600,
+ 0xd601,0xd604,0xd608,0xd610,0xd611,0xd613,0xd614,0xd615,0xd61c,0xd620
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd624,0xd62d,0xd638,0xd639,
+ 0xd63c,0xd640,0xd645,0xd648,0xd649,0xd64b,0xd64d,0xd651,0xd654,0xd655,
+ 0xd658,0xd65c,0xd667,0xd669,0xd670,0xd671,0xd674,0xd683,0xd685,0xd68c,
+ 0xd68d,0xd690,0xd694,0xd69d,0xd69f,0xd6a1,0xd6a8,0xd6ac,0xd6b0,0xd6b9,
+ 0xd6bb,0xd6c4,0xd6c5,0xd6c8,0xd6cc,0xd6d1,0xd6d4,0xd6d7,0xd6d9,0xd6e0,
+ 0xd6e4,0xd6e8,0xd6f0,0xd6f5,0xd6fc,0xd6fd,0xd700,0xd704,0xd711,0xd718,
+ 0xd719,0xd71c,0xd720,0xd728,0xd729,0xd72b,0xd72d,0xd734,0xd735,0xd738,
+ 0xd73c,0xd744,0xd747,0xd749,0xd750,0xd751,0xd754,0xd756,0xd757,0xd758,
+ 0xd759,0xd760,0xd761,0xd763,0xd765,0xd769,0xd76c,0xd770,0xd774,0xd77c,
+ 0xd77d,0xd781,0xd788,0xd789,0xd78c,0xd790,0xd798,0xd799,0xd79b,0xd79d
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f3d,0x4f73,0x5047,0x50f9,
+ 0x52a0,0x53ef,0x5475,0x54e5,0x5609,0x5ac1,0x5bb6,0x6687,0x67b6,0x67b7,
+ 0x67ef,0x6b4c,0x73c2,0x75c2,0x7a3c,0x82db,0x8304,0x8857,0x8888,0x8a36,
+ 0x8cc8,0x8dcf,0x8efb,0x8fe6,0x99d5,0x523b,0x5374,0x5404,0x606a,0x6164,
+ 0x6bbc,0x73cf,0x811a,0x89ba,0x89d2,0x95a3,0x4f83,0x520a,0x58be,0x5978,
+ 0x59e6,0x5e72,0x5e79,0x61c7,0x63c0,0x6746,0x67ec,0x687f,0x6f97,0x764e,
+ 0x770b,0x78f5,0x7a08,0x7aff,0x7c21,0x809d,0x826e,0x8271,0x8aeb,0x9593,
+ 0x4e6b,0x559d,0x66f7,0x6e34,0x78a3,0x7aed,0x845b,0x8910,0x874e,0x97a8,
+ 0x52d8,0x574e,0x582a,0x5d4c,0x611f,0x61be,0x6221,0x6562,0x67d1,0x6a44,
+ 0x6e1b,0x7518,0x75b3,0x76e3,0x77b0,0x7d3a,0x90af,0x9451,0x9452,0x9f95
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5323,0x5cac,0x7532,0x80db,
+ 0x9240,0x9598,0x525b,0x5808,0x59dc,0x5ca1,0x5d17,0x5eb7,0x5f3a,0x5f4a,
+ 0x6177,0x6c5f,0x757a,0x7586,0x7ce0,0x7d73,0x7db1,0x7f8c,0x8154,0x8221,
+ 0x8591,0x8941,0x8b1b,0x92fc,0x964d,0x9c47,0x4ecb,0x4ef7,0x500b,0x51f1,
+ 0x584f,0x6137,0x613e,0x6168,0x6539,0x69ea,0x6f11,0x75a5,0x7686,0x76d6,
+ 0x7b87,0x82a5,0x84cb,0xf900,0x93a7,0x958b,0x5580,0x5ba2,0x5751,0xf901,
+ 0x7cb3,0x7fb9,0x91b5,0x5028,0x53bb,0x5c45,0x5de8,0x62d2,0x636e,0x64da,
+ 0x64e7,0x6e20,0x70ac,0x795b,0x8ddd,0x8e1e,0xf902,0x907d,0x9245,0x92f8,
+ 0x4e7e,0x4ef6,0x5065,0x5dfe,0x5efa,0x6106,0x6957,0x8171,0x8654,0x8e47,
+ 0x9375,0x9a2b,0x4e5e,0x5091,0x6770,0x6840,0x5109,0x528d,0x5292,0x6aa2
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x77bc,0x9210,0x9ed4,0x52ab,
+ 0x602f,0x8ff2,0x5048,0x61a9,0x63ed,0x64ca,0x683c,0x6a84,0x6fc0,0x8188,
+ 0x89a1,0x9694,0x5805,0x727d,0x72ac,0x7504,0x7d79,0x7e6d,0x80a9,0x898b,
+ 0x8b74,0x9063,0x9d51,0x6289,0x6c7a,0x6f54,0x7d50,0x7f3a,0x8a23,0x517c,
+ 0x614a,0x7b9d,0x8b19,0x9257,0x938c,0x4eac,0x4fd3,0x501e,0x50be,0x5106,
+ 0x52c1,0x52cd,0x537f,0x5770,0x5883,0x5e9a,0x5f91,0x6176,0x61ac,0x64ce,
+ 0x656c,0x666f,0x66bb,0x66f4,0x6897,0x6d87,0x7085,0x70f1,0x749f,0x74a5,
+ 0x74ca,0x75d9,0x786c,0x78ec,0x7adf,0x7af6,0x7d45,0x7d93,0x8015,0x803f,
+ 0x811b,0x8396,0x8b66,0x8f15,0x9015,0x93e1,0x9803,0x9838,0x9a5a,0x9be8,
+ 0x4fc2,0x5553,0x583a,0x5951,0x5b63,0x5c46,0x60b8,0x6212,0x6842,0x68b0
+ },
+ { /* ku 4d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x68e8,0x6eaa,0x754c,0x7678,
+ 0x78ce,0x7a3d,0x7cfb,0x7e6b,0x7e7c,0x8a08,0x8aa1,0x8c3f,0x968e,0x9dc4,
+ 0x53e4,0x53e9,0x544a,0x5471,0x56fa,0x59d1,0x5b64,0x5c3b,0x5eab,0x62f7,
+ 0x6537,0x6545,0x6572,0x66a0,0x67af,0x69c1,0x6cbd,0x75fc,0x7690,0x777e,
+ 0x7a3f,0x7f94,0x8003,0x80a1,0x818f,0x82e6,0x82fd,0x83f0,0x85c1,0x8831,
+ 0x88b4,0x8aa5,0xf903,0x8f9c,0x932e,0x96c7,0x9867,0x9ad8,0x9f13,0x54ed,
+ 0x659b,0x66f2,0x688f,0x7a40,0x8c37,0x9d60,0x56f0,0x5764,0x5d11,0x6606,
+ 0x68b1,0x68cd,0x6efe,0x7428,0x889e,0x9be4,0x6c68,0xf904,0x9aa8,0x4f9b,
+ 0x516c,0x5171,0x529f,0x5b54,0x5de5,0x6050,0x606d,0x62f1,0x63a7,0x653b,
+ 0x73d9,0x7a7a,0x86a3,0x8ca2,0x978f,0x4e32,0x5be1,0x6208,0x679c,0x74dc
+ },
+ { /* ku 4e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x79d1,0x83d3,0x8a87,0x8ab2,
+ 0x8de8,0x904e,0x934b,0x9846,0x5ed3,0x69e8,0x85ff,0x90ed,0xf905,0x51a0,
+ 0x5b98,0x5bec,0x6163,0x68fa,0x6b3e,0x704c,0x742f,0x74d8,0x7ba1,0x7f50,
+ 0x83c5,0x89c0,0x8cab,0x95dc,0x9928,0x522e,0x605d,0x62ec,0x9002,0x4f8a,
+ 0x5149,0x5321,0x58d9,0x5ee3,0x66e0,0x6d38,0x709a,0x72c2,0x73d6,0x7b50,
+ 0x80f1,0x945b,0x5366,0x639b,0x7f6b,0x4e56,0x5080,0x584a,0x58de,0x602a,
+ 0x6127,0x62d0,0x69d0,0x9b41,0x5b8f,0x7d18,0x80b1,0x8f5f,0x4ea4,0x50d1,
+ 0x54ac,0x55ac,0x5b0c,0x5da0,0x5de7,0x652a,0x654e,0x6821,0x6a4b,0x72e1,
+ 0x768e,0x77ef,0x7d5e,0x7ff9,0x81a0,0x854e,0x86df,0x8f03,0x8f4e,0x90ca,
+ 0x9903,0x9a55,0x9bab,0x4e18,0x4e45,0x4e5d,0x4ec7,0x4ff1,0x5177,0x52fe
+ },
+ { /* ku 4f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5340,0x53e3,0x53e5,0x548e,
+ 0x5614,0x5775,0x57a2,0x5bc7,0x5d87,0x5ed0,0x61fc,0x62d8,0x6551,0x67b8,
+ 0x67e9,0x69cb,0x6b50,0x6bc6,0x6bec,0x6c42,0x6e9d,0x7078,0x72d7,0x7396,
+ 0x7403,0x77bf,0x77e9,0x7a76,0x7d7f,0x8009,0x81fc,0x8205,0x820a,0x82df,
+ 0x8862,0x8b33,0x8cfc,0x8ec0,0x9011,0x90b1,0x9264,0x92b6,0x99d2,0x9a45,
+ 0x9ce9,0x9dd7,0x9f9c,0x570b,0x5c40,0x83ca,0x97a0,0x97ab,0x9eb4,0x541b,
+ 0x7a98,0x7fa4,0x88d9,0x8ecd,0x90e1,0x5800,0x5c48,0x6398,0x7a9f,0x5bae,
+ 0x5f13,0x7a79,0x7aae,0x828e,0x8eac,0x5026,0x5238,0x52f8,0x5377,0x5708,
+ 0x62f3,0x6372,0x6b0a,0x6dc3,0x7737,0x53a5,0x7357,0x8568,0x8e76,0x95d5,
+ 0x673a,0x6ac3,0x6f70,0x8a6d,0x8ecc,0x994b,0xf906,0x6677,0x6b78,0x8cb4
+ },
+ { /* ku 50 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b3c,0xf907,0x53eb,0x572d,
+ 0x594e,0x63c6,0x69fb,0x73ea,0x7845,0x7aba,0x7ac5,0x7cfe,0x8475,0x898f,
+ 0x8d73,0x9035,0x95a8,0x52fb,0x5747,0x7547,0x7b60,0x83cc,0x921e,0xf908,
+ 0x6a58,0x514b,0x524b,0x5287,0x621f,0x68d8,0x6975,0x9699,0x50c5,0x52a4,
+ 0x52e4,0x61c3,0x65a4,0x6839,0x69ff,0x747e,0x7b4b,0x82b9,0x83eb,0x89b2,
+ 0x8b39,0x8fd1,0x9949,0xf909,0x4eca,0x5997,0x64d2,0x6611,0x6a8e,0x7434,
+ 0x7981,0x79bd,0x82a9,0x887e,0x887f,0x895f,0xf90a,0x9326,0x4f0b,0x53ca,
+ 0x6025,0x6271,0x6c72,0x7d1a,0x7d66,0x4e98,0x5162,0x77dc,0x80af,0x4f01,
+ 0x4f0e,0x5176,0x5180,0x55dc,0x5668,0x573b,0x57fa,0x57fc,0x5914,0x5947,
+ 0x5993,0x5bc4,0x5c90,0x5d0e,0x5df1,0x5e7e,0x5fcc,0x6280,0x65d7,0x65e3
+ },
+ { /* ku 51 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x671e,0x671f,0x675e,0x68cb,
+ 0x68c4,0x6a5f,0x6b3a,0x6c23,0x6c7d,0x6c82,0x6dc7,0x7398,0x7426,0x742a,
+ 0x7482,0x74a3,0x7578,0x757f,0x7881,0x78ef,0x7941,0x7947,0x7948,0x797a,
+ 0x7b95,0x7d00,0x7dba,0x7f88,0x8006,0x802d,0x808c,0x8a18,0x8b4f,0x8c48,
+ 0x8d77,0x9321,0x9324,0x98e2,0x9951,0x9a0e,0x9a0f,0x9a65,0x9e92,0x7dca,
+ 0x4f76,0x5409,0x62ee,0x6854,0x91d1,0x55ab,0x513a,0xf90b,0xf90c,0x5a1c,
+ 0x61e6,0xf90d,0x62cf,0x62ff,0xf90e,0xf90f,0xf910,0xf911,0xf912,0xf913,
+ 0x90a3,0xf914,0xf915,0xf916,0xf917,0xf918,0x8afe,0xf919,0xf91a,0xf91b,
+ 0xf91c,0x6696,0xf91d,0x7156,0xf91e,0xf91f,0x96e3,0xf920,0x634f,0x637a,
+ 0x5357,0xf921,0x678f,0x6960,0x6e73,0xf922,0x7537,0xf923,0xf924,0xf925
+ },
+ { /* ku 52 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7d0d,0xf926,0xf927,0x8872,
+ 0x56ca,0x5a18,0xf928,0xf929,0xf92a,0xf92b,0xf92c,0x4e43,0xf92d,0x5167,
+ 0x5948,0x67f0,0x8010,0xf92e,0x5973,0x5e74,0x649a,0x79ca,0x5ff5,0x606c,
+ 0x62c8,0x637b,0x5be7,0x5bd7,0x52aa,0xf92f,0x5974,0x5f29,0x6012,0xf930,
+ 0xf931,0xf932,0x7459,0xf933,0xf934,0xf935,0xf936,0xf937,0xf938,0x99d1,
+ 0xf939,0xf93a,0xf93b,0xf93c,0xf93d,0xf93e,0xf93f,0xf940,0xf941,0xf942,
+ 0xf943,0x6fc3,0xf944,0xf945,0x81bf,0x8fb2,0x60f1,0xf946,0xf947,0x8166,
+ 0xf948,0xf949,0x5c3f,0xf94a,0xf94b,0xf94c,0xf94d,0xf94e,0xf94f,0xf950,
+ 0xf951,0x5ae9,0x8a25,0x677b,0x7d10,0xf952,0xf953,0xf954,0xf955,0xf956,
+ 0xf957,0x80fd,0xf958,0xf959,0x5c3c,0x6ce5,0x533f,0x6eba,0x591a,0x8336
+ },
+ { /* ku 53 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4e39,0x4eb6,0x4f46,0x55ae,
+ 0x5718,0x58c7,0x5f56,0x65b7,0x65e6,0x6a80,0x6bb5,0x6e4d,0x77ed,0x7aef,
+ 0x7c1e,0x7dde,0x86cb,0x8892,0x9132,0x935b,0x64bb,0x6fbe,0x737a,0x75b8,
+ 0x9054,0x5556,0x574d,0x61ba,0x64d4,0x66c7,0x6de1,0x6e5b,0x6f6d,0x6fb9,
+ 0x75f0,0x8043,0x81bd,0x8541,0x8983,0x8ac7,0x8b5a,0x931f,0x6c93,0x7553,
+ 0x7b54,0x8e0f,0x905d,0x5510,0x5802,0x5858,0x5e62,0x6207,0x649e,0x68e0,
+ 0x7576,0x7cd6,0x87b3,0x9ee8,0x4ee3,0x5788,0x576e,0x5927,0x5c0d,0x5cb1,
+ 0x5e36,0x5f85,0x6234,0x64e1,0x73b3,0x81fa,0x888b,0x8cb8,0x968a,0x9edb,
+ 0x5b85,0x5fb7,0x60b3,0x5012,0x5200,0x5230,0x5716,0x5835,0x5857,0x5c0e,
+ 0x5c60,0x5cf6,0x5d8b,0x5ea6,0x5f92,0x60bc,0x6311,0x6389,0x6417,0x6843
+ },
+ { /* ku 54 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x68f9,0x6ac2,0x6dd8,0x6e21,
+ 0x6ed4,0x6fe4,0x71fe,0x76dc,0x7779,0x79b1,0x7a3b,0x8404,0x89a9,0x8ced,
+ 0x8df3,0x8e48,0x9003,0x9014,0x9053,0x90fd,0x934d,0x9676,0x97dc,0x6bd2,
+ 0x7006,0x7258,0x72a2,0x7368,0x7763,0x79bf,0x7be4,0x7e9b,0x8b80,0x58a9,
+ 0x60c7,0x6566,0x65fd,0x66be,0x6c8c,0x711e,0x71c9,0x8c5a,0x9813,0x4e6d,
+ 0x7a81,0x4edd,0x51ac,0x51cd,0x52d5,0x540c,0x61a7,0x6771,0x6850,0x68df,
+ 0x6d1e,0x6f7c,0x75bc,0x77b3,0x7ae5,0x80f4,0x8463,0x9285,0x515c,0x6597,
+ 0x675c,0x6793,0x75d8,0x7ac7,0x8373,0xf95a,0x8c46,0x9017,0x982d,0x5c6f,
+ 0x81c0,0x829a,0x9041,0x906f,0x920d,0x5f97,0x5d9d,0x6a59,0x71c8,0x767b,
+ 0x7b49,0x85e4,0x8b04,0x9127,0x9a30,0x5587,0x61f6,0xf95b,0x7669,0x7f85
+ },
+ { /* ku 55 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x863f,0x87ba,0x88f8,0x908f,
+ 0xf95c,0x6d1b,0x70d9,0x73de,0x7d61,0x843d,0xf95d,0x916a,0x99f1,0xf95e,
+ 0x4e82,0x5375,0x6b04,0x6b12,0x703e,0x721b,0x862d,0x9e1e,0x524c,0x8fa3,
+ 0x5d50,0x64e5,0x652c,0x6b16,0x6feb,0x7c43,0x7e9c,0x85cd,0x8964,0x89bd,
+ 0x62c9,0x81d8,0x881f,0x5eca,0x6717,0x6d6a,0x72fc,0x7405,0x746f,0x8782,
+ 0x90de,0x4f86,0x5d0d,0x5fa0,0x840a,0x51b7,0x63a0,0x7565,0x4eae,0x5006,
+ 0x5169,0x51c9,0x6881,0x6a11,0x7cae,0x7cb1,0x7ce7,0x826f,0x8ad2,0x8f1b,
+ 0x91cf,0x4fb6,0x5137,0x52f5,0x5442,0x5eec,0x616e,0x623e,0x65c5,0x6ada,
+ 0x6ffe,0x792a,0x85dc,0x8823,0x95ad,0x9a62,0x9a6a,0x9e97,0x9ece,0x529b,
+ 0x66c6,0x6b77,0x701d,0x792b,0x8f62,0x9742,0x6190,0x6200,0x6523,0x6f23
+ },
+ { /* ku 56 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7149,0x7489,0x7df4,0x806f,
+ 0x84ee,0x8f26,0x9023,0x934a,0x51bd,0x5217,0x52a3,0x6d0c,0x70c8,0x88c2,
+ 0x5ec9,0x6582,0x6bae,0x6fc2,0x7c3e,0x7375,0x4ee4,0x4f36,0x56f9,0xf95f,
+ 0x5cba,0x5dba,0x601c,0x73b2,0x7b2d,0x7f9a,0x7fce,0x8046,0x901e,0x9234,
+ 0x96f6,0x9748,0x9818,0x9f61,0x4f8b,0x6fa7,0x79ae,0x91b4,0x96b7,0x52de,
+ 0xf960,0x6488,0x64c4,0x6ad3,0x6f5e,0x7018,0x7210,0x76e7,0x8001,0x8606,
+ 0x865c,0x8def,0x8f05,0x9732,0x9b6f,0x9dfa,0x9e75,0x788c,0x797f,0x7da0,
+ 0x83c9,0x9304,0x9e7f,0x9e93,0x8ad6,0x58df,0x5f04,0x6727,0x7027,0x74cf,
+ 0x7c60,0x807e,0x5121,0x7028,0x7262,0x78ca,0x8cc2,0x8cda,0x8cf4,0x96f7,
+ 0x4e86,0x50da,0x5bee,0x5ed6,0x6599,0x71ce,0x7642,0x77ad,0x804a,0x84fc
+ },
+ { /* ku 57 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x907c,0x9b27,0x9f8d,0x58d8,
+ 0x5a41,0x5c62,0x6a13,0x6dda,0x6f0f,0x763b,0x7d2f,0x7e37,0x851e,0x8938,
+ 0x93e4,0x964b,0x5289,0x65d2,0x67f3,0x69b4,0x6d41,0x6e9c,0x700f,0x7409,
+ 0x7460,0x7559,0x7624,0x786b,0x8b2c,0x985e,0x516d,0x622e,0x9678,0x4f96,
+ 0x502b,0x5d19,0x6dea,0x7db8,0x8f2a,0x5f8b,0x6144,0x6817,0xf961,0x9686,
+ 0x52d2,0x808b,0x51dc,0x51cc,0x695e,0x7a1c,0x7dbe,0x83f1,0x9675,0x4fda,
+ 0x5229,0x5398,0x540f,0x550e,0x5c65,0x60a7,0x674e,0x68a8,0x6d6c,0x7281,
+ 0x72f8,0x7406,0x7483,0xf962,0x75e2,0x7c6c,0x7f79,0x7fb8,0x8389,0x88cf,
+ 0x88e1,0x91cc,0x91d0,0x96e2,0x9bc9,0x541d,0x6f7e,0x71d0,0x7498,0x85fa,
+ 0x8eaa,0x96a3,0x9c57,0x9e9f,0x6797,0x6dcb,0x7433,0x81e8,0x9716,0x782c
+ },
+ { /* ku 58 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7acb,0x7b20,0x7c92,0x6469,
+ 0x746a,0x75f2,0x78bc,0x78e8,0x99ac,0x9b54,0x9ebb,0x5bde,0x5e55,0x6f20,
+ 0x819c,0x83ab,0x9088,0x4e07,0x534d,0x5a29,0x5dd2,0x5f4e,0x6162,0x633d,
+ 0x6669,0x66fc,0x6eff,0x6f2b,0x7063,0x779e,0x842c,0x8513,0x883b,0x8f13,
+ 0x9945,0x9c3b,0x551c,0x62b9,0x672b,0x6cab,0x8309,0x896a,0x977a,0x4ea1,
+ 0x5984,0x5fd8,0x5fd9,0x671b,0x7db2,0x7f54,0x8292,0x832b,0x83bd,0x8f1e,
+ 0x9099,0x57cb,0x59b9,0x5a92,0x5bd0,0x6627,0x679a,0x6885,0x6bcf,0x7164,
+ 0x7f75,0x8cb7,0x8ce3,0x9081,0x9b45,0x8108,0x8c8a,0x964c,0x9a40,0x9ea5,
+ 0x5b5f,0x6c13,0x731b,0x76f2,0x76df,0x840c,0x51aa,0x8993,0x514d,0x5195,
+ 0x52c9,0x68c9,0x6c94,0x7704,0x7720,0x7dbf,0x7dec,0x9762,0x9eb5,0x6ec5
+ },
+ { /* ku 59 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8511,0x51a5,0x540d,0x547d,
+ 0x660e,0x669d,0x6927,0x6e9f,0x76bf,0x7791,0x8317,0x84c2,0x879f,0x9169,
+ 0x9298,0x9cf4,0x8882,0x4fae,0x5192,0x52df,0x59c6,0x5e3d,0x6155,0x6478,
+ 0x6479,0x66ae,0x67d0,0x6a21,0x6bcd,0x6bdb,0x725f,0x7261,0x7441,0x7738,
+ 0x77db,0x8017,0x82bc,0x8305,0x8b00,0x8b28,0x8c8c,0x6728,0x6c90,0x7267,
+ 0x76ee,0x7766,0x7a46,0x9da9,0x6b7f,0x6c92,0x5922,0x6726,0x8499,0x536f,
+ 0x5893,0x5999,0x5edf,0x63cf,0x6634,0x6773,0x6e3a,0x732b,0x7ad7,0x82d7,
+ 0x9328,0x52d9,0x5deb,0x61ae,0x61cb,0x620a,0x62c7,0x64ab,0x65e0,0x6959,
+ 0x6b66,0x6bcb,0x7121,0x73f7,0x755d,0x7e46,0x821e,0x8302,0x856a,0x8aa3,
+ 0x8cbf,0x9727,0x9d61,0x58a8,0x9ed8,0x5011,0x520e,0x543b,0x554f,0x6587
+ },
+ { /* ku 5a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c76,0x7d0a,0x7d0b,0x805e,
+ 0x868a,0x9580,0x96ef,0x52ff,0x6c95,0x7269,0x5473,0x5a9a,0x5c3e,0x5d4b,
+ 0x5f4c,0x5fae,0x672a,0x68b6,0x6963,0x6e3c,0x6e44,0x7709,0x7c73,0x7f8e,
+ 0x8587,0x8b0e,0x8ff7,0x9761,0x9ef4,0x5cb7,0x60b6,0x610d,0x61ab,0x654f,
+ 0x65fb,0x65fc,0x6c11,0x6cef,0x739f,0x73c9,0x7de1,0x9594,0x5bc6,0x871c,
+ 0x8b10,0x525d,0x535a,0x62cd,0x640f,0x64b2,0x6734,0x6a38,0x6cca,0x73c0,
+ 0x749e,0x7b94,0x7c95,0x7e1b,0x818a,0x8236,0x8584,0x8feb,0x96f9,0x99c1,
+ 0x4f34,0x534a,0x53cd,0x53db,0x62cc,0x642c,0x6500,0x6591,0x69c3,0x6cee,
+ 0x6f58,0x73ed,0x7554,0x7622,0x76e4,0x76fc,0x78d0,0x78fb,0x792c,0x7d46,
+ 0x822c,0x87e0,0x8fd4,0x9812,0x98ef,0x52c3,0x62d4,0x64a5,0x6e24,0x6f51
+ },
+ { /* ku 5b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x767c,0x8dcb,0x91b1,0x9262,
+ 0x9aee,0x9b43,0x5023,0x508d,0x574a,0x59a8,0x5c28,0x5e47,0x5f77,0x623f,
+ 0x653e,0x65b9,0x65c1,0x6609,0x678b,0x699c,0x6ec2,0x78c5,0x7d21,0x80aa,
+ 0x8180,0x822b,0x82b3,0x84a1,0x868c,0x8a2a,0x8b17,0x90a6,0x9632,0x9f90,
+ 0x500d,0x4ff3,0xf963,0x57f9,0x5f98,0x62dc,0x6392,0x676f,0x6e43,0x7119,
+ 0x76c3,0x80cc,0x80da,0x88f4,0x88f5,0x8919,0x8ce0,0x8f29,0x914d,0x966a,
+ 0x4f2f,0x4f70,0x5e1b,0x67cf,0x6822,0x767d,0x767e,0x9b44,0x5e61,0x6a0a,
+ 0x7169,0x71d4,0x756a,0xf964,0x7e41,0x8543,0x85e9,0x98dc,0x4f10,0x7b4f,
+ 0x7f70,0x95a5,0x51e1,0x5e06,0x68b5,0x6c3e,0x6c4e,0x6cdb,0x72af,0x7bc4,
+ 0x8303,0x6cd5,0x743a,0x50fb,0x5288,0x58c1,0x64d8,0x6a97,0x74a7,0x7656
+ },
+ { /* ku 5c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x78a7,0x8617,0x95e2,0x9739,
+ 0xf965,0x535e,0x5f01,0x8b8a,0x8fa8,0x8faf,0x908a,0x5225,0x77a5,0x9c49,
+ 0x9f08,0x4e19,0x5002,0x5175,0x5c5b,0x5e77,0x661e,0x663a,0x67c4,0x68c5,
+ 0x70b3,0x7501,0x75c5,0x79c9,0x7add,0x8f27,0x9920,0x9a08,0x4fdd,0x5821,
+ 0x5831,0x5bf6,0x666e,0x6b65,0x6d11,0x6e7a,0x6f7d,0x73e4,0x752b,0x83e9,
+ 0x88dc,0x8913,0x8b5c,0x8f14,0x4f0f,0x50d5,0x5310,0x535c,0x5b93,0x5fa9,
+ 0x670d,0x798f,0x8179,0x832f,0x8514,0x8907,0x8986,0x8f39,0x8f3b,0x99a5,
+ 0x9c12,0x672c,0x4e76,0x4ff8,0x5949,0x5c01,0x5cef,0x5cf0,0x6367,0x68d2,
+ 0x70fd,0x71a2,0x742b,0x7e2b,0x84ec,0x8702,0x9022,0x92d2,0x9cf3,0x4e0d,
+ 0x4ed8,0x4fef,0x5085,0x5256,0x526f,0x5426,0x5490,0x57e0,0x592b,0x5a66
+ },
+ { /* ku 5d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5b5a,0x5b75,0x5bcc,0x5e9c,
+ 0xf966,0x6276,0x6577,0x65a7,0x6d6e,0x6ea5,0x7236,0x7b26,0x7c3f,0x7f36,
+ 0x8150,0x8151,0x819a,0x8240,0x8299,0x83a9,0x8a03,0x8ca0,0x8ce6,0x8cfb,
+ 0x8d74,0x8dba,0x90e8,0x91dc,0x961c,0x9644,0x99d9,0x9ce7,0x5317,0x5206,
+ 0x5429,0x5674,0x58b3,0x5954,0x596e,0x5fff,0x61a4,0x626e,0x6610,0x6c7e,
+ 0x711a,0x76c6,0x7c89,0x7cde,0x7d1b,0x82ac,0x8cc1,0x96f0,0xf967,0x4f5b,
+ 0x5f17,0x5f7f,0x62c2,0x5d29,0x670b,0x68da,0x787c,0x7e43,0x9d6c,0x4e15,
+ 0x5099,0x5315,0x532a,0x5351,0x5983,0x5a62,0x5e87,0x60b2,0x618a,0x6249,
+ 0x6279,0x6590,0x6787,0x69a7,0x6bd4,0x6bd6,0x6bd7,0x6bd8,0x6cb8,0xf968,
+ 0x7435,0x75fa,0x7812,0x7891,0x79d5,0x79d8,0x7c83,0x7dcb,0x7fe1,0x80a5
+ },
+ { /* ku 5e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x813e,0x81c2,0x83f2,0x871a,
+ 0x88e8,0x8ab9,0x8b6c,0x8cbb,0x9119,0x975e,0x98db,0x9f3b,0x56ac,0x5b2a,
+ 0x5f6c,0x658c,0x6ab3,0x6baf,0x6d5c,0x6ff1,0x7015,0x725d,0x73ad,0x8ca7,
+ 0x8cd3,0x983b,0x6191,0x6c37,0x8058,0x9a01,0x4e4d,0x4e8b,0x4e9b,0x4ed5,
+ 0x4f3a,0x4f3c,0x4f7f,0x4fdf,0x50ff,0x53f2,0x53f8,0x5506,0x55e3,0x56db,
+ 0x58eb,0x5962,0x5a11,0x5beb,0x5bfa,0x5c04,0x5df3,0x5e2b,0x5f99,0x601d,
+ 0x6368,0x659c,0x65af,0x67f6,0x67fb,0x68ad,0x6b7b,0x6c99,0x6cd7,0x6e23,
+ 0x7009,0x7345,0x7802,0x793e,0x7940,0x7960,0x79c1,0x7be9,0x7d17,0x7d72,
+ 0x8086,0x820d,0x838e,0x84d1,0x86c7,0x88df,0x8a50,0x8a5e,0x8b1d,0x8cdc,
+ 0x8d66,0x8fad,0x90aa,0x98fc,0x99df,0x9e9d,0x524a,0xf969,0x6714,0xf96a
+ },
+ { /* ku 5f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5098,0x522a,0x5c71,0x6563,
+ 0x6c55,0x73ca,0x7523,0x759d,0x7b97,0x849c,0x9178,0x9730,0x4e77,0x6492,
+ 0x6bba,0x715e,0x85a9,0x4e09,0xf96b,0x6749,0x68ee,0x6e17,0x829f,0x8518,
+ 0x886b,0x63f7,0x6f81,0x9212,0x98af,0x4e0a,0x50b7,0x50cf,0x511f,0x5546,
+ 0x55aa,0x5617,0x5b40,0x5c19,0x5ce0,0x5e38,0x5e8a,0x5ea0,0x5ec2,0x60f3,
+ 0x6851,0x6a61,0x6e58,0x723d,0x7240,0x72c0,0x76f8,0x7965,0x7bb1,0x7fd4,
+ 0x88f3,0x89f4,0x8a73,0x8c61,0x8cde,0x971c,0x585e,0x74bd,0x8cfd,0x55c7,
+ 0xf96c,0x7a61,0x7d22,0x8272,0x7272,0x751f,0x7525,0xf96d,0x7b19,0x5885,
+ 0x58fb,0x5dbc,0x5e8f,0x5eb6,0x5f90,0x6055,0x6292,0x637f,0x654d,0x6691,
+ 0x66d9,0x66f8,0x6816,0x68f2,0x7280,0x745e,0x7b6e,0x7d6e,0x7dd6,0x7f72
+ },
+ { /* ku 60 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x80e5,0x8212,0x85af,0x897f,
+ 0x8a93,0x901d,0x92e4,0x9ecd,0x9f20,0x5915,0x596d,0x5e2d,0x60dc,0x6614,
+ 0x6673,0x6790,0x6c50,0x6dc5,0x6f5f,0x77f3,0x78a9,0x84c6,0x91cb,0x932b,
+ 0x4ed9,0x50ca,0x5148,0x5584,0x5b0b,0x5ba3,0x6247,0x657e,0x65cb,0x6e32,
+ 0x717d,0x7401,0x7444,0x7487,0x74bf,0x766c,0x79aa,0x7dda,0x7e55,0x7fa8,
+ 0x817a,0x81b3,0x8239,0x861a,0x87ec,0x8a75,0x8de3,0x9078,0x9291,0x9425,
+ 0x994d,0x9bae,0x5368,0x5c51,0x6954,0x6cc4,0x6d29,0x6e2b,0x820c,0x859b,
+ 0x893b,0x8a2d,0x8aaa,0x96ea,0x9f67,0x5261,0x66b9,0x6bb2,0x7e96,0x87fe,
+ 0x8d0d,0x9583,0x965d,0x651d,0x6d89,0x71ee,0xf96e,0x57ce,0x59d3,0x5bac,
+ 0x6027,0x60fa,0x6210,0x661f,0x665f,0x7329,0x73f9,0x76db,0x7701,0x7b6c
+ },
+ { /* ku 61 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8056,0x8072,0x8165,0x8aa0,
+ 0x9192,0x4e16,0x52e2,0x6b72,0x6d17,0x7a05,0x7b39,0x7d30,0xf96f,0x8cb0,
+ 0x53ec,0x562f,0x5851,0x5bb5,0x5c0f,0x5c11,0x5de2,0x6240,0x6383,0x6414,
+ 0x662d,0x68b3,0x6cbc,0x6d88,0x6eaf,0x701f,0x70a4,0x71d2,0x7526,0x758f,
+ 0x758e,0x7619,0x7b11,0x7be0,0x7c2b,0x7d20,0x7d39,0x852c,0x856d,0x8607,
+ 0x8a34,0x900d,0x9061,0x90b5,0x92b7,0x97f6,0x9a37,0x4fd7,0x5c6c,0x675f,
+ 0x6d91,0x7c9f,0x7e8c,0x8b16,0x8d16,0x901f,0x5b6b,0x5dfd,0x640d,0x84c0,
+ 0x905c,0x98e1,0x7387,0x5b8b,0x609a,0x677e,0x6dde,0x8a1f,0x8aa6,0x9001,
+ 0x980c,0x5237,0xf970,0x7051,0x788e,0x9396,0x8870,0x91d7,0x4fee,0x53d7,
+ 0x55fd,0x56da,0x5782,0x58fd,0x5ac2,0x5b88,0x5cab,0x5cc0,0x5e25,0x6101
+ },
+ { /* ku 62 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x620d,0x624b,0x6388,0x641c,
+ 0x6536,0x6578,0x6a39,0x6b8a,0x6c34,0x6d19,0x6f31,0x71e7,0x72e9,0x7378,
+ 0x7407,0x74b2,0x7626,0x7761,0x79c0,0x7a57,0x7aea,0x7cb9,0x7d8f,0x7dac,
+ 0x7e61,0x7f9e,0x8129,0x8331,0x8490,0x84da,0x85ea,0x8896,0x8ab0,0x8b90,
+ 0x8f38,0x9042,0x9083,0x916c,0x9296,0x92b9,0x968b,0x96a7,0x96a8,0x96d6,
+ 0x9700,0x9808,0x9996,0x9ad3,0x9b1a,0x53d4,0x587e,0x5919,0x5b70,0x5bbf,
+ 0x6dd1,0x6f5a,0x719f,0x7421,0x74b9,0x8085,0x83fd,0x5de1,0x5f87,0x5faa,
+ 0x6042,0x65ec,0x6812,0x696f,0x6a53,0x6b89,0x6d35,0x6df3,0x73e3,0x76fe,
+ 0x77ac,0x7b4d,0x7d14,0x8123,0x821c,0x8340,0x84f4,0x8563,0x8a62,0x8ac4,
+ 0x9187,0x931e,0x9806,0x99b4,0x620c,0x8853,0x8ff0,0x9265,0x5d07,0x5d27
+ },
+ { /* ku 63 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5d69,0x745f,0x819d,0x8768,
+ 0x6fd5,0x62fe,0x7fd2,0x8936,0x8972,0x4e1e,0x4e58,0x50e7,0x52dd,0x5347,
+ 0x627f,0x6607,0x7e69,0x8805,0x965e,0x4f8d,0x5319,0x5636,0x59cb,0x5aa4,
+ 0x5c38,0x5c4e,0x5c4d,0x5e02,0x5f11,0x6043,0x65bd,0x662f,0x6642,0x67be,
+ 0x67f4,0x731c,0x77e2,0x793a,0x7fc5,0x8494,0x84cd,0x8996,0x8a66,0x8a69,
+ 0x8ae1,0x8c55,0x8c7a,0x57f4,0x5bd4,0x5f0f,0x606f,0x62ed,0x690d,0x6b96,
+ 0x6e5c,0x7184,0x7bd2,0x8755,0x8b58,0x8efe,0x98df,0x98fe,0x4f38,0x4f81,
+ 0x4fe1,0x547b,0x5a20,0x5bb8,0x613c,0x65b0,0x6668,0x71fc,0x7533,0x795e,
+ 0x7d33,0x814e,0x81e3,0x8398,0x85aa,0x85ce,0x8703,0x8a0a,0x8eab,0x8f9b,
+ 0xf971,0x8fc5,0x5931,0x5ba4,0x5be6,0x6089,0x5be9,0x5c0b,0x5fc3,0x6c81
+ },
+ { /* ku 64 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf972,0x6df1,0x700b,0x751a,
+ 0x82af,0x8af6,0x4ec0,0x5341,0xf973,0x96d9,0x6c0f,0x4e9e,0x4fc4,0x5152,
+ 0x555e,0x5a25,0x5ce8,0x6211,0x7259,0x82bd,0x83aa,0x86fe,0x8859,0x8a1d,
+ 0x963f,0x96c5,0x9913,0x9d09,0x9d5d,0x580a,0x5cb3,0x5dbd,0x5e44,0x60e1,
+ 0x6115,0x63e1,0x6a02,0x6e25,0x9102,0x9354,0x984e,0x9c10,0x9f77,0x5b89,
+ 0x5cb8,0x6309,0x664f,0x6848,0x773c,0x96c1,0x978d,0x9854,0x9b9f,0x65a1,
+ 0x8b01,0x8ecb,0x95bc,0x5535,0x5ca9,0x5dd6,0x5eb5,0x6697,0x764c,0x83f4,
+ 0x95c7,0x58d3,0x62bc,0x72ce,0x9d28,0x4ef0,0x592e,0x600f,0x663b,0x6b83,
+ 0x79e7,0x9d26,0x5393,0x54c0,0x57c3,0x5d16,0x611b,0x66d6,0x6daf,0x788d,
+ 0x827e,0x9698,0x9744,0x5384,0x627c,0x6396,0x6db2,0x7e0a,0x814b,0x984d
+ },
+ { /* ku 65 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6afb,0x7f4c,0x9daf,0x9e1a,
+ 0x4e5f,0x503b,0x51b6,0x591c,0x60f9,0x63f6,0x6930,0x723a,0x8036,0xf974,
+ 0x91ce,0x5f31,0xf975,0xf976,0x7d04,0x82e5,0x846f,0x84bb,0x85e5,0x8e8d,
+ 0xf977,0x4f6f,0xf978,0xf979,0x58e4,0x5b43,0x6059,0x63da,0x6518,0x656d,
+ 0x6698,0xf97a,0x694a,0x6a23,0x6d0b,0x7001,0x716c,0x75d2,0x760d,0x79b3,
+ 0x7a70,0xf97b,0x7f8a,0xf97c,0x8944,0xf97d,0x8b93,0x91c0,0x967d,0xf97e,
+ 0x990a,0x5704,0x5fa1,0x65bc,0x6f01,0x7600,0x79a6,0x8a9e,0x99ad,0x9b5a,
+ 0x9f6c,0x5104,0x61b6,0x6291,0x6a8d,0x81c6,0x5043,0x5830,0x5f66,0x7109,
+ 0x8a00,0x8afa,0x5b7c,0x8616,0x4ffa,0x513c,0x56b4,0x5944,0x63a9,0x6df9,
+ 0x5daa,0x696d,0x5186,0x4e88,0x4f59,0xf97f,0xf980,0xf981,0x5982,0xf982
+ },
+ { /* ku 66 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf983,0x6b5f,0x6c5d,0xf984,
+ 0x74b5,0x7916,0xf985,0x8207,0x8245,0x8339,0x8f3f,0x8f5d,0xf986,0x9918,
+ 0xf987,0xf988,0xf989,0x4ea6,0xf98a,0x57df,0x5f79,0x6613,0xf98b,0xf98c,
+ 0x75ab,0x7e79,0x8b6f,0xf98d,0x9006,0x9a5b,0x56a5,0x5827,0x59f8,0x5a1f,
+ 0x5bb4,0xf98e,0x5ef6,0xf98f,0xf990,0x6350,0x633b,0xf991,0x693d,0x6c87,
+ 0x6cbf,0x6d8e,0x6d93,0x6df5,0x6f14,0xf992,0x70df,0x7136,0x7159,0xf993,
+ 0x71c3,0x71d5,0xf994,0x784f,0x786f,0xf995,0x7b75,0x7de3,0xf996,0x7e2f,
+ 0xf997,0x884d,0x8edf,0xf998,0xf999,0xf99a,0x925b,0xf99b,0x9cf6,0xf99c,
+ 0xf99d,0xf99e,0x6085,0x6d85,0xf99f,0x71b1,0xf9a0,0xf9a1,0x95b1,0x53ad,
+ 0xf9a2,0xf9a3,0xf9a4,0x67d3,0xf9a5,0x708e,0x7130,0x7430,0x8276,0x82d2
+ },
+ { /* ku 67 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf9a6,0x95bb,0x9ae5,0x9e7d,
+ 0x66c4,0xf9a7,0x71c1,0x8449,0xf9a8,0xf9a9,0x584b,0xf9aa,0xf9ab,0x5db8,
+ 0x5f71,0xf9ac,0x6620,0x668e,0x6979,0x69ae,0x6c38,0x6cf3,0x6e36,0x6f41,
+ 0x6fda,0x701b,0x702f,0x7150,0x71df,0x7370,0xf9ad,0x745b,0xf9ae,0x74d4,
+ 0x76c8,0x7a4e,0x7e93,0xf9af,0xf9b0,0x82f1,0x8a60,0x8fce,0xf9b1,0x9348,
+ 0xf9b2,0x9719,0xf9b3,0xf9b4,0x4e42,0x502a,0xf9b5,0x5208,0x53e1,0x66f3,
+ 0x6c6d,0x6fca,0x730a,0x777f,0x7a62,0x82ae,0x85dd,0x8602,0xf9b6,0x88d4,
+ 0x8a63,0x8b7d,0x8c6b,0xf9b7,0x92b3,0xf9b8,0x9713,0x9810,0x4e94,0x4f0d,
+ 0x4fc9,0x50b2,0x5348,0x543e,0x5433,0x55da,0x5862,0x58ba,0x5967,0x5a1b,
+ 0x5be4,0x609f,0xf9b9,0x61ca,0x6556,0x65ff,0x6664,0x68a7,0x6c5a,0x6fb3
+ },
+ { /* ku 68 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x70cf,0x71ac,0x7352,0x7b7d,
+ 0x8708,0x8aa4,0x9c32,0x9f07,0x5c4b,0x6c83,0x7344,0x7389,0x923a,0x6eab,
+ 0x7465,0x761f,0x7a69,0x7e15,0x860a,0x5140,0x58c5,0x64c1,0x74ee,0x7515,
+ 0x7670,0x7fc1,0x9095,0x96cd,0x9954,0x6e26,0x74e6,0x7aa9,0x7aaa,0x81e5,
+ 0x86d9,0x8778,0x8a1b,0x5a49,0x5b8c,0x5b9b,0x68a1,0x6900,0x6d63,0x73a9,
+ 0x7413,0x742c,0x7897,0x7de9,0x7feb,0x8118,0x8155,0x839e,0x8c4c,0x962e,
+ 0x9811,0x66f0,0x5f80,0x65fa,0x6789,0x6c6a,0x738b,0x502d,0x5a03,0x6b6a,
+ 0x77ee,0x5916,0x5d6c,0x5dcd,0x7325,0x754f,0xf9ba,0xf9bb,0x50e5,0x51f9,
+ 0x582f,0x592d,0x5996,0x59da,0x5be5,0xf9bc,0xf9bd,0x5da2,0x62d7,0x6416,
+ 0x6493,0x64fe,0xf9be,0x66dc,0xf9bf,0x6a48,0xf9c0,0x71ff,0x7464,0xf9c1
+ },
+ { /* ku 69 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a88,0x7aaf,0x7e47,0x7e5e,
+ 0x8000,0x8170,0xf9c2,0x87ef,0x8981,0x8b20,0x9059,0xf9c3,0x9080,0x9952,
+ 0x617e,0x6b32,0x6d74,0x7e1f,0x8925,0x8fb1,0x4fd1,0x50ad,0x5197,0x52c7,
+ 0x57c7,0x5889,0x5bb9,0x5eb8,0x6142,0x6995,0x6d8c,0x6e67,0x6eb6,0x7194,
+ 0x7462,0x7528,0x752c,0x8073,0x8338,0x84c9,0x8e0a,0x9394,0x93de,0xf9c4,
+ 0x4e8e,0x4f51,0x5076,0x512a,0x53c8,0x53cb,0x53f3,0x5b87,0x5bd3,0x5c24,
+ 0x611a,0x6182,0x65f4,0x725b,0x7397,0x7440,0x76c2,0x7950,0x7991,0x79b9,
+ 0x7d06,0x7fbd,0x828b,0x85d5,0x865e,0x8fc2,0x9047,0x90f5,0x91ea,0x9685,
+ 0x96e8,0x96e9,0x52d6,0x5f67,0x65ed,0x6631,0x682f,0x715c,0x7a36,0x90c1,
+ 0x980a,0x4e91,0xf9c5,0x6a52,0x6b9e,0x6f90,0x7189,0x8018,0x82b8,0x8553
+ },
+ { /* ku 6a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x904b,0x9695,0x96f2,0x97fb,
+ 0x851a,0x9b31,0x4e90,0x718a,0x96c4,0x5143,0x539f,0x54e1,0x5713,0x5712,
+ 0x57a3,0x5a9b,0x5ac4,0x5bc3,0x6028,0x613f,0x63f4,0x6c85,0x6d39,0x6e72,
+ 0x6e90,0x7230,0x733f,0x7457,0x82d1,0x8881,0x8f45,0x9060,0xf9c6,0x9662,
+ 0x9858,0x9d1b,0x6708,0x8d8a,0x925e,0x4f4d,0x5049,0x50de,0x5371,0x570d,
+ 0x59d4,0x5a01,0x5c09,0x6170,0x6690,0x6e2d,0x7232,0x744b,0x7def,0x80c3,
+ 0x840e,0x8466,0x853f,0x875f,0x885b,0x8918,0x8b02,0x9055,0x97cb,0x9b4f,
+ 0x4e73,0x4f91,0x5112,0x516a,0xf9c7,0x552f,0x55a9,0x5b7a,0x5ba5,0x5e7c,
+ 0x5e7d,0x5ebe,0x60a0,0x60df,0x6108,0x6109,0x63c4,0x6538,0x6709,0xf9c8,
+ 0x67d4,0x67da,0xf9c9,0x6961,0x6962,0x6cb9,0x6d27,0xf9ca,0x6e38,0xf9cb
+ },
+ { /* ku 6b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6fe1,0x7336,0x7337,0xf9cc,
+ 0x745c,0x7531,0xf9cd,0x7652,0xf9ce,0xf9cf,0x7dad,0x81fe,0x8438,0x88d5,
+ 0x8a98,0x8adb,0x8aed,0x8e30,0x8e42,0x904a,0x903e,0x907a,0x9149,0x91c9,
+ 0x936e,0xf9d0,0xf9d1,0x5809,0xf9d2,0x6bd3,0x8089,0x80b2,0xf9d3,0xf9d4,
+ 0x5141,0x596b,0x5c39,0xf9d5,0xf9d6,0x6f64,0x73a7,0x80e4,0x8d07,0xf9d7,
+ 0x9217,0x958f,0xf9d8,0xf9d9,0xf9da,0xf9db,0x807f,0x620e,0x701c,0x7d68,
+ 0x878d,0xf9dc,0x57a0,0x6069,0x6147,0x6bb7,0x8abe,0x9280,0x96b1,0x4e59,
+ 0x541f,0x6deb,0x852d,0x9670,0x97f3,0x98ee,0x63d6,0x6ce3,0x9091,0x51dd,
+ 0x61c9,0x81ba,0x9df9,0x4f9d,0x501a,0x5100,0x5b9c,0x610f,0x61ff,0x64ec,
+ 0x6905,0x6bc5,0x7591,0x77e3,0x7fa9,0x8264,0x858f,0x87fb,0x8863,0x8abc
+ },
+ { /* ku 6c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8b70,0x91ab,0x4e8c,0x4ee5,
+ 0x4f0a,0xf9dd,0xf9de,0x5937,0x59e8,0xf9df,0x5df2,0x5f1b,0x5f5b,0x6021,
+ 0xf9e0,0xf9e1,0xf9e2,0xf9e3,0x723e,0x73e5,0xf9e4,0x7570,0x75cd,0xf9e5,
+ 0x79fb,0xf9e6,0x800c,0x8033,0x8084,0x82e1,0x8351,0xf9e7,0xf9e8,0x8cbd,
+ 0x8cb3,0x9087,0xf9e9,0xf9ea,0x98f4,0x990c,0xf9eb,0xf9ec,0x7037,0x76ca,
+ 0x7fca,0x7fcc,0x7ffc,0x8b1a,0x4eba,0x4ec1,0x5203,0x5370,0xf9ed,0x54bd,
+ 0x56e0,0x59fb,0x5bc5,0x5f15,0x5fcd,0x6e6e,0xf9ee,0xf9ef,0x7d6a,0x8335,
+ 0xf9f0,0x8693,0x8a8d,0xf9f1,0x976d,0x9777,0xf9f2,0xf9f3,0x4e00,0x4f5a,
+ 0x4f7e,0x58f9,0x65e5,0x6ea2,0x9038,0x93b0,0x99b9,0x4efb,0x58ec,0x598a,
+ 0x59d9,0x6041,0xf9f4,0xf9f5,0x7a14,0xf9f6,0x834f,0x8cc3,0x5165,0x5344
+ },
+ { /* ku 6d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf9f7,0xf9f8,0xf9f9,0x4ecd,
+ 0x5269,0x5b55,0x82bf,0x4ed4,0x523a,0x54a8,0x59c9,0x59ff,0x5b50,0x5b57,
+ 0x5b5c,0x6063,0x6148,0x6ecb,0x7099,0x716e,0x7386,0x74f7,0x75b5,0x78c1,
+ 0x7d2b,0x8005,0x81ea,0x8328,0x8517,0x85c9,0x8aee,0x8cc7,0x96cc,0x4f5c,
+ 0x52fa,0x56bc,0x65ab,0x6628,0x707c,0x70b8,0x7235,0x7dbd,0x828d,0x914c,
+ 0x96c0,0x9d72,0x5b71,0x68e7,0x6b98,0x6f7a,0x76de,0x5c91,0x66ab,0x6f5b,
+ 0x7bb4,0x7c2a,0x8836,0x96dc,0x4e08,0x4ed7,0x5320,0x5834,0x58bb,0x58ef,
+ 0x596c,0x5c07,0x5e33,0x5e84,0x5f35,0x638c,0x66b2,0x6756,0x6a1f,0x6aa3,
+ 0x6b0c,0x6f3f,0x7246,0xf9fa,0x7350,0x748b,0x7ae0,0x7ca7,0x8178,0x81df,
+ 0x81e7,0x838a,0x846c,0x8523,0x8594,0x85cf,0x88dd,0x8d13,0x91ac,0x9577
+ },
+ { /* ku 6e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x969c,0x518d,0x54c9,0x5728,
+ 0x5bb0,0x624d,0x6750,0x683d,0x6893,0x6e3d,0x6ed3,0x707d,0x7e21,0x88c1,
+ 0x8ca1,0x8f09,0x9f4b,0x9f4e,0x722d,0x7b8f,0x8acd,0x931a,0x4f47,0x4f4e,
+ 0x5132,0x5480,0x59d0,0x5e95,0x62b5,0x6775,0x696e,0x6a17,0x6cae,0x6e1a,
+ 0x72d9,0x732a,0x75bd,0x7bb8,0x7d35,0x82e7,0x83f9,0x8457,0x85f7,0x8a5b,
+ 0x8caf,0x8e87,0x9019,0x90b8,0x96ce,0x9f5f,0x52e3,0x540a,0x5ae1,0x5bc2,
+ 0x6458,0x6575,0x6ef4,0x72c4,0xf9fb,0x7684,0x7a4d,0x7b1b,0x7c4d,0x7e3e,
+ 0x7fdf,0x837b,0x8b2b,0x8cca,0x8d64,0x8de1,0x8e5f,0x8fea,0x8ff9,0x9069,
+ 0x93d1,0x4f43,0x4f7a,0x50b3,0x5168,0x5178,0x524d,0x526a,0x5861,0x587c,
+ 0x5960,0x5c08,0x5c55,0x5edb,0x609b,0x6230,0x6813,0x6bbf,0x6c08,0x6fb1
+ },
+ { /* ku 6f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x714e,0x7420,0x7530,0x7538,
+ 0x7551,0x7672,0x7b4c,0x7b8b,0x7bad,0x7bc6,0x7e8f,0x8a6e,0x8f3e,0x8f49,
+ 0x923f,0x9293,0x9322,0x942b,0x96fb,0x985a,0x986b,0x991e,0x5207,0x622a,
+ 0x6298,0x6d59,0x7664,0x7aca,0x7bc0,0x7d76,0x5360,0x5cbe,0x5e97,0x6f38,
+ 0x70b9,0x7c98,0x9711,0x9b8e,0x9ede,0x63a5,0x647a,0x8776,0x4e01,0x4e95,
+ 0x4ead,0x505c,0x5075,0x5448,0x59c3,0x5b9a,0x5e40,0x5ead,0x5ef7,0x5f81,
+ 0x60c5,0x633a,0x653f,0x6574,0x65cc,0x6676,0x6678,0x67fe,0x6968,0x6a89,
+ 0x6b63,0x6c40,0x6dc0,0x6de8,0x6e1f,0x6e5e,0x701e,0x70a1,0x738e,0x73fd,
+ 0x753a,0x775b,0x7887,0x798e,0x7a0b,0x7a7d,0x7cbe,0x7d8e,0x8247,0x8a02,
+ 0x8aea,0x8c9e,0x912d,0x914a,0x91d8,0x9266,0x92cc,0x9320,0x9706,0x9756
+ },
+ { /* ku 70 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x975c,0x9802,0x9f0e,0x5236,
+ 0x5291,0x557c,0x5824,0x5e1d,0x5f1f,0x608c,0x63d0,0x68af,0x6fdf,0x796d,
+ 0x7b2c,0x81cd,0x85ba,0x88fd,0x8af8,0x8e44,0x918d,0x9664,0x969b,0x973d,
+ 0x984c,0x9f4a,0x4fce,0x5146,0x51cb,0x52a9,0x5632,0x5f14,0x5f6b,0x63aa,
+ 0x64cd,0x65e9,0x6641,0x66fa,0x66f9,0x671d,0x689d,0x68d7,0x69fd,0x6f15,
+ 0x6f6e,0x7167,0x71e5,0x722a,0x74aa,0x773a,0x7956,0x795a,0x79df,0x7a20,
+ 0x7a95,0x7c97,0x7cdf,0x7d44,0x7e70,0x8087,0x85fb,0x86a4,0x8a54,0x8abf,
+ 0x8d99,0x8e81,0x9020,0x906d,0x91e3,0x963b,0x96d5,0x9ce5,0x65cf,0x7c07,
+ 0x8db3,0x93c3,0x5b58,0x5c0a,0x5352,0x62d9,0x731d,0x5027,0x5b97,0x5f9e,
+ 0x60b0,0x616b,0x68d5,0x6dd9,0x742e,0x7a2e,0x7d42,0x7d9c,0x7e31,0x816b
+ },
+ { /* ku 71 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8e2a,0x8e35,0x937e,0x9418,
+ 0x4f50,0x5750,0x5de6,0x5ea7,0x632b,0x7f6a,0x4e3b,0x4f4f,0x4f8f,0x505a,
+ 0x59dd,0x80c4,0x546a,0x5468,0x55fe,0x594f,0x5b99,0x5dde,0x5eda,0x665d,
+ 0x6731,0x67f1,0x682a,0x6ce8,0x6d32,0x6e4a,0x6f8d,0x70b7,0x73e0,0x7587,
+ 0x7c4c,0x7d02,0x7d2c,0x7da2,0x821f,0x86db,0x8a3b,0x8a85,0x8d70,0x8e8a,
+ 0x8f33,0x9031,0x914e,0x9152,0x9444,0x99d0,0x7af9,0x7ca5,0x4fca,0x5101,
+ 0x51c6,0x57c8,0x5bef,0x5cfb,0x6659,0x6a3d,0x6d5a,0x6e96,0x6fec,0x710c,
+ 0x756f,0x7ae3,0x8822,0x9021,0x9075,0x96cb,0x99ff,0x8301,0x4e2d,0x4ef2,
+ 0x8846,0x91cd,0x537d,0x6adb,0x696b,0x6c41,0x847a,0x589e,0x618e,0x66fe,
+ 0x62ef,0x70dd,0x7511,0x75c7,0x7e52,0x84b8,0x8b49,0x8d08,0x4e4b,0x53ea
+ },
+ { /* ku 72 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x54ab,0x5730,0x5740,0x5fd7,
+ 0x6301,0x6307,0x646f,0x652f,0x65e8,0x667a,0x679d,0x67b3,0x6b62,0x6c60,
+ 0x6c9a,0x6f2c,0x77e5,0x7825,0x7949,0x7957,0x7d19,0x80a2,0x8102,0x81f3,
+ 0x829d,0x82b7,0x8718,0x8a8c,0xf9fc,0x8d04,0x8dbe,0x9072,0x76f4,0x7a19,
+ 0x7a37,0x7e54,0x8077,0x5507,0x55d4,0x5875,0x632f,0x6422,0x6649,0x664b,
+ 0x686d,0x699b,0x6b84,0x6d25,0x6eb1,0x73cd,0x7468,0x74a1,0x755b,0x75b9,
+ 0x76e1,0x771e,0x778b,0x79e6,0x7e09,0x7e1d,0x81fb,0x852f,0x8897,0x8a3a,
+ 0x8cd1,0x8eeb,0x8fb0,0x9032,0x93ad,0x9663,0x9673,0x9707,0x4f84,0x53f1,
+ 0x59ea,0x5ac9,0x5e19,0x684e,0x74c6,0x75be,0x79e9,0x7a92,0x81a3,0x86ed,
+ 0x8cea,0x8dcc,0x8fed,0x659f,0x6715,0xf9fd,0x57f7,0x6f57,0x7ddd,0x8f2f
+ },
+ { /* ku 73 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x93f6,0x96c6,0x5fb5,0x61f2,
+ 0x6f84,0x4e14,0x4f98,0x501f,0x53c9,0x55df,0x5d6f,0x5dee,0x6b21,0x6b64,
+ 0x78cb,0x7b9a,0xf9fe,0x8e49,0x8eca,0x906e,0x6349,0x643e,0x7740,0x7a84,
+ 0x932f,0x947f,0x9f6a,0x64b0,0x6faf,0x71e6,0x74a8,0x74da,0x7ac4,0x7c12,
+ 0x7e82,0x7cb2,0x7e98,0x8b9a,0x8d0a,0x947d,0x9910,0x994c,0x5239,0x5bdf,
+ 0x64e6,0x672d,0x7d2e,0x50ed,0x53c3,0x5879,0x6158,0x6159,0x61fa,0x65ac,
+ 0x7ad9,0x8b92,0x8b96,0x5009,0x5021,0x5275,0x5531,0x5a3c,0x5ee0,0x5f70,
+ 0x6134,0x655e,0x660c,0x6636,0x66a2,0x69cd,0x6ec4,0x6f32,0x7316,0x7621,
+ 0x7a93,0x8139,0x8259,0x83d6,0x84bc,0x50b5,0x57f0,0x5bc0,0x5be8,0x5f69,
+ 0x63a1,0x7826,0x7db5,0x83dc,0x8521,0x91c7,0x91f5,0x518a,0x67f5,0x7b56
+ },
+ { /* ku 74 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8cac,0x51c4,0x59bb,0x60bd,
+ 0x8655,0x501c,0xf9ff,0x5254,0x5c3a,0x617d,0x621a,0x62d3,0x64f2,0x65a5,
+ 0x6ecc,0x7620,0x810a,0x8e60,0x965f,0x96bb,0x4edf,0x5343,0x5598,0x5929,
+ 0x5ddd,0x64c5,0x6cc9,0x6dfa,0x7394,0x7a7f,0x821b,0x85a6,0x8ce4,0x8e10,
+ 0x9077,0x91e7,0x95e1,0x9621,0x97c6,0x51f8,0x54f2,0x5586,0x5fb9,0x64a4,
+ 0x6f88,0x7db4,0x8f1f,0x8f4d,0x9435,0x50c9,0x5c16,0x6cbe,0x6dfb,0x751b,
+ 0x77bb,0x7c3d,0x7c64,0x8a79,0x8ac2,0x581e,0x59be,0x5e16,0x6377,0x7252,
+ 0x758a,0x776b,0x8adc,0x8cbc,0x8f12,0x5ef3,0x6674,0x6df8,0x807d,0x83c1,
+ 0x8acb,0x9751,0x9bd6,0xfa00,0x5243,0x66ff,0x6d95,0x6eef,0x7de0,0x8ae6,
+ 0x902e,0x905e,0x9ad4,0x521d,0x527f,0x54e8,0x6194,0x6284,0x62db,0x68a2
+ },
+ { /* ku 75 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6912,0x695a,0x6a35,0x7092,
+ 0x7126,0x785d,0x7901,0x790e,0x79d2,0x7a0d,0x8096,0x8278,0x82d5,0x8349,
+ 0x8549,0x8c82,0x8d85,0x9162,0x918b,0x91ae,0x4fc3,0x56d1,0x71ed,0x77d7,
+ 0x8700,0x89f8,0x5bf8,0x5fd6,0x6751,0x90a8,0x53e2,0x585a,0x5bf5,0x60a4,
+ 0x6181,0x6460,0x7e3d,0x8070,0x8525,0x9283,0x64ae,0x50ac,0x5d14,0x6700,
+ 0x589c,0x62bd,0x63a8,0x690e,0x6978,0x6a1e,0x6e6b,0x76ba,0x79cb,0x82bb,
+ 0x8429,0x8acf,0x8da8,0x8ffd,0x9112,0x914b,0x919c,0x9310,0x9318,0x939a,
+ 0x96db,0x9a36,0x9c0d,0x4e11,0x755c,0x795d,0x7afa,0x7b51,0x7bc9,0x7e2e,
+ 0x84c4,0x8e59,0x8e74,0x8ef8,0x9010,0x6625,0x693f,0x7443,0x51fa,0x672e,
+ 0x9edc,0x5145,0x5fe0,0x6c96,0x87f2,0x885d,0x8877,0x60b4,0x81b5,0x8403
+ },
+ { /* ku 76 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d05,0x53d6,0x5439,0x5634,
+ 0x5a36,0x5c31,0x708a,0x7fe0,0x805a,0x8106,0x81ed,0x8da3,0x9189,0x9a5f,
+ 0x9df2,0x5074,0x4ec4,0x53a0,0x60fb,0x6e2c,0x5c64,0x4f88,0x5024,0x55e4,
+ 0x5cd9,0x5e5f,0x6065,0x6894,0x6cbb,0x6dc4,0x71be,0x75d4,0x75f4,0x7661,
+ 0x7a1a,0x7a49,0x7dc7,0x7dfb,0x7f6e,0x81f4,0x86a9,0x8f1c,0x96c9,0x99b3,
+ 0x9f52,0x5247,0x52c5,0x98ed,0x89aa,0x4e03,0x67d2,0x6f06,0x4fb5,0x5be2,
+ 0x6795,0x6c88,0x6d78,0x741b,0x7827,0x91dd,0x937c,0x87c4,0x79e4,0x7a31,
+ 0x5feb,0x4ed6,0x54a4,0x553e,0x58ae,0x59a5,0x60f0,0x6253,0x62d6,0x6736,
+ 0x6955,0x8235,0x9640,0x99b1,0x99dd,0x502c,0x5353,0x5544,0x577c,0xfa01,
+ 0x6258,0xfa02,0x64e2,0x666b,0x67dd,0x6fc1,0x6fef,0x7422,0x7438,0x8a17
+ },
+ { /* ku 77 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9438,0x5451,0x5606,0x5766,
+ 0x5f48,0x619a,0x6b4e,0x7058,0x70ad,0x7dbb,0x8a95,0x596a,0x812b,0x63a2,
+ 0x7708,0x803d,0x8caa,0x5854,0x642d,0x69bb,0x5b95,0x5e11,0x6e6f,0xfa03,
+ 0x8569,0x514c,0x53f0,0x592a,0x6020,0x614b,0x6b86,0x6c70,0x6cf0,0x7b1e,
+ 0x80ce,0x82d4,0x8dc6,0x90b0,0x98b1,0xfa04,0x64c7,0x6fa4,0x6491,0x6504,
+ 0x514e,0x5410,0x571f,0x8a0e,0x615f,0x6876,0xfa05,0x75db,0x7b52,0x7d71,
+ 0x901a,0x5806,0x69cc,0x817f,0x892a,0x9000,0x9839,0x5078,0x5957,0x59ac,
+ 0x6295,0x900f,0x9b2a,0x615d,0x7279,0x95d6,0x5761,0x5a46,0x5df4,0x628a,
+ 0x64ad,0x64fa,0x6777,0x6ce2,0x6d3e,0x722c,0x7436,0x7834,0x7f77,0x82ad,
+ 0x8ddb,0x9817,0x5224,0x5742,0x677f,0x7248,0x74e3,0x8ca9,0x8fa6,0x9211
+ },
+ { /* ku 78 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x962a,0x516b,0x53ed,0x634c,
+ 0x4f69,0x5504,0x6096,0x6557,0x6c9b,0x6d7f,0x724c,0x72fd,0x7a17,0x8987,
+ 0x8c9d,0x5f6d,0x6f8e,0x70f9,0x81a8,0x610e,0x4fbf,0x504f,0x6241,0x7247,
+ 0x7bc7,0x7de8,0x7fe9,0x904d,0x97ad,0x9a19,0x8cb6,0x576a,0x5e73,0x67b0,
+ 0x840d,0x8a55,0x5420,0x5b16,0x5e63,0x5ee2,0x5f0a,0x6583,0x80ba,0x853d,
+ 0x9589,0x965b,0x4f48,0x5305,0x530d,0x530f,0x5486,0x54fa,0x5703,0x5e03,
+ 0x6016,0x629b,0x62b1,0x6355,0xfa06,0x6ce1,0x6d66,0x75b1,0x7832,0x80de,
+ 0x812f,0x82de,0x8461,0x84b2,0x888d,0x8912,0x900b,0x92ea,0x98fd,0x9b91,
+ 0x5e45,0x66b4,0x66dd,0x7011,0x7206,0xfa07,0x4ff5,0x527d,0x5f6a,0x6153,
+ 0x6753,0x6a19,0x6f02,0x74e2,0x7968,0x8868,0x8c79,0x98c7,0x98c4,0x9a43
+ },
+ { /* ku 79 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x54c1,0x7a1f,0x6953,0x8af7,
+ 0x8c4a,0x98a8,0x99ae,0x5f7c,0x62ab,0x75b2,0x76ae,0x88ab,0x907f,0x9642,
+ 0x5339,0x5f3c,0x5fc5,0x6ccc,0x73cc,0x7562,0x758b,0x7b46,0x82fe,0x999d,
+ 0x4e4f,0x903c,0x4e0b,0x4f55,0x53a6,0x590f,0x5ec8,0x6630,0x6cb3,0x7455,
+ 0x8377,0x8766,0x8cc0,0x9050,0x971e,0x9c15,0x58d1,0x5b78,0x8650,0x8b14,
+ 0x9db4,0x5bd2,0x6068,0x608d,0x65f1,0x6c57,0x6f22,0x6fa3,0x701a,0x7f55,
+ 0x7ff0,0x9591,0x9592,0x9650,0x97d3,0x5272,0x8f44,0x51fd,0x542b,0x54b8,
+ 0x5563,0x558a,0x6abb,0x6db5,0x7dd8,0x8266,0x929c,0x9677,0x9e79,0x5408,
+ 0x54c8,0x76d2,0x86e4,0x95a4,0x95d4,0x965c,0x4ea2,0x4f09,0x59ee,0x5ae6,
+ 0x5df7,0x6052,0x6297,0x676d,0x6841,0x6c86,0x6e2f,0x7f38,0x809b,0x822a
+ },
+ { /* ku 7a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xfa08,0xfa09,0x9805,0x4ea5,
+ 0x5055,0x54b3,0x5793,0x595a,0x5b69,0x5bb3,0x61c8,0x6977,0x6d77,0x7023,
+ 0x87f9,0x89e3,0x8a72,0x8ae7,0x9082,0x99ed,0x9ab8,0x52be,0x6838,0x5016,
+ 0x5e78,0x674f,0x8347,0x884c,0x4eab,0x5411,0x56ae,0x73e6,0x9115,0x97ff,
+ 0x9909,0x9957,0x9999,0x5653,0x589f,0x865b,0x8a31,0x61b2,0x6af6,0x737b,
+ 0x8ed2,0x6b47,0x96aa,0x9a57,0x5955,0x7200,0x8d6b,0x9769,0x4fd4,0x5cf4,
+ 0x5f26,0x61f8,0x665b,0x6ceb,0x70ab,0x7384,0x73b9,0x73fe,0x7729,0x774d,
+ 0x7d43,0x7d62,0x7e23,0x8237,0x8852,0xfa0a,0x8ce2,0x9249,0x986f,0x5b51,
+ 0x7a74,0x8840,0x9801,0x5acc,0x4fe0,0x5354,0x593e,0x5cfd,0x633e,0x6d79,
+ 0x72f9,0x8105,0x8107,0x83a2,0x92cf,0x9830,0x4ea8,0x5144,0x5211,0x578b
+ },
+ { /* ku 7b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5f62,0x6cc2,0x6ece,0x7005,
+ 0x7050,0x70af,0x7192,0x73e9,0x7469,0x834a,0x87a2,0x8861,0x9008,0x90a2,
+ 0x93a3,0x99a8,0x516e,0x5f57,0x60e0,0x6167,0x66b3,0x8559,0x8e4a,0x91af,
+ 0x978b,0x4e4e,0x4e92,0x547c,0x58d5,0x58fa,0x597d,0x5cb5,0x5f27,0x6236,
+ 0x6248,0x660a,0x6667,0x6beb,0x6d69,0x6dcf,0x6e56,0x6ef8,0x6f94,0x6fe0,
+ 0x6fe9,0x705d,0x72d0,0x7425,0x745a,0x74e0,0x7693,0x795c,0x7cca,0x7e1e,
+ 0x80e1,0x82a6,0x846b,0x84bf,0x864e,0x865f,0x8774,0x8b77,0x8c6a,0x93ac,
+ 0x9800,0x9865,0x60d1,0x6216,0x9177,0x5a5a,0x660f,0x6df7,0x6e3e,0x743f,
+ 0x9b42,0x5ffd,0x60da,0x7b0f,0x54c4,0x5f18,0x6c5e,0x6cd3,0x6d2a,0x70d8,
+ 0x7d05,0x8679,0x8a0c,0x9d3b,0x5316,0x548c,0x5b05,0x6a3a,0x706b,0x7575
+ },
+ { /* ku 7c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x798d,0x79be,0x82b1,0x83ef,
+ 0x8a71,0x8b41,0x8ca8,0x9774,0xfa0b,0x64f4,0x652b,0x78ba,0x78bb,0x7a6b,
+ 0x4e38,0x559a,0x5950,0x5ba6,0x5e7b,0x60a3,0x63db,0x6b61,0x6665,0x6853,
+ 0x6e19,0x7165,0x74b0,0x7d08,0x9084,0x9a69,0x9c25,0x6d3b,0x6ed1,0x733e,
+ 0x8c41,0x95ca,0x51f0,0x5e4c,0x5fa8,0x604d,0x60f6,0x6130,0x614c,0x6643,
+ 0x6644,0x69a5,0x6cc1,0x6e5f,0x6ec9,0x6f62,0x714c,0x749c,0x7687,0x7bc1,
+ 0x7c27,0x8352,0x8757,0x9051,0x968d,0x9ec3,0x532f,0x56de,0x5efb,0x5f8a,
+ 0x6062,0x6094,0x61f7,0x6666,0x6703,0x6a9c,0x6dee,0x6fae,0x7070,0x736a,
+ 0x7e6a,0x81be,0x8334,0x86d4,0x8aa8,0x8cc4,0x5283,0x7372,0x5b96,0x6a6b,
+ 0x9404,0x54ee,0x5686,0x5b5d,0x6548,0x6585,0x66c9,0x689f,0x6d8d,0x6dc6
+ },
+ { /* ku 7d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x723b,0x80b4,0x9175,0x9a4d,
+ 0x4faf,0x5019,0x539a,0x540e,0x543c,0x5589,0x55c5,0x5e3f,0x5f8c,0x673d,
+ 0x7166,0x73dd,0x9005,0x52db,0x52f3,0x5864,0x58ce,0x7104,0x718f,0x71fb,
+ 0x85b0,0x8a13,0x6688,0x85a8,0x55a7,0x6684,0x714a,0x8431,0x5349,0x5599,
+ 0x6bc1,0x5f59,0x5fbd,0x63ee,0x6689,0x7147,0x8af1,0x8f1d,0x9ebe,0x4f11,
+ 0x643a,0x70cb,0x7566,0x8667,0x6064,0x8b4e,0x9df8,0x5147,0x51f6,0x5308,
+ 0x6d36,0x80f8,0x9ed1,0x6615,0x6b23,0x7098,0x75d5,0x5403,0x5c79,0x7d07,
+ 0x8a16,0x6b20,0x6b3d,0x6b46,0x5438,0x6070,0x6d3d,0x7fd5,0x8208,0x50d6,
+ 0x51de,0x559c,0x566b,0x56cd,0x59ec,0x5b09,0x5e0c,0x6199,0x6198,0x6231,
+ 0x665e,0x66e6,0x7199,0x71b9,0x71ba,0x72a7,0x79a7,0x7a00,0x7fb2,0x8a70
+ }
+};
diff --git a/imap/src/charset/tis_620.c b/imap/src/charset/tis_620.c
new file mode 100644
index 00000000..e1a9bcd8
--- /dev/null
+++ b/imap/src/charset/tis_620.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TIS 620-2529 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 October 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* TIS 620-2529 is the "Thai Industrial Standard for Thai Character Code
+ * for Computer", published by the Thai Industrial Standards Institute,
+ * Ministry of Industry of Thailand.
+ */
+
+static const unsigned short tis620tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ UBOGON,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
diff --git a/imap/src/charset/tmap.c b/imap/src/charset/tmap.c
new file mode 100644
index 00000000..7b523180
--- /dev/null
+++ b/imap/src/charset/tmap.c
@@ -0,0 +1,1487 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode title case mapping table (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 April 2006
+ * Last Edited: 6 December 2006
+ */
+
+
+#define UCS4_TMAPMAX 0x2d25 /* size of mapping table */
+
+#define UCS4_TMAPHIMIN 0xff41 /* high mapping minimum */
+#define UCS4_TMAPHIMAX 0xff5a /* high mapping maximum */
+#define UCS4_TMAPHIMAP 0x20 /* high mapping offset */
+
+ /* Values for Deseret */
+#define UCS4_TMAPDESERETMIN 0x10428
+#define UCS4_TMAPDESERETMAX 0x1044f
+#define UCS4_TMAPDESERETMAP 0x28
+
+static const unsigned short ucs4_tmaptab[UCS4_TMAPMAX+1] = {
+ 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,
+ 0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
+ 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,
+ 0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
+ 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x007b,0x007c,0x007d,0x007e,0x007f,
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x039c,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00f7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x0178,
+ 0x0100,0x0100,0x0102,0x0102,0x0104,0x0104,0x0106,0x0106,
+ 0x0108,0x0108,0x010a,0x010a,0x010c,0x010c,0x010e,0x010e,
+ 0x0110,0x0110,0x0112,0x0112,0x0114,0x0114,0x0116,0x0116,
+ 0x0118,0x0118,0x011a,0x011a,0x011c,0x011c,0x011e,0x011e,
+ 0x0120,0x0120,0x0122,0x0122,0x0124,0x0124,0x0126,0x0126,
+ 0x0128,0x0128,0x012a,0x012a,0x012c,0x012c,0x012e,0x012e,
+ 0x0130,0x0049,0x0132,0x0132,0x0134,0x0134,0x0136,0x0136,
+ 0x0138,0x0139,0x0139,0x013b,0x013b,0x013d,0x013d,0x013f,
+ 0x013f,0x0141,0x0141,0x0143,0x0143,0x0145,0x0145,0x0147,
+ 0x0147,0x0149,0x014a,0x014a,0x014c,0x014c,0x014e,0x014e,
+ 0x0150,0x0150,0x0152,0x0152,0x0154,0x0154,0x0156,0x0156,
+ 0x0158,0x0158,0x015a,0x015a,0x015c,0x015c,0x015e,0x015e,
+ 0x0160,0x0160,0x0162,0x0162,0x0164,0x0164,0x0166,0x0166,
+ 0x0168,0x0168,0x016a,0x016a,0x016c,0x016c,0x016e,0x016e,
+ 0x0170,0x0170,0x0172,0x0172,0x0174,0x0174,0x0176,0x0176,
+ 0x0178,0x0179,0x0179,0x017b,0x017b,0x017d,0x017d,0x0053,
+ 0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,
+ 0x0187,0x0189,0x018a,0x018b,0x018b,0x018d,0x018e,0x018f,
+ 0x0190,0x0191,0x0191,0x0193,0x0194,0x01f6,0x0196,0x0197,
+ 0x0198,0x0198,0x023d,0x019b,0x019c,0x019d,0x0220,0x019f,
+ 0x01a0,0x01a0,0x01a2,0x01a2,0x01a4,0x01a4,0x01a6,0x01a7,
+ 0x01a7,0x01a9,0x01aa,0x01ab,0x01ac,0x01ac,0x01ae,0x01af,
+ 0x01af,0x01b1,0x01b2,0x01b3,0x01b3,0x01b5,0x01b5,0x01b7,
+ 0x01b8,0x01b8,0x01ba,0x01bb,0x01bc,0x01bc,0x01be,0x01f7,
+ 0x01c0,0x01c1,0x01c2,0x01c3,0x01c5,0x01c5,0x01c5,0x01c8,
+ 0x01c8,0x01c8,0x01cb,0x01cb,0x01cb,0x01cd,0x01cd,0x01cf,
+ 0x01cf,0x01d1,0x01d1,0x01d3,0x01d3,0x01d5,0x01d5,0x01d7,
+ 0x01d7,0x01d9,0x01d9,0x01db,0x01db,0x018e,0x01de,0x01de,
+ 0x01e0,0x01e0,0x01e2,0x01e2,0x01e4,0x01e4,0x01e6,0x01e6,
+ 0x01e8,0x01e8,0x01ea,0x01ea,0x01ec,0x01ec,0x01ee,0x01ee,
+ 0x01f0,0x01f2,0x01f2,0x01f2,0x01f4,0x01f4,0x01f6,0x01f7,
+ 0x01f8,0x01f8,0x01fa,0x01fa,0x01fc,0x01fc,0x01fe,0x01fe,
+ 0x0200,0x0200,0x0202,0x0202,0x0204,0x0204,0x0206,0x0206,
+ 0x0208,0x0208,0x020a,0x020a,0x020c,0x020c,0x020e,0x020e,
+ 0x0210,0x0210,0x0212,0x0212,0x0214,0x0214,0x0216,0x0216,
+ 0x0218,0x0218,0x021a,0x021a,0x021c,0x021c,0x021e,0x021e,
+ 0x0220,0x0221,0x0222,0x0222,0x0224,0x0224,0x0226,0x0226,
+ 0x0228,0x0228,0x022a,0x022a,0x022c,0x022c,0x022e,0x022e,
+ 0x0230,0x0230,0x0232,0x0232,0x0234,0x0235,0x0236,0x0237,
+ 0x0238,0x0239,0x023a,0x023b,0x023b,0x023d,0x023e,0x023f,
+ 0x0240,0x0241,0x0241,0x0243,0x0244,0x0245,0x0246,0x0246,
+ 0x0248,0x0248,0x024a,0x024a,0x024c,0x024c,0x024e,0x024e,
+ 0x0250,0x0251,0x0252,0x0181,0x0186,0x0255,0x0189,0x018a,
+ 0x0258,0x018f,0x025a,0x0190,0x025c,0x025d,0x025e,0x025f,
+ 0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,
+ 0x0197,0x0196,0x026a,0x2c62,0x026c,0x026d,0x026e,0x019c,
+ 0x0270,0x0271,0x019d,0x0273,0x0274,0x019f,0x0276,0x0277,
+ 0x0278,0x0279,0x027a,0x027b,0x027c,0x2c64,0x027e,0x027f,
+ 0x01a6,0x0281,0x0282,0x01a9,0x0284,0x0285,0x0286,0x0287,
+ 0x01ae,0x0244,0x01b1,0x01b2,0x0245,0x028d,0x028e,0x028f,
+ 0x0290,0x0291,0x01b7,0x0293,0x0294,0x0295,0x0296,0x0297,
+ 0x0298,0x0299,0x029a,0x029b,0x029c,0x029d,0x029e,0x029f,
+ 0x02a0,0x02a1,0x02a2,0x02a3,0x02a4,0x02a5,0x02a6,0x02a7,
+ 0x02a8,0x02a9,0x02aa,0x02ab,0x02ac,0x02ad,0x02ae,0x02af,
+ 0x02b0,0x02b1,0x02b2,0x02b3,0x02b4,0x02b5,0x02b6,0x02b7,
+ 0x02b8,0x02b9,0x02ba,0x02bb,0x02bc,0x02bd,0x02be,0x02bf,
+ 0x02c0,0x02c1,0x02c2,0x02c3,0x02c4,0x02c5,0x02c6,0x02c7,
+ 0x02c8,0x02c9,0x02ca,0x02cb,0x02cc,0x02cd,0x02ce,0x02cf,
+ 0x02d0,0x02d1,0x02d2,0x02d3,0x02d4,0x02d5,0x02d6,0x02d7,
+ 0x02d8,0x02d9,0x02da,0x02db,0x02dc,0x02dd,0x02de,0x02df,
+ 0x02e0,0x02e1,0x02e2,0x02e3,0x02e4,0x02e5,0x02e6,0x02e7,
+ 0x02e8,0x02e9,0x02ea,0x02eb,0x02ec,0x02ed,0x02ee,0x02ef,
+ 0x02f0,0x02f1,0x02f2,0x02f3,0x02f4,0x02f5,0x02f6,0x02f7,
+ 0x02f8,0x02f9,0x02fa,0x02fb,0x02fc,0x02fd,0x02fe,0x02ff,
+ 0x0300,0x0301,0x0302,0x0303,0x0304,0x0305,0x0306,0x0307,
+ 0x0308,0x0309,0x030a,0x030b,0x030c,0x030d,0x030e,0x030f,
+ 0x0310,0x0311,0x0312,0x0313,0x0314,0x0315,0x0316,0x0317,
+ 0x0318,0x0319,0x031a,0x031b,0x031c,0x031d,0x031e,0x031f,
+ 0x0320,0x0321,0x0322,0x0323,0x0324,0x0325,0x0326,0x0327,
+ 0x0328,0x0329,0x032a,0x032b,0x032c,0x032d,0x032e,0x032f,
+ 0x0330,0x0331,0x0332,0x0333,0x0334,0x0335,0x0336,0x0337,
+ 0x0338,0x0339,0x033a,0x033b,0x033c,0x033d,0x033e,0x033f,
+ 0x0340,0x0341,0x0342,0x0343,0x0344,0x0399,0x0346,0x0347,
+ 0x0348,0x0349,0x034a,0x034b,0x034c,0x034d,0x034e,0x034f,
+ 0x0350,0x0351,0x0352,0x0353,0x0354,0x0355,0x0356,0x0357,
+ 0x0358,0x0359,0x035a,0x035b,0x035c,0x035d,0x035e,0x035f,
+ 0x0360,0x0361,0x0362,0x0363,0x0364,0x0365,0x0366,0x0367,
+ 0x0368,0x0369,0x036a,0x036b,0x036c,0x036d,0x036e,0x036f,
+ 0x0370,0x0371,0x0372,0x0373,0x0374,0x0375,0x0376,0x0377,
+ 0x0378,0x0379,0x037a,0x03fd,0x03fe,0x03ff,0x037e,0x037f,
+ 0x0380,0x0381,0x0382,0x0383,0x0384,0x0385,0x0386,0x0387,
+ 0x0388,0x0389,0x038a,0x038b,0x038c,0x038d,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,0x03a2,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x0386,0x0388,0x0389,0x038a,
+ 0x03b0,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,0x03a3,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x038c,0x038e,0x038f,0x03cf,
+ 0x0392,0x0398,0x03d2,0x03d3,0x03d4,0x03a6,0x03a0,0x03d7,
+ 0x03d8,0x03d8,0x03da,0x03da,0x03dc,0x03dc,0x03de,0x03de,
+ 0x03e0,0x03e0,0x03e2,0x03e2,0x03e4,0x03e4,0x03e6,0x03e6,
+ 0x03e8,0x03e8,0x03ea,0x03ea,0x03ec,0x03ec,0x03ee,0x03ee,
+ 0x039a,0x03a1,0x03f9,0x03f3,0x03f4,0x0395,0x03f6,0x03f7,
+ 0x03f7,0x03f9,0x03fa,0x03fa,0x03fc,0x03fd,0x03fe,0x03ff,
+ 0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,0x040e,0x040f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,0x040e,0x040f,
+ 0x0460,0x0460,0x0462,0x0462,0x0464,0x0464,0x0466,0x0466,
+ 0x0468,0x0468,0x046a,0x046a,0x046c,0x046c,0x046e,0x046e,
+ 0x0470,0x0470,0x0472,0x0472,0x0474,0x0474,0x0476,0x0476,
+ 0x0478,0x0478,0x047a,0x047a,0x047c,0x047c,0x047e,0x047e,
+ 0x0480,0x0480,0x0482,0x0483,0x0484,0x0485,0x0486,0x0487,
+ 0x0488,0x0489,0x048a,0x048a,0x048c,0x048c,0x048e,0x048e,
+ 0x0490,0x0490,0x0492,0x0492,0x0494,0x0494,0x0496,0x0496,
+ 0x0498,0x0498,0x049a,0x049a,0x049c,0x049c,0x049e,0x049e,
+ 0x04a0,0x04a0,0x04a2,0x04a2,0x04a4,0x04a4,0x04a6,0x04a6,
+ 0x04a8,0x04a8,0x04aa,0x04aa,0x04ac,0x04ac,0x04ae,0x04ae,
+ 0x04b0,0x04b0,0x04b2,0x04b2,0x04b4,0x04b4,0x04b6,0x04b6,
+ 0x04b8,0x04b8,0x04ba,0x04ba,0x04bc,0x04bc,0x04be,0x04be,
+ 0x04c0,0x04c1,0x04c1,0x04c3,0x04c3,0x04c5,0x04c5,0x04c7,
+ 0x04c7,0x04c9,0x04c9,0x04cb,0x04cb,0x04cd,0x04cd,0x04c0,
+ 0x04d0,0x04d0,0x04d2,0x04d2,0x04d4,0x04d4,0x04d6,0x04d6,
+ 0x04d8,0x04d8,0x04da,0x04da,0x04dc,0x04dc,0x04de,0x04de,
+ 0x04e0,0x04e0,0x04e2,0x04e2,0x04e4,0x04e4,0x04e6,0x04e6,
+ 0x04e8,0x04e8,0x04ea,0x04ea,0x04ec,0x04ec,0x04ee,0x04ee,
+ 0x04f0,0x04f0,0x04f2,0x04f2,0x04f4,0x04f4,0x04f6,0x04f6,
+ 0x04f8,0x04f8,0x04fa,0x04fa,0x04fc,0x04fc,0x04fe,0x04fe,
+ 0x0500,0x0500,0x0502,0x0502,0x0504,0x0504,0x0506,0x0506,
+ 0x0508,0x0508,0x050a,0x050a,0x050c,0x050c,0x050e,0x050e,
+ 0x0510,0x0510,0x0512,0x0512,0x0514,0x0515,0x0516,0x0517,
+ 0x0518,0x0519,0x051a,0x051b,0x051c,0x051d,0x051e,0x051f,
+ 0x0520,0x0521,0x0522,0x0523,0x0524,0x0525,0x0526,0x0527,
+ 0x0528,0x0529,0x052a,0x052b,0x052c,0x052d,0x052e,0x052f,
+ 0x0530,0x0531,0x0532,0x0533,0x0534,0x0535,0x0536,0x0537,
+ 0x0538,0x0539,0x053a,0x053b,0x053c,0x053d,0x053e,0x053f,
+ 0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,0x0547,
+ 0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,0x054f,
+ 0x0550,0x0551,0x0552,0x0553,0x0554,0x0555,0x0556,0x0557,
+ 0x0558,0x0559,0x055a,0x055b,0x055c,0x055d,0x055e,0x055f,
+ 0x0560,0x0531,0x0532,0x0533,0x0534,0x0535,0x0536,0x0537,
+ 0x0538,0x0539,0x053a,0x053b,0x053c,0x053d,0x053e,0x053f,
+ 0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,0x0547,
+ 0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,0x054f,
+ 0x0550,0x0551,0x0552,0x0553,0x0554,0x0555,0x0556,0x0587,
+ 0x0588,0x0589,0x058a,0x058b,0x058c,0x058d,0x058e,0x058f,
+ 0x0590,0x0591,0x0592,0x0593,0x0594,0x0595,0x0596,0x0597,
+ 0x0598,0x0599,0x059a,0x059b,0x059c,0x059d,0x059e,0x059f,
+ 0x05a0,0x05a1,0x05a2,0x05a3,0x05a4,0x05a5,0x05a6,0x05a7,
+ 0x05a8,0x05a9,0x05aa,0x05ab,0x05ac,0x05ad,0x05ae,0x05af,
+ 0x05b0,0x05b1,0x05b2,0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,
+ 0x05b8,0x05b9,0x05ba,0x05bb,0x05bc,0x05bd,0x05be,0x05bf,
+ 0x05c0,0x05c1,0x05c2,0x05c3,0x05c4,0x05c5,0x05c6,0x05c7,
+ 0x05c8,0x05c9,0x05ca,0x05cb,0x05cc,0x05cd,0x05ce,0x05cf,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,0x05eb,0x05ec,0x05ed,0x05ee,0x05ef,
+ 0x05f0,0x05f1,0x05f2,0x05f3,0x05f4,0x05f5,0x05f6,0x05f7,
+ 0x05f8,0x05f9,0x05fa,0x05fb,0x05fc,0x05fd,0x05fe,0x05ff,
+ 0x0600,0x0601,0x0602,0x0603,0x0604,0x0605,0x0606,0x0607,
+ 0x0608,0x0609,0x060a,0x060b,0x060c,0x060d,0x060e,0x060f,
+ 0x0610,0x0611,0x0612,0x0613,0x0614,0x0615,0x0616,0x0617,
+ 0x0618,0x0619,0x061a,0x061b,0x061c,0x061d,0x061e,0x061f,
+ 0x0620,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x0637,
+ 0x0638,0x0639,0x063a,0x063b,0x063c,0x063d,0x063e,0x063f,
+ 0x0640,0x0641,0x0642,0x0643,0x0644,0x0645,0x0646,0x0647,
+ 0x0648,0x0649,0x064a,0x064b,0x064c,0x064d,0x064e,0x064f,
+ 0x0650,0x0651,0x0652,0x0653,0x0654,0x0655,0x0656,0x0657,
+ 0x0658,0x0659,0x065a,0x065b,0x065c,0x065d,0x065e,0x065f,
+ 0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,
+ 0x0668,0x0669,0x066a,0x066b,0x066c,0x066d,0x066e,0x066f,
+ 0x0670,0x0671,0x0672,0x0673,0x0674,0x0675,0x0676,0x0677,
+ 0x0678,0x0679,0x067a,0x067b,0x067c,0x067d,0x067e,0x067f,
+ 0x0680,0x0681,0x0682,0x0683,0x0684,0x0685,0x0686,0x0687,
+ 0x0688,0x0689,0x068a,0x068b,0x068c,0x068d,0x068e,0x068f,
+ 0x0690,0x0691,0x0692,0x0693,0x0694,0x0695,0x0696,0x0697,
+ 0x0698,0x0699,0x069a,0x069b,0x069c,0x069d,0x069e,0x069f,
+ 0x06a0,0x06a1,0x06a2,0x06a3,0x06a4,0x06a5,0x06a6,0x06a7,
+ 0x06a8,0x06a9,0x06aa,0x06ab,0x06ac,0x06ad,0x06ae,0x06af,
+ 0x06b0,0x06b1,0x06b2,0x06b3,0x06b4,0x06b5,0x06b6,0x06b7,
+ 0x06b8,0x06b9,0x06ba,0x06bb,0x06bc,0x06bd,0x06be,0x06bf,
+ 0x06c0,0x06c1,0x06c2,0x06c3,0x06c4,0x06c5,0x06c6,0x06c7,
+ 0x06c8,0x06c9,0x06ca,0x06cb,0x06cc,0x06cd,0x06ce,0x06cf,
+ 0x06d0,0x06d1,0x06d2,0x06d3,0x06d4,0x06d5,0x06d6,0x06d7,
+ 0x06d8,0x06d9,0x06da,0x06db,0x06dc,0x06dd,0x06de,0x06df,
+ 0x06e0,0x06e1,0x06e2,0x06e3,0x06e4,0x06e5,0x06e6,0x06e7,
+ 0x06e8,0x06e9,0x06ea,0x06eb,0x06ec,0x06ed,0x06ee,0x06ef,
+ 0x06f0,0x06f1,0x06f2,0x06f3,0x06f4,0x06f5,0x06f6,0x06f7,
+ 0x06f8,0x06f9,0x06fa,0x06fb,0x06fc,0x06fd,0x06fe,0x06ff,
+ 0x0700,0x0701,0x0702,0x0703,0x0704,0x0705,0x0706,0x0707,
+ 0x0708,0x0709,0x070a,0x070b,0x070c,0x070d,0x070e,0x070f,
+ 0x0710,0x0711,0x0712,0x0713,0x0714,0x0715,0x0716,0x0717,
+ 0x0718,0x0719,0x071a,0x071b,0x071c,0x071d,0x071e,0x071f,
+ 0x0720,0x0721,0x0722,0x0723,0x0724,0x0725,0x0726,0x0727,
+ 0x0728,0x0729,0x072a,0x072b,0x072c,0x072d,0x072e,0x072f,
+ 0x0730,0x0731,0x0732,0x0733,0x0734,0x0735,0x0736,0x0737,
+ 0x0738,0x0739,0x073a,0x073b,0x073c,0x073d,0x073e,0x073f,
+ 0x0740,0x0741,0x0742,0x0743,0x0744,0x0745,0x0746,0x0747,
+ 0x0748,0x0749,0x074a,0x074b,0x074c,0x074d,0x074e,0x074f,
+ 0x0750,0x0751,0x0752,0x0753,0x0754,0x0755,0x0756,0x0757,
+ 0x0758,0x0759,0x075a,0x075b,0x075c,0x075d,0x075e,0x075f,
+ 0x0760,0x0761,0x0762,0x0763,0x0764,0x0765,0x0766,0x0767,
+ 0x0768,0x0769,0x076a,0x076b,0x076c,0x076d,0x076e,0x076f,
+ 0x0770,0x0771,0x0772,0x0773,0x0774,0x0775,0x0776,0x0777,
+ 0x0778,0x0779,0x077a,0x077b,0x077c,0x077d,0x077e,0x077f,
+ 0x0780,0x0781,0x0782,0x0783,0x0784,0x0785,0x0786,0x0787,
+ 0x0788,0x0789,0x078a,0x078b,0x078c,0x078d,0x078e,0x078f,
+ 0x0790,0x0791,0x0792,0x0793,0x0794,0x0795,0x0796,0x0797,
+ 0x0798,0x0799,0x079a,0x079b,0x079c,0x079d,0x079e,0x079f,
+ 0x07a0,0x07a1,0x07a2,0x07a3,0x07a4,0x07a5,0x07a6,0x07a7,
+ 0x07a8,0x07a9,0x07aa,0x07ab,0x07ac,0x07ad,0x07ae,0x07af,
+ 0x07b0,0x07b1,0x07b2,0x07b3,0x07b4,0x07b5,0x07b6,0x07b7,
+ 0x07b8,0x07b9,0x07ba,0x07bb,0x07bc,0x07bd,0x07be,0x07bf,
+ 0x07c0,0x07c1,0x07c2,0x07c3,0x07c4,0x07c5,0x07c6,0x07c7,
+ 0x07c8,0x07c9,0x07ca,0x07cb,0x07cc,0x07cd,0x07ce,0x07cf,
+ 0x07d0,0x07d1,0x07d2,0x07d3,0x07d4,0x07d5,0x07d6,0x07d7,
+ 0x07d8,0x07d9,0x07da,0x07db,0x07dc,0x07dd,0x07de,0x07df,
+ 0x07e0,0x07e1,0x07e2,0x07e3,0x07e4,0x07e5,0x07e6,0x07e7,
+ 0x07e8,0x07e9,0x07ea,0x07eb,0x07ec,0x07ed,0x07ee,0x07ef,
+ 0x07f0,0x07f1,0x07f2,0x07f3,0x07f4,0x07f5,0x07f6,0x07f7,
+ 0x07f8,0x07f9,0x07fa,0x07fb,0x07fc,0x07fd,0x07fe,0x07ff,
+ 0x0800,0x0801,0x0802,0x0803,0x0804,0x0805,0x0806,0x0807,
+ 0x0808,0x0809,0x080a,0x080b,0x080c,0x080d,0x080e,0x080f,
+ 0x0810,0x0811,0x0812,0x0813,0x0814,0x0815,0x0816,0x0817,
+ 0x0818,0x0819,0x081a,0x081b,0x081c,0x081d,0x081e,0x081f,
+ 0x0820,0x0821,0x0822,0x0823,0x0824,0x0825,0x0826,0x0827,
+ 0x0828,0x0829,0x082a,0x082b,0x082c,0x082d,0x082e,0x082f,
+ 0x0830,0x0831,0x0832,0x0833,0x0834,0x0835,0x0836,0x0837,
+ 0x0838,0x0839,0x083a,0x083b,0x083c,0x083d,0x083e,0x083f,
+ 0x0840,0x0841,0x0842,0x0843,0x0844,0x0845,0x0846,0x0847,
+ 0x0848,0x0849,0x084a,0x084b,0x084c,0x084d,0x084e,0x084f,
+ 0x0850,0x0851,0x0852,0x0853,0x0854,0x0855,0x0856,0x0857,
+ 0x0858,0x0859,0x085a,0x085b,0x085c,0x085d,0x085e,0x085f,
+ 0x0860,0x0861,0x0862,0x0863,0x0864,0x0865,0x0866,0x0867,
+ 0x0868,0x0869,0x086a,0x086b,0x086c,0x086d,0x086e,0x086f,
+ 0x0870,0x0871,0x0872,0x0873,0x0874,0x0875,0x0876,0x0877,
+ 0x0878,0x0879,0x087a,0x087b,0x087c,0x087d,0x087e,0x087f,
+ 0x0880,0x0881,0x0882,0x0883,0x0884,0x0885,0x0886,0x0887,
+ 0x0888,0x0889,0x088a,0x088b,0x088c,0x088d,0x088e,0x088f,
+ 0x0890,0x0891,0x0892,0x0893,0x0894,0x0895,0x0896,0x0897,
+ 0x0898,0x0899,0x089a,0x089b,0x089c,0x089d,0x089e,0x089f,
+ 0x08a0,0x08a1,0x08a2,0x08a3,0x08a4,0x08a5,0x08a6,0x08a7,
+ 0x08a8,0x08a9,0x08aa,0x08ab,0x08ac,0x08ad,0x08ae,0x08af,
+ 0x08b0,0x08b1,0x08b2,0x08b3,0x08b4,0x08b5,0x08b6,0x08b7,
+ 0x08b8,0x08b9,0x08ba,0x08bb,0x08bc,0x08bd,0x08be,0x08bf,
+ 0x08c0,0x08c1,0x08c2,0x08c3,0x08c4,0x08c5,0x08c6,0x08c7,
+ 0x08c8,0x08c9,0x08ca,0x08cb,0x08cc,0x08cd,0x08ce,0x08cf,
+ 0x08d0,0x08d1,0x08d2,0x08d3,0x08d4,0x08d5,0x08d6,0x08d7,
+ 0x08d8,0x08d9,0x08da,0x08db,0x08dc,0x08dd,0x08de,0x08df,
+ 0x08e0,0x08e1,0x08e2,0x08e3,0x08e4,0x08e5,0x08e6,0x08e7,
+ 0x08e8,0x08e9,0x08ea,0x08eb,0x08ec,0x08ed,0x08ee,0x08ef,
+ 0x08f0,0x08f1,0x08f2,0x08f3,0x08f4,0x08f5,0x08f6,0x08f7,
+ 0x08f8,0x08f9,0x08fa,0x08fb,0x08fc,0x08fd,0x08fe,0x08ff,
+ 0x0900,0x0901,0x0902,0x0903,0x0904,0x0905,0x0906,0x0907,
+ 0x0908,0x0909,0x090a,0x090b,0x090c,0x090d,0x090e,0x090f,
+ 0x0910,0x0911,0x0912,0x0913,0x0914,0x0915,0x0916,0x0917,
+ 0x0918,0x0919,0x091a,0x091b,0x091c,0x091d,0x091e,0x091f,
+ 0x0920,0x0921,0x0922,0x0923,0x0924,0x0925,0x0926,0x0927,
+ 0x0928,0x0929,0x092a,0x092b,0x092c,0x092d,0x092e,0x092f,
+ 0x0930,0x0931,0x0932,0x0933,0x0934,0x0935,0x0936,0x0937,
+ 0x0938,0x0939,0x093a,0x093b,0x093c,0x093d,0x093e,0x093f,
+ 0x0940,0x0941,0x0942,0x0943,0x0944,0x0945,0x0946,0x0947,
+ 0x0948,0x0949,0x094a,0x094b,0x094c,0x094d,0x094e,0x094f,
+ 0x0950,0x0951,0x0952,0x0953,0x0954,0x0955,0x0956,0x0957,
+ 0x0958,0x0959,0x095a,0x095b,0x095c,0x095d,0x095e,0x095f,
+ 0x0960,0x0961,0x0962,0x0963,0x0964,0x0965,0x0966,0x0967,
+ 0x0968,0x0969,0x096a,0x096b,0x096c,0x096d,0x096e,0x096f,
+ 0x0970,0x0971,0x0972,0x0973,0x0974,0x0975,0x0976,0x0977,
+ 0x0978,0x0979,0x097a,0x097b,0x097c,0x097d,0x097e,0x097f,
+ 0x0980,0x0981,0x0982,0x0983,0x0984,0x0985,0x0986,0x0987,
+ 0x0988,0x0989,0x098a,0x098b,0x098c,0x098d,0x098e,0x098f,
+ 0x0990,0x0991,0x0992,0x0993,0x0994,0x0995,0x0996,0x0997,
+ 0x0998,0x0999,0x099a,0x099b,0x099c,0x099d,0x099e,0x099f,
+ 0x09a0,0x09a1,0x09a2,0x09a3,0x09a4,0x09a5,0x09a6,0x09a7,
+ 0x09a8,0x09a9,0x09aa,0x09ab,0x09ac,0x09ad,0x09ae,0x09af,
+ 0x09b0,0x09b1,0x09b2,0x09b3,0x09b4,0x09b5,0x09b6,0x09b7,
+ 0x09b8,0x09b9,0x09ba,0x09bb,0x09bc,0x09bd,0x09be,0x09bf,
+ 0x09c0,0x09c1,0x09c2,0x09c3,0x09c4,0x09c5,0x09c6,0x09c7,
+ 0x09c8,0x09c9,0x09ca,0x09cb,0x09cc,0x09cd,0x09ce,0x09cf,
+ 0x09d0,0x09d1,0x09d2,0x09d3,0x09d4,0x09d5,0x09d6,0x09d7,
+ 0x09d8,0x09d9,0x09da,0x09db,0x09dc,0x09dd,0x09de,0x09df,
+ 0x09e0,0x09e1,0x09e2,0x09e3,0x09e4,0x09e5,0x09e6,0x09e7,
+ 0x09e8,0x09e9,0x09ea,0x09eb,0x09ec,0x09ed,0x09ee,0x09ef,
+ 0x09f0,0x09f1,0x09f2,0x09f3,0x09f4,0x09f5,0x09f6,0x09f7,
+ 0x09f8,0x09f9,0x09fa,0x09fb,0x09fc,0x09fd,0x09fe,0x09ff,
+ 0x0a00,0x0a01,0x0a02,0x0a03,0x0a04,0x0a05,0x0a06,0x0a07,
+ 0x0a08,0x0a09,0x0a0a,0x0a0b,0x0a0c,0x0a0d,0x0a0e,0x0a0f,
+ 0x0a10,0x0a11,0x0a12,0x0a13,0x0a14,0x0a15,0x0a16,0x0a17,
+ 0x0a18,0x0a19,0x0a1a,0x0a1b,0x0a1c,0x0a1d,0x0a1e,0x0a1f,
+ 0x0a20,0x0a21,0x0a22,0x0a23,0x0a24,0x0a25,0x0a26,0x0a27,
+ 0x0a28,0x0a29,0x0a2a,0x0a2b,0x0a2c,0x0a2d,0x0a2e,0x0a2f,
+ 0x0a30,0x0a31,0x0a32,0x0a33,0x0a34,0x0a35,0x0a36,0x0a37,
+ 0x0a38,0x0a39,0x0a3a,0x0a3b,0x0a3c,0x0a3d,0x0a3e,0x0a3f,
+ 0x0a40,0x0a41,0x0a42,0x0a43,0x0a44,0x0a45,0x0a46,0x0a47,
+ 0x0a48,0x0a49,0x0a4a,0x0a4b,0x0a4c,0x0a4d,0x0a4e,0x0a4f,
+ 0x0a50,0x0a51,0x0a52,0x0a53,0x0a54,0x0a55,0x0a56,0x0a57,
+ 0x0a58,0x0a59,0x0a5a,0x0a5b,0x0a5c,0x0a5d,0x0a5e,0x0a5f,
+ 0x0a60,0x0a61,0x0a62,0x0a63,0x0a64,0x0a65,0x0a66,0x0a67,
+ 0x0a68,0x0a69,0x0a6a,0x0a6b,0x0a6c,0x0a6d,0x0a6e,0x0a6f,
+ 0x0a70,0x0a71,0x0a72,0x0a73,0x0a74,0x0a75,0x0a76,0x0a77,
+ 0x0a78,0x0a79,0x0a7a,0x0a7b,0x0a7c,0x0a7d,0x0a7e,0x0a7f,
+ 0x0a80,0x0a81,0x0a82,0x0a83,0x0a84,0x0a85,0x0a86,0x0a87,
+ 0x0a88,0x0a89,0x0a8a,0x0a8b,0x0a8c,0x0a8d,0x0a8e,0x0a8f,
+ 0x0a90,0x0a91,0x0a92,0x0a93,0x0a94,0x0a95,0x0a96,0x0a97,
+ 0x0a98,0x0a99,0x0a9a,0x0a9b,0x0a9c,0x0a9d,0x0a9e,0x0a9f,
+ 0x0aa0,0x0aa1,0x0aa2,0x0aa3,0x0aa4,0x0aa5,0x0aa6,0x0aa7,
+ 0x0aa8,0x0aa9,0x0aaa,0x0aab,0x0aac,0x0aad,0x0aae,0x0aaf,
+ 0x0ab0,0x0ab1,0x0ab2,0x0ab3,0x0ab4,0x0ab5,0x0ab6,0x0ab7,
+ 0x0ab8,0x0ab9,0x0aba,0x0abb,0x0abc,0x0abd,0x0abe,0x0abf,
+ 0x0ac0,0x0ac1,0x0ac2,0x0ac3,0x0ac4,0x0ac5,0x0ac6,0x0ac7,
+ 0x0ac8,0x0ac9,0x0aca,0x0acb,0x0acc,0x0acd,0x0ace,0x0acf,
+ 0x0ad0,0x0ad1,0x0ad2,0x0ad3,0x0ad4,0x0ad5,0x0ad6,0x0ad7,
+ 0x0ad8,0x0ad9,0x0ada,0x0adb,0x0adc,0x0add,0x0ade,0x0adf,
+ 0x0ae0,0x0ae1,0x0ae2,0x0ae3,0x0ae4,0x0ae5,0x0ae6,0x0ae7,
+ 0x0ae8,0x0ae9,0x0aea,0x0aeb,0x0aec,0x0aed,0x0aee,0x0aef,
+ 0x0af0,0x0af1,0x0af2,0x0af3,0x0af4,0x0af5,0x0af6,0x0af7,
+ 0x0af8,0x0af9,0x0afa,0x0afb,0x0afc,0x0afd,0x0afe,0x0aff,
+ 0x0b00,0x0b01,0x0b02,0x0b03,0x0b04,0x0b05,0x0b06,0x0b07,
+ 0x0b08,0x0b09,0x0b0a,0x0b0b,0x0b0c,0x0b0d,0x0b0e,0x0b0f,
+ 0x0b10,0x0b11,0x0b12,0x0b13,0x0b14,0x0b15,0x0b16,0x0b17,
+ 0x0b18,0x0b19,0x0b1a,0x0b1b,0x0b1c,0x0b1d,0x0b1e,0x0b1f,
+ 0x0b20,0x0b21,0x0b22,0x0b23,0x0b24,0x0b25,0x0b26,0x0b27,
+ 0x0b28,0x0b29,0x0b2a,0x0b2b,0x0b2c,0x0b2d,0x0b2e,0x0b2f,
+ 0x0b30,0x0b31,0x0b32,0x0b33,0x0b34,0x0b35,0x0b36,0x0b37,
+ 0x0b38,0x0b39,0x0b3a,0x0b3b,0x0b3c,0x0b3d,0x0b3e,0x0b3f,
+ 0x0b40,0x0b41,0x0b42,0x0b43,0x0b44,0x0b45,0x0b46,0x0b47,
+ 0x0b48,0x0b49,0x0b4a,0x0b4b,0x0b4c,0x0b4d,0x0b4e,0x0b4f,
+ 0x0b50,0x0b51,0x0b52,0x0b53,0x0b54,0x0b55,0x0b56,0x0b57,
+ 0x0b58,0x0b59,0x0b5a,0x0b5b,0x0b5c,0x0b5d,0x0b5e,0x0b5f,
+ 0x0b60,0x0b61,0x0b62,0x0b63,0x0b64,0x0b65,0x0b66,0x0b67,
+ 0x0b68,0x0b69,0x0b6a,0x0b6b,0x0b6c,0x0b6d,0x0b6e,0x0b6f,
+ 0x0b70,0x0b71,0x0b72,0x0b73,0x0b74,0x0b75,0x0b76,0x0b77,
+ 0x0b78,0x0b79,0x0b7a,0x0b7b,0x0b7c,0x0b7d,0x0b7e,0x0b7f,
+ 0x0b80,0x0b81,0x0b82,0x0b83,0x0b84,0x0b85,0x0b86,0x0b87,
+ 0x0b88,0x0b89,0x0b8a,0x0b8b,0x0b8c,0x0b8d,0x0b8e,0x0b8f,
+ 0x0b90,0x0b91,0x0b92,0x0b93,0x0b94,0x0b95,0x0b96,0x0b97,
+ 0x0b98,0x0b99,0x0b9a,0x0b9b,0x0b9c,0x0b9d,0x0b9e,0x0b9f,
+ 0x0ba0,0x0ba1,0x0ba2,0x0ba3,0x0ba4,0x0ba5,0x0ba6,0x0ba7,
+ 0x0ba8,0x0ba9,0x0baa,0x0bab,0x0bac,0x0bad,0x0bae,0x0baf,
+ 0x0bb0,0x0bb1,0x0bb2,0x0bb3,0x0bb4,0x0bb5,0x0bb6,0x0bb7,
+ 0x0bb8,0x0bb9,0x0bba,0x0bbb,0x0bbc,0x0bbd,0x0bbe,0x0bbf,
+ 0x0bc0,0x0bc1,0x0bc2,0x0bc3,0x0bc4,0x0bc5,0x0bc6,0x0bc7,
+ 0x0bc8,0x0bc9,0x0bca,0x0bcb,0x0bcc,0x0bcd,0x0bce,0x0bcf,
+ 0x0bd0,0x0bd1,0x0bd2,0x0bd3,0x0bd4,0x0bd5,0x0bd6,0x0bd7,
+ 0x0bd8,0x0bd9,0x0bda,0x0bdb,0x0bdc,0x0bdd,0x0bde,0x0bdf,
+ 0x0be0,0x0be1,0x0be2,0x0be3,0x0be4,0x0be5,0x0be6,0x0be7,
+ 0x0be8,0x0be9,0x0bea,0x0beb,0x0bec,0x0bed,0x0bee,0x0bef,
+ 0x0bf0,0x0bf1,0x0bf2,0x0bf3,0x0bf4,0x0bf5,0x0bf6,0x0bf7,
+ 0x0bf8,0x0bf9,0x0bfa,0x0bfb,0x0bfc,0x0bfd,0x0bfe,0x0bff,
+ 0x0c00,0x0c01,0x0c02,0x0c03,0x0c04,0x0c05,0x0c06,0x0c07,
+ 0x0c08,0x0c09,0x0c0a,0x0c0b,0x0c0c,0x0c0d,0x0c0e,0x0c0f,
+ 0x0c10,0x0c11,0x0c12,0x0c13,0x0c14,0x0c15,0x0c16,0x0c17,
+ 0x0c18,0x0c19,0x0c1a,0x0c1b,0x0c1c,0x0c1d,0x0c1e,0x0c1f,
+ 0x0c20,0x0c21,0x0c22,0x0c23,0x0c24,0x0c25,0x0c26,0x0c27,
+ 0x0c28,0x0c29,0x0c2a,0x0c2b,0x0c2c,0x0c2d,0x0c2e,0x0c2f,
+ 0x0c30,0x0c31,0x0c32,0x0c33,0x0c34,0x0c35,0x0c36,0x0c37,
+ 0x0c38,0x0c39,0x0c3a,0x0c3b,0x0c3c,0x0c3d,0x0c3e,0x0c3f,
+ 0x0c40,0x0c41,0x0c42,0x0c43,0x0c44,0x0c45,0x0c46,0x0c47,
+ 0x0c48,0x0c49,0x0c4a,0x0c4b,0x0c4c,0x0c4d,0x0c4e,0x0c4f,
+ 0x0c50,0x0c51,0x0c52,0x0c53,0x0c54,0x0c55,0x0c56,0x0c57,
+ 0x0c58,0x0c59,0x0c5a,0x0c5b,0x0c5c,0x0c5d,0x0c5e,0x0c5f,
+ 0x0c60,0x0c61,0x0c62,0x0c63,0x0c64,0x0c65,0x0c66,0x0c67,
+ 0x0c68,0x0c69,0x0c6a,0x0c6b,0x0c6c,0x0c6d,0x0c6e,0x0c6f,
+ 0x0c70,0x0c71,0x0c72,0x0c73,0x0c74,0x0c75,0x0c76,0x0c77,
+ 0x0c78,0x0c79,0x0c7a,0x0c7b,0x0c7c,0x0c7d,0x0c7e,0x0c7f,
+ 0x0c80,0x0c81,0x0c82,0x0c83,0x0c84,0x0c85,0x0c86,0x0c87,
+ 0x0c88,0x0c89,0x0c8a,0x0c8b,0x0c8c,0x0c8d,0x0c8e,0x0c8f,
+ 0x0c90,0x0c91,0x0c92,0x0c93,0x0c94,0x0c95,0x0c96,0x0c97,
+ 0x0c98,0x0c99,0x0c9a,0x0c9b,0x0c9c,0x0c9d,0x0c9e,0x0c9f,
+ 0x0ca0,0x0ca1,0x0ca2,0x0ca3,0x0ca4,0x0ca5,0x0ca6,0x0ca7,
+ 0x0ca8,0x0ca9,0x0caa,0x0cab,0x0cac,0x0cad,0x0cae,0x0caf,
+ 0x0cb0,0x0cb1,0x0cb2,0x0cb3,0x0cb4,0x0cb5,0x0cb6,0x0cb7,
+ 0x0cb8,0x0cb9,0x0cba,0x0cbb,0x0cbc,0x0cbd,0x0cbe,0x0cbf,
+ 0x0cc0,0x0cc1,0x0cc2,0x0cc3,0x0cc4,0x0cc5,0x0cc6,0x0cc7,
+ 0x0cc8,0x0cc9,0x0cca,0x0ccb,0x0ccc,0x0ccd,0x0cce,0x0ccf,
+ 0x0cd0,0x0cd1,0x0cd2,0x0cd3,0x0cd4,0x0cd5,0x0cd6,0x0cd7,
+ 0x0cd8,0x0cd9,0x0cda,0x0cdb,0x0cdc,0x0cdd,0x0cde,0x0cdf,
+ 0x0ce0,0x0ce1,0x0ce2,0x0ce3,0x0ce4,0x0ce5,0x0ce6,0x0ce7,
+ 0x0ce8,0x0ce9,0x0cea,0x0ceb,0x0cec,0x0ced,0x0cee,0x0cef,
+ 0x0cf0,0x0cf1,0x0cf2,0x0cf3,0x0cf4,0x0cf5,0x0cf6,0x0cf7,
+ 0x0cf8,0x0cf9,0x0cfa,0x0cfb,0x0cfc,0x0cfd,0x0cfe,0x0cff,
+ 0x0d00,0x0d01,0x0d02,0x0d03,0x0d04,0x0d05,0x0d06,0x0d07,
+ 0x0d08,0x0d09,0x0d0a,0x0d0b,0x0d0c,0x0d0d,0x0d0e,0x0d0f,
+ 0x0d10,0x0d11,0x0d12,0x0d13,0x0d14,0x0d15,0x0d16,0x0d17,
+ 0x0d18,0x0d19,0x0d1a,0x0d1b,0x0d1c,0x0d1d,0x0d1e,0x0d1f,
+ 0x0d20,0x0d21,0x0d22,0x0d23,0x0d24,0x0d25,0x0d26,0x0d27,
+ 0x0d28,0x0d29,0x0d2a,0x0d2b,0x0d2c,0x0d2d,0x0d2e,0x0d2f,
+ 0x0d30,0x0d31,0x0d32,0x0d33,0x0d34,0x0d35,0x0d36,0x0d37,
+ 0x0d38,0x0d39,0x0d3a,0x0d3b,0x0d3c,0x0d3d,0x0d3e,0x0d3f,
+ 0x0d40,0x0d41,0x0d42,0x0d43,0x0d44,0x0d45,0x0d46,0x0d47,
+ 0x0d48,0x0d49,0x0d4a,0x0d4b,0x0d4c,0x0d4d,0x0d4e,0x0d4f,
+ 0x0d50,0x0d51,0x0d52,0x0d53,0x0d54,0x0d55,0x0d56,0x0d57,
+ 0x0d58,0x0d59,0x0d5a,0x0d5b,0x0d5c,0x0d5d,0x0d5e,0x0d5f,
+ 0x0d60,0x0d61,0x0d62,0x0d63,0x0d64,0x0d65,0x0d66,0x0d67,
+ 0x0d68,0x0d69,0x0d6a,0x0d6b,0x0d6c,0x0d6d,0x0d6e,0x0d6f,
+ 0x0d70,0x0d71,0x0d72,0x0d73,0x0d74,0x0d75,0x0d76,0x0d77,
+ 0x0d78,0x0d79,0x0d7a,0x0d7b,0x0d7c,0x0d7d,0x0d7e,0x0d7f,
+ 0x0d80,0x0d81,0x0d82,0x0d83,0x0d84,0x0d85,0x0d86,0x0d87,
+ 0x0d88,0x0d89,0x0d8a,0x0d8b,0x0d8c,0x0d8d,0x0d8e,0x0d8f,
+ 0x0d90,0x0d91,0x0d92,0x0d93,0x0d94,0x0d95,0x0d96,0x0d97,
+ 0x0d98,0x0d99,0x0d9a,0x0d9b,0x0d9c,0x0d9d,0x0d9e,0x0d9f,
+ 0x0da0,0x0da1,0x0da2,0x0da3,0x0da4,0x0da5,0x0da6,0x0da7,
+ 0x0da8,0x0da9,0x0daa,0x0dab,0x0dac,0x0dad,0x0dae,0x0daf,
+ 0x0db0,0x0db1,0x0db2,0x0db3,0x0db4,0x0db5,0x0db6,0x0db7,
+ 0x0db8,0x0db9,0x0dba,0x0dbb,0x0dbc,0x0dbd,0x0dbe,0x0dbf,
+ 0x0dc0,0x0dc1,0x0dc2,0x0dc3,0x0dc4,0x0dc5,0x0dc6,0x0dc7,
+ 0x0dc8,0x0dc9,0x0dca,0x0dcb,0x0dcc,0x0dcd,0x0dce,0x0dcf,
+ 0x0dd0,0x0dd1,0x0dd2,0x0dd3,0x0dd4,0x0dd5,0x0dd6,0x0dd7,
+ 0x0dd8,0x0dd9,0x0dda,0x0ddb,0x0ddc,0x0ddd,0x0dde,0x0ddf,
+ 0x0de0,0x0de1,0x0de2,0x0de3,0x0de4,0x0de5,0x0de6,0x0de7,
+ 0x0de8,0x0de9,0x0dea,0x0deb,0x0dec,0x0ded,0x0dee,0x0def,
+ 0x0df0,0x0df1,0x0df2,0x0df3,0x0df4,0x0df5,0x0df6,0x0df7,
+ 0x0df8,0x0df9,0x0dfa,0x0dfb,0x0dfc,0x0dfd,0x0dfe,0x0dff,
+ 0x0e00,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,0x0e3b,0x0e3c,0x0e3d,0x0e3e,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,0x0e5c,0x0e5d,0x0e5e,0x0e5f,
+ 0x0e60,0x0e61,0x0e62,0x0e63,0x0e64,0x0e65,0x0e66,0x0e67,
+ 0x0e68,0x0e69,0x0e6a,0x0e6b,0x0e6c,0x0e6d,0x0e6e,0x0e6f,
+ 0x0e70,0x0e71,0x0e72,0x0e73,0x0e74,0x0e75,0x0e76,0x0e77,
+ 0x0e78,0x0e79,0x0e7a,0x0e7b,0x0e7c,0x0e7d,0x0e7e,0x0e7f,
+ 0x0e80,0x0e81,0x0e82,0x0e83,0x0e84,0x0e85,0x0e86,0x0e87,
+ 0x0e88,0x0e89,0x0e8a,0x0e8b,0x0e8c,0x0e8d,0x0e8e,0x0e8f,
+ 0x0e90,0x0e91,0x0e92,0x0e93,0x0e94,0x0e95,0x0e96,0x0e97,
+ 0x0e98,0x0e99,0x0e9a,0x0e9b,0x0e9c,0x0e9d,0x0e9e,0x0e9f,
+ 0x0ea0,0x0ea1,0x0ea2,0x0ea3,0x0ea4,0x0ea5,0x0ea6,0x0ea7,
+ 0x0ea8,0x0ea9,0x0eaa,0x0eab,0x0eac,0x0ead,0x0eae,0x0eaf,
+ 0x0eb0,0x0eb1,0x0eb2,0x0eb3,0x0eb4,0x0eb5,0x0eb6,0x0eb7,
+ 0x0eb8,0x0eb9,0x0eba,0x0ebb,0x0ebc,0x0ebd,0x0ebe,0x0ebf,
+ 0x0ec0,0x0ec1,0x0ec2,0x0ec3,0x0ec4,0x0ec5,0x0ec6,0x0ec7,
+ 0x0ec8,0x0ec9,0x0eca,0x0ecb,0x0ecc,0x0ecd,0x0ece,0x0ecf,
+ 0x0ed0,0x0ed1,0x0ed2,0x0ed3,0x0ed4,0x0ed5,0x0ed6,0x0ed7,
+ 0x0ed8,0x0ed9,0x0eda,0x0edb,0x0edc,0x0edd,0x0ede,0x0edf,
+ 0x0ee0,0x0ee1,0x0ee2,0x0ee3,0x0ee4,0x0ee5,0x0ee6,0x0ee7,
+ 0x0ee8,0x0ee9,0x0eea,0x0eeb,0x0eec,0x0eed,0x0eee,0x0eef,
+ 0x0ef0,0x0ef1,0x0ef2,0x0ef3,0x0ef4,0x0ef5,0x0ef6,0x0ef7,
+ 0x0ef8,0x0ef9,0x0efa,0x0efb,0x0efc,0x0efd,0x0efe,0x0eff,
+ 0x0f00,0x0f01,0x0f02,0x0f03,0x0f04,0x0f05,0x0f06,0x0f07,
+ 0x0f08,0x0f09,0x0f0a,0x0f0b,0x0f0c,0x0f0d,0x0f0e,0x0f0f,
+ 0x0f10,0x0f11,0x0f12,0x0f13,0x0f14,0x0f15,0x0f16,0x0f17,
+ 0x0f18,0x0f19,0x0f1a,0x0f1b,0x0f1c,0x0f1d,0x0f1e,0x0f1f,
+ 0x0f20,0x0f21,0x0f22,0x0f23,0x0f24,0x0f25,0x0f26,0x0f27,
+ 0x0f28,0x0f29,0x0f2a,0x0f2b,0x0f2c,0x0f2d,0x0f2e,0x0f2f,
+ 0x0f30,0x0f31,0x0f32,0x0f33,0x0f34,0x0f35,0x0f36,0x0f37,
+ 0x0f38,0x0f39,0x0f3a,0x0f3b,0x0f3c,0x0f3d,0x0f3e,0x0f3f,
+ 0x0f40,0x0f41,0x0f42,0x0f43,0x0f44,0x0f45,0x0f46,0x0f47,
+ 0x0f48,0x0f49,0x0f4a,0x0f4b,0x0f4c,0x0f4d,0x0f4e,0x0f4f,
+ 0x0f50,0x0f51,0x0f52,0x0f53,0x0f54,0x0f55,0x0f56,0x0f57,
+ 0x0f58,0x0f59,0x0f5a,0x0f5b,0x0f5c,0x0f5d,0x0f5e,0x0f5f,
+ 0x0f60,0x0f61,0x0f62,0x0f63,0x0f64,0x0f65,0x0f66,0x0f67,
+ 0x0f68,0x0f69,0x0f6a,0x0f6b,0x0f6c,0x0f6d,0x0f6e,0x0f6f,
+ 0x0f70,0x0f71,0x0f72,0x0f73,0x0f74,0x0f75,0x0f76,0x0f77,
+ 0x0f78,0x0f79,0x0f7a,0x0f7b,0x0f7c,0x0f7d,0x0f7e,0x0f7f,
+ 0x0f80,0x0f81,0x0f82,0x0f83,0x0f84,0x0f85,0x0f86,0x0f87,
+ 0x0f88,0x0f89,0x0f8a,0x0f8b,0x0f8c,0x0f8d,0x0f8e,0x0f8f,
+ 0x0f90,0x0f91,0x0f92,0x0f93,0x0f94,0x0f95,0x0f96,0x0f97,
+ 0x0f98,0x0f99,0x0f9a,0x0f9b,0x0f9c,0x0f9d,0x0f9e,0x0f9f,
+ 0x0fa0,0x0fa1,0x0fa2,0x0fa3,0x0fa4,0x0fa5,0x0fa6,0x0fa7,
+ 0x0fa8,0x0fa9,0x0faa,0x0fab,0x0fac,0x0fad,0x0fae,0x0faf,
+ 0x0fb0,0x0fb1,0x0fb2,0x0fb3,0x0fb4,0x0fb5,0x0fb6,0x0fb7,
+ 0x0fb8,0x0fb9,0x0fba,0x0fbb,0x0fbc,0x0fbd,0x0fbe,0x0fbf,
+ 0x0fc0,0x0fc1,0x0fc2,0x0fc3,0x0fc4,0x0fc5,0x0fc6,0x0fc7,
+ 0x0fc8,0x0fc9,0x0fca,0x0fcb,0x0fcc,0x0fcd,0x0fce,0x0fcf,
+ 0x0fd0,0x0fd1,0x0fd2,0x0fd3,0x0fd4,0x0fd5,0x0fd6,0x0fd7,
+ 0x0fd8,0x0fd9,0x0fda,0x0fdb,0x0fdc,0x0fdd,0x0fde,0x0fdf,
+ 0x0fe0,0x0fe1,0x0fe2,0x0fe3,0x0fe4,0x0fe5,0x0fe6,0x0fe7,
+ 0x0fe8,0x0fe9,0x0fea,0x0feb,0x0fec,0x0fed,0x0fee,0x0fef,
+ 0x0ff0,0x0ff1,0x0ff2,0x0ff3,0x0ff4,0x0ff5,0x0ff6,0x0ff7,
+ 0x0ff8,0x0ff9,0x0ffa,0x0ffb,0x0ffc,0x0ffd,0x0ffe,0x0fff,
+ 0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007,
+ 0x1008,0x1009,0x100a,0x100b,0x100c,0x100d,0x100e,0x100f,
+ 0x1010,0x1011,0x1012,0x1013,0x1014,0x1015,0x1016,0x1017,
+ 0x1018,0x1019,0x101a,0x101b,0x101c,0x101d,0x101e,0x101f,
+ 0x1020,0x1021,0x1022,0x1023,0x1024,0x1025,0x1026,0x1027,
+ 0x1028,0x1029,0x102a,0x102b,0x102c,0x102d,0x102e,0x102f,
+ 0x1030,0x1031,0x1032,0x1033,0x1034,0x1035,0x1036,0x1037,
+ 0x1038,0x1039,0x103a,0x103b,0x103c,0x103d,0x103e,0x103f,
+ 0x1040,0x1041,0x1042,0x1043,0x1044,0x1045,0x1046,0x1047,
+ 0x1048,0x1049,0x104a,0x104b,0x104c,0x104d,0x104e,0x104f,
+ 0x1050,0x1051,0x1052,0x1053,0x1054,0x1055,0x1056,0x1057,
+ 0x1058,0x1059,0x105a,0x105b,0x105c,0x105d,0x105e,0x105f,
+ 0x1060,0x1061,0x1062,0x1063,0x1064,0x1065,0x1066,0x1067,
+ 0x1068,0x1069,0x106a,0x106b,0x106c,0x106d,0x106e,0x106f,
+ 0x1070,0x1071,0x1072,0x1073,0x1074,0x1075,0x1076,0x1077,
+ 0x1078,0x1079,0x107a,0x107b,0x107c,0x107d,0x107e,0x107f,
+ 0x1080,0x1081,0x1082,0x1083,0x1084,0x1085,0x1086,0x1087,
+ 0x1088,0x1089,0x108a,0x108b,0x108c,0x108d,0x108e,0x108f,
+ 0x1090,0x1091,0x1092,0x1093,0x1094,0x1095,0x1096,0x1097,
+ 0x1098,0x1099,0x109a,0x109b,0x109c,0x109d,0x109e,0x109f,
+ 0x10a0,0x10a1,0x10a2,0x10a3,0x10a4,0x10a5,0x10a6,0x10a7,
+ 0x10a8,0x10a9,0x10aa,0x10ab,0x10ac,0x10ad,0x10ae,0x10af,
+ 0x10b0,0x10b1,0x10b2,0x10b3,0x10b4,0x10b5,0x10b6,0x10b7,
+ 0x10b8,0x10b9,0x10ba,0x10bb,0x10bc,0x10bd,0x10be,0x10bf,
+ 0x10c0,0x10c1,0x10c2,0x10c3,0x10c4,0x10c5,0x10c6,0x10c7,
+ 0x10c8,0x10c9,0x10ca,0x10cb,0x10cc,0x10cd,0x10ce,0x10cf,
+ 0x10d0,0x10d1,0x10d2,0x10d3,0x10d4,0x10d5,0x10d6,0x10d7,
+ 0x10d8,0x10d9,0x10da,0x10db,0x10dc,0x10dd,0x10de,0x10df,
+ 0x10e0,0x10e1,0x10e2,0x10e3,0x10e4,0x10e5,0x10e6,0x10e7,
+ 0x10e8,0x10e9,0x10ea,0x10eb,0x10ec,0x10ed,0x10ee,0x10ef,
+ 0x10f0,0x10f1,0x10f2,0x10f3,0x10f4,0x10f5,0x10f6,0x10f7,
+ 0x10f8,0x10f9,0x10fa,0x10fb,0x10fc,0x10fd,0x10fe,0x10ff,
+ 0x1100,0x1101,0x1102,0x1103,0x1104,0x1105,0x1106,0x1107,
+ 0x1108,0x1109,0x110a,0x110b,0x110c,0x110d,0x110e,0x110f,
+ 0x1110,0x1111,0x1112,0x1113,0x1114,0x1115,0x1116,0x1117,
+ 0x1118,0x1119,0x111a,0x111b,0x111c,0x111d,0x111e,0x111f,
+ 0x1120,0x1121,0x1122,0x1123,0x1124,0x1125,0x1126,0x1127,
+ 0x1128,0x1129,0x112a,0x112b,0x112c,0x112d,0x112e,0x112f,
+ 0x1130,0x1131,0x1132,0x1133,0x1134,0x1135,0x1136,0x1137,
+ 0x1138,0x1139,0x113a,0x113b,0x113c,0x113d,0x113e,0x113f,
+ 0x1140,0x1141,0x1142,0x1143,0x1144,0x1145,0x1146,0x1147,
+ 0x1148,0x1149,0x114a,0x114b,0x114c,0x114d,0x114e,0x114f,
+ 0x1150,0x1151,0x1152,0x1153,0x1154,0x1155,0x1156,0x1157,
+ 0x1158,0x1159,0x115a,0x115b,0x115c,0x115d,0x115e,0x115f,
+ 0x1160,0x1161,0x1162,0x1163,0x1164,0x1165,0x1166,0x1167,
+ 0x1168,0x1169,0x116a,0x116b,0x116c,0x116d,0x116e,0x116f,
+ 0x1170,0x1171,0x1172,0x1173,0x1174,0x1175,0x1176,0x1177,
+ 0x1178,0x1179,0x117a,0x117b,0x117c,0x117d,0x117e,0x117f,
+ 0x1180,0x1181,0x1182,0x1183,0x1184,0x1185,0x1186,0x1187,
+ 0x1188,0x1189,0x118a,0x118b,0x118c,0x118d,0x118e,0x118f,
+ 0x1190,0x1191,0x1192,0x1193,0x1194,0x1195,0x1196,0x1197,
+ 0x1198,0x1199,0x119a,0x119b,0x119c,0x119d,0x119e,0x119f,
+ 0x11a0,0x11a1,0x11a2,0x11a3,0x11a4,0x11a5,0x11a6,0x11a7,
+ 0x11a8,0x11a9,0x11aa,0x11ab,0x11ac,0x11ad,0x11ae,0x11af,
+ 0x11b0,0x11b1,0x11b2,0x11b3,0x11b4,0x11b5,0x11b6,0x11b7,
+ 0x11b8,0x11b9,0x11ba,0x11bb,0x11bc,0x11bd,0x11be,0x11bf,
+ 0x11c0,0x11c1,0x11c2,0x11c3,0x11c4,0x11c5,0x11c6,0x11c7,
+ 0x11c8,0x11c9,0x11ca,0x11cb,0x11cc,0x11cd,0x11ce,0x11cf,
+ 0x11d0,0x11d1,0x11d2,0x11d3,0x11d4,0x11d5,0x11d6,0x11d7,
+ 0x11d8,0x11d9,0x11da,0x11db,0x11dc,0x11dd,0x11de,0x11df,
+ 0x11e0,0x11e1,0x11e2,0x11e3,0x11e4,0x11e5,0x11e6,0x11e7,
+ 0x11e8,0x11e9,0x11ea,0x11eb,0x11ec,0x11ed,0x11ee,0x11ef,
+ 0x11f0,0x11f1,0x11f2,0x11f3,0x11f4,0x11f5,0x11f6,0x11f7,
+ 0x11f8,0x11f9,0x11fa,0x11fb,0x11fc,0x11fd,0x11fe,0x11ff,
+ 0x1200,0x1201,0x1202,0x1203,0x1204,0x1205,0x1206,0x1207,
+ 0x1208,0x1209,0x120a,0x120b,0x120c,0x120d,0x120e,0x120f,
+ 0x1210,0x1211,0x1212,0x1213,0x1214,0x1215,0x1216,0x1217,
+ 0x1218,0x1219,0x121a,0x121b,0x121c,0x121d,0x121e,0x121f,
+ 0x1220,0x1221,0x1222,0x1223,0x1224,0x1225,0x1226,0x1227,
+ 0x1228,0x1229,0x122a,0x122b,0x122c,0x122d,0x122e,0x122f,
+ 0x1230,0x1231,0x1232,0x1233,0x1234,0x1235,0x1236,0x1237,
+ 0x1238,0x1239,0x123a,0x123b,0x123c,0x123d,0x123e,0x123f,
+ 0x1240,0x1241,0x1242,0x1243,0x1244,0x1245,0x1246,0x1247,
+ 0x1248,0x1249,0x124a,0x124b,0x124c,0x124d,0x124e,0x124f,
+ 0x1250,0x1251,0x1252,0x1253,0x1254,0x1255,0x1256,0x1257,
+ 0x1258,0x1259,0x125a,0x125b,0x125c,0x125d,0x125e,0x125f,
+ 0x1260,0x1261,0x1262,0x1263,0x1264,0x1265,0x1266,0x1267,
+ 0x1268,0x1269,0x126a,0x126b,0x126c,0x126d,0x126e,0x126f,
+ 0x1270,0x1271,0x1272,0x1273,0x1274,0x1275,0x1276,0x1277,
+ 0x1278,0x1279,0x127a,0x127b,0x127c,0x127d,0x127e,0x127f,
+ 0x1280,0x1281,0x1282,0x1283,0x1284,0x1285,0x1286,0x1287,
+ 0x1288,0x1289,0x128a,0x128b,0x128c,0x128d,0x128e,0x128f,
+ 0x1290,0x1291,0x1292,0x1293,0x1294,0x1295,0x1296,0x1297,
+ 0x1298,0x1299,0x129a,0x129b,0x129c,0x129d,0x129e,0x129f,
+ 0x12a0,0x12a1,0x12a2,0x12a3,0x12a4,0x12a5,0x12a6,0x12a7,
+ 0x12a8,0x12a9,0x12aa,0x12ab,0x12ac,0x12ad,0x12ae,0x12af,
+ 0x12b0,0x12b1,0x12b2,0x12b3,0x12b4,0x12b5,0x12b6,0x12b7,
+ 0x12b8,0x12b9,0x12ba,0x12bb,0x12bc,0x12bd,0x12be,0x12bf,
+ 0x12c0,0x12c1,0x12c2,0x12c3,0x12c4,0x12c5,0x12c6,0x12c7,
+ 0x12c8,0x12c9,0x12ca,0x12cb,0x12cc,0x12cd,0x12ce,0x12cf,
+ 0x12d0,0x12d1,0x12d2,0x12d3,0x12d4,0x12d5,0x12d6,0x12d7,
+ 0x12d8,0x12d9,0x12da,0x12db,0x12dc,0x12dd,0x12de,0x12df,
+ 0x12e0,0x12e1,0x12e2,0x12e3,0x12e4,0x12e5,0x12e6,0x12e7,
+ 0x12e8,0x12e9,0x12ea,0x12eb,0x12ec,0x12ed,0x12ee,0x12ef,
+ 0x12f0,0x12f1,0x12f2,0x12f3,0x12f4,0x12f5,0x12f6,0x12f7,
+ 0x12f8,0x12f9,0x12fa,0x12fb,0x12fc,0x12fd,0x12fe,0x12ff,
+ 0x1300,0x1301,0x1302,0x1303,0x1304,0x1305,0x1306,0x1307,
+ 0x1308,0x1309,0x130a,0x130b,0x130c,0x130d,0x130e,0x130f,
+ 0x1310,0x1311,0x1312,0x1313,0x1314,0x1315,0x1316,0x1317,
+ 0x1318,0x1319,0x131a,0x131b,0x131c,0x131d,0x131e,0x131f,
+ 0x1320,0x1321,0x1322,0x1323,0x1324,0x1325,0x1326,0x1327,
+ 0x1328,0x1329,0x132a,0x132b,0x132c,0x132d,0x132e,0x132f,
+ 0x1330,0x1331,0x1332,0x1333,0x1334,0x1335,0x1336,0x1337,
+ 0x1338,0x1339,0x133a,0x133b,0x133c,0x133d,0x133e,0x133f,
+ 0x1340,0x1341,0x1342,0x1343,0x1344,0x1345,0x1346,0x1347,
+ 0x1348,0x1349,0x134a,0x134b,0x134c,0x134d,0x134e,0x134f,
+ 0x1350,0x1351,0x1352,0x1353,0x1354,0x1355,0x1356,0x1357,
+ 0x1358,0x1359,0x135a,0x135b,0x135c,0x135d,0x135e,0x135f,
+ 0x1360,0x1361,0x1362,0x1363,0x1364,0x1365,0x1366,0x1367,
+ 0x1368,0x1369,0x136a,0x136b,0x136c,0x136d,0x136e,0x136f,
+ 0x1370,0x1371,0x1372,0x1373,0x1374,0x1375,0x1376,0x1377,
+ 0x1378,0x1379,0x137a,0x137b,0x137c,0x137d,0x137e,0x137f,
+ 0x1380,0x1381,0x1382,0x1383,0x1384,0x1385,0x1386,0x1387,
+ 0x1388,0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,
+ 0x1390,0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,
+ 0x1398,0x1399,0x139a,0x139b,0x139c,0x139d,0x139e,0x139f,
+ 0x13a0,0x13a1,0x13a2,0x13a3,0x13a4,0x13a5,0x13a6,0x13a7,
+ 0x13a8,0x13a9,0x13aa,0x13ab,0x13ac,0x13ad,0x13ae,0x13af,
+ 0x13b0,0x13b1,0x13b2,0x13b3,0x13b4,0x13b5,0x13b6,0x13b7,
+ 0x13b8,0x13b9,0x13ba,0x13bb,0x13bc,0x13bd,0x13be,0x13bf,
+ 0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,0x13c5,0x13c6,0x13c7,
+ 0x13c8,0x13c9,0x13ca,0x13cb,0x13cc,0x13cd,0x13ce,0x13cf,
+ 0x13d0,0x13d1,0x13d2,0x13d3,0x13d4,0x13d5,0x13d6,0x13d7,
+ 0x13d8,0x13d9,0x13da,0x13db,0x13dc,0x13dd,0x13de,0x13df,
+ 0x13e0,0x13e1,0x13e2,0x13e3,0x13e4,0x13e5,0x13e6,0x13e7,
+ 0x13e8,0x13e9,0x13ea,0x13eb,0x13ec,0x13ed,0x13ee,0x13ef,
+ 0x13f0,0x13f1,0x13f2,0x13f3,0x13f4,0x13f5,0x13f6,0x13f7,
+ 0x13f8,0x13f9,0x13fa,0x13fb,0x13fc,0x13fd,0x13fe,0x13ff,
+ 0x1400,0x1401,0x1402,0x1403,0x1404,0x1405,0x1406,0x1407,
+ 0x1408,0x1409,0x140a,0x140b,0x140c,0x140d,0x140e,0x140f,
+ 0x1410,0x1411,0x1412,0x1413,0x1414,0x1415,0x1416,0x1417,
+ 0x1418,0x1419,0x141a,0x141b,0x141c,0x141d,0x141e,0x141f,
+ 0x1420,0x1421,0x1422,0x1423,0x1424,0x1425,0x1426,0x1427,
+ 0x1428,0x1429,0x142a,0x142b,0x142c,0x142d,0x142e,0x142f,
+ 0x1430,0x1431,0x1432,0x1433,0x1434,0x1435,0x1436,0x1437,
+ 0x1438,0x1439,0x143a,0x143b,0x143c,0x143d,0x143e,0x143f,
+ 0x1440,0x1441,0x1442,0x1443,0x1444,0x1445,0x1446,0x1447,
+ 0x1448,0x1449,0x144a,0x144b,0x144c,0x144d,0x144e,0x144f,
+ 0x1450,0x1451,0x1452,0x1453,0x1454,0x1455,0x1456,0x1457,
+ 0x1458,0x1459,0x145a,0x145b,0x145c,0x145d,0x145e,0x145f,
+ 0x1460,0x1461,0x1462,0x1463,0x1464,0x1465,0x1466,0x1467,
+ 0x1468,0x1469,0x146a,0x146b,0x146c,0x146d,0x146e,0x146f,
+ 0x1470,0x1471,0x1472,0x1473,0x1474,0x1475,0x1476,0x1477,
+ 0x1478,0x1479,0x147a,0x147b,0x147c,0x147d,0x147e,0x147f,
+ 0x1480,0x1481,0x1482,0x1483,0x1484,0x1485,0x1486,0x1487,
+ 0x1488,0x1489,0x148a,0x148b,0x148c,0x148d,0x148e,0x148f,
+ 0x1490,0x1491,0x1492,0x1493,0x1494,0x1495,0x1496,0x1497,
+ 0x1498,0x1499,0x149a,0x149b,0x149c,0x149d,0x149e,0x149f,
+ 0x14a0,0x14a1,0x14a2,0x14a3,0x14a4,0x14a5,0x14a6,0x14a7,
+ 0x14a8,0x14a9,0x14aa,0x14ab,0x14ac,0x14ad,0x14ae,0x14af,
+ 0x14b0,0x14b1,0x14b2,0x14b3,0x14b4,0x14b5,0x14b6,0x14b7,
+ 0x14b8,0x14b9,0x14ba,0x14bb,0x14bc,0x14bd,0x14be,0x14bf,
+ 0x14c0,0x14c1,0x14c2,0x14c3,0x14c4,0x14c5,0x14c6,0x14c7,
+ 0x14c8,0x14c9,0x14ca,0x14cb,0x14cc,0x14cd,0x14ce,0x14cf,
+ 0x14d0,0x14d1,0x14d2,0x14d3,0x14d4,0x14d5,0x14d6,0x14d7,
+ 0x14d8,0x14d9,0x14da,0x14db,0x14dc,0x14dd,0x14de,0x14df,
+ 0x14e0,0x14e1,0x14e2,0x14e3,0x14e4,0x14e5,0x14e6,0x14e7,
+ 0x14e8,0x14e9,0x14ea,0x14eb,0x14ec,0x14ed,0x14ee,0x14ef,
+ 0x14f0,0x14f1,0x14f2,0x14f3,0x14f4,0x14f5,0x14f6,0x14f7,
+ 0x14f8,0x14f9,0x14fa,0x14fb,0x14fc,0x14fd,0x14fe,0x14ff,
+ 0x1500,0x1501,0x1502,0x1503,0x1504,0x1505,0x1506,0x1507,
+ 0x1508,0x1509,0x150a,0x150b,0x150c,0x150d,0x150e,0x150f,
+ 0x1510,0x1511,0x1512,0x1513,0x1514,0x1515,0x1516,0x1517,
+ 0x1518,0x1519,0x151a,0x151b,0x151c,0x151d,0x151e,0x151f,
+ 0x1520,0x1521,0x1522,0x1523,0x1524,0x1525,0x1526,0x1527,
+ 0x1528,0x1529,0x152a,0x152b,0x152c,0x152d,0x152e,0x152f,
+ 0x1530,0x1531,0x1532,0x1533,0x1534,0x1535,0x1536,0x1537,
+ 0x1538,0x1539,0x153a,0x153b,0x153c,0x153d,0x153e,0x153f,
+ 0x1540,0x1541,0x1542,0x1543,0x1544,0x1545,0x1546,0x1547,
+ 0x1548,0x1549,0x154a,0x154b,0x154c,0x154d,0x154e,0x154f,
+ 0x1550,0x1551,0x1552,0x1553,0x1554,0x1555,0x1556,0x1557,
+ 0x1558,0x1559,0x155a,0x155b,0x155c,0x155d,0x155e,0x155f,
+ 0x1560,0x1561,0x1562,0x1563,0x1564,0x1565,0x1566,0x1567,
+ 0x1568,0x1569,0x156a,0x156b,0x156c,0x156d,0x156e,0x156f,
+ 0x1570,0x1571,0x1572,0x1573,0x1574,0x1575,0x1576,0x1577,
+ 0x1578,0x1579,0x157a,0x157b,0x157c,0x157d,0x157e,0x157f,
+ 0x1580,0x1581,0x1582,0x1583,0x1584,0x1585,0x1586,0x1587,
+ 0x1588,0x1589,0x158a,0x158b,0x158c,0x158d,0x158e,0x158f,
+ 0x1590,0x1591,0x1592,0x1593,0x1594,0x1595,0x1596,0x1597,
+ 0x1598,0x1599,0x159a,0x159b,0x159c,0x159d,0x159e,0x159f,
+ 0x15a0,0x15a1,0x15a2,0x15a3,0x15a4,0x15a5,0x15a6,0x15a7,
+ 0x15a8,0x15a9,0x15aa,0x15ab,0x15ac,0x15ad,0x15ae,0x15af,
+ 0x15b0,0x15b1,0x15b2,0x15b3,0x15b4,0x15b5,0x15b6,0x15b7,
+ 0x15b8,0x15b9,0x15ba,0x15bb,0x15bc,0x15bd,0x15be,0x15bf,
+ 0x15c0,0x15c1,0x15c2,0x15c3,0x15c4,0x15c5,0x15c6,0x15c7,
+ 0x15c8,0x15c9,0x15ca,0x15cb,0x15cc,0x15cd,0x15ce,0x15cf,
+ 0x15d0,0x15d1,0x15d2,0x15d3,0x15d4,0x15d5,0x15d6,0x15d7,
+ 0x15d8,0x15d9,0x15da,0x15db,0x15dc,0x15dd,0x15de,0x15df,
+ 0x15e0,0x15e1,0x15e2,0x15e3,0x15e4,0x15e5,0x15e6,0x15e7,
+ 0x15e8,0x15e9,0x15ea,0x15eb,0x15ec,0x15ed,0x15ee,0x15ef,
+ 0x15f0,0x15f1,0x15f2,0x15f3,0x15f4,0x15f5,0x15f6,0x15f7,
+ 0x15f8,0x15f9,0x15fa,0x15fb,0x15fc,0x15fd,0x15fe,0x15ff,
+ 0x1600,0x1601,0x1602,0x1603,0x1604,0x1605,0x1606,0x1607,
+ 0x1608,0x1609,0x160a,0x160b,0x160c,0x160d,0x160e,0x160f,
+ 0x1610,0x1611,0x1612,0x1613,0x1614,0x1615,0x1616,0x1617,
+ 0x1618,0x1619,0x161a,0x161b,0x161c,0x161d,0x161e,0x161f,
+ 0x1620,0x1621,0x1622,0x1623,0x1624,0x1625,0x1626,0x1627,
+ 0x1628,0x1629,0x162a,0x162b,0x162c,0x162d,0x162e,0x162f,
+ 0x1630,0x1631,0x1632,0x1633,0x1634,0x1635,0x1636,0x1637,
+ 0x1638,0x1639,0x163a,0x163b,0x163c,0x163d,0x163e,0x163f,
+ 0x1640,0x1641,0x1642,0x1643,0x1644,0x1645,0x1646,0x1647,
+ 0x1648,0x1649,0x164a,0x164b,0x164c,0x164d,0x164e,0x164f,
+ 0x1650,0x1651,0x1652,0x1653,0x1654,0x1655,0x1656,0x1657,
+ 0x1658,0x1659,0x165a,0x165b,0x165c,0x165d,0x165e,0x165f,
+ 0x1660,0x1661,0x1662,0x1663,0x1664,0x1665,0x1666,0x1667,
+ 0x1668,0x1669,0x166a,0x166b,0x166c,0x166d,0x166e,0x166f,
+ 0x1670,0x1671,0x1672,0x1673,0x1674,0x1675,0x1676,0x1677,
+ 0x1678,0x1679,0x167a,0x167b,0x167c,0x167d,0x167e,0x167f,
+ 0x1680,0x1681,0x1682,0x1683,0x1684,0x1685,0x1686,0x1687,
+ 0x1688,0x1689,0x168a,0x168b,0x168c,0x168d,0x168e,0x168f,
+ 0x1690,0x1691,0x1692,0x1693,0x1694,0x1695,0x1696,0x1697,
+ 0x1698,0x1699,0x169a,0x169b,0x169c,0x169d,0x169e,0x169f,
+ 0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0x16a6,0x16a7,
+ 0x16a8,0x16a9,0x16aa,0x16ab,0x16ac,0x16ad,0x16ae,0x16af,
+ 0x16b0,0x16b1,0x16b2,0x16b3,0x16b4,0x16b5,0x16b6,0x16b7,
+ 0x16b8,0x16b9,0x16ba,0x16bb,0x16bc,0x16bd,0x16be,0x16bf,
+ 0x16c0,0x16c1,0x16c2,0x16c3,0x16c4,0x16c5,0x16c6,0x16c7,
+ 0x16c8,0x16c9,0x16ca,0x16cb,0x16cc,0x16cd,0x16ce,0x16cf,
+ 0x16d0,0x16d1,0x16d2,0x16d3,0x16d4,0x16d5,0x16d6,0x16d7,
+ 0x16d8,0x16d9,0x16da,0x16db,0x16dc,0x16dd,0x16de,0x16df,
+ 0x16e0,0x16e1,0x16e2,0x16e3,0x16e4,0x16e5,0x16e6,0x16e7,
+ 0x16e8,0x16e9,0x16ea,0x16eb,0x16ec,0x16ed,0x16ee,0x16ef,
+ 0x16f0,0x16f1,0x16f2,0x16f3,0x16f4,0x16f5,0x16f6,0x16f7,
+ 0x16f8,0x16f9,0x16fa,0x16fb,0x16fc,0x16fd,0x16fe,0x16ff,
+ 0x1700,0x1701,0x1702,0x1703,0x1704,0x1705,0x1706,0x1707,
+ 0x1708,0x1709,0x170a,0x170b,0x170c,0x170d,0x170e,0x170f,
+ 0x1710,0x1711,0x1712,0x1713,0x1714,0x1715,0x1716,0x1717,
+ 0x1718,0x1719,0x171a,0x171b,0x171c,0x171d,0x171e,0x171f,
+ 0x1720,0x1721,0x1722,0x1723,0x1724,0x1725,0x1726,0x1727,
+ 0x1728,0x1729,0x172a,0x172b,0x172c,0x172d,0x172e,0x172f,
+ 0x1730,0x1731,0x1732,0x1733,0x1734,0x1735,0x1736,0x1737,
+ 0x1738,0x1739,0x173a,0x173b,0x173c,0x173d,0x173e,0x173f,
+ 0x1740,0x1741,0x1742,0x1743,0x1744,0x1745,0x1746,0x1747,
+ 0x1748,0x1749,0x174a,0x174b,0x174c,0x174d,0x174e,0x174f,
+ 0x1750,0x1751,0x1752,0x1753,0x1754,0x1755,0x1756,0x1757,
+ 0x1758,0x1759,0x175a,0x175b,0x175c,0x175d,0x175e,0x175f,
+ 0x1760,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,
+ 0x1768,0x1769,0x176a,0x176b,0x176c,0x176d,0x176e,0x176f,
+ 0x1770,0x1771,0x1772,0x1773,0x1774,0x1775,0x1776,0x1777,
+ 0x1778,0x1779,0x177a,0x177b,0x177c,0x177d,0x177e,0x177f,
+ 0x1780,0x1781,0x1782,0x1783,0x1784,0x1785,0x1786,0x1787,
+ 0x1788,0x1789,0x178a,0x178b,0x178c,0x178d,0x178e,0x178f,
+ 0x1790,0x1791,0x1792,0x1793,0x1794,0x1795,0x1796,0x1797,
+ 0x1798,0x1799,0x179a,0x179b,0x179c,0x179d,0x179e,0x179f,
+ 0x17a0,0x17a1,0x17a2,0x17a3,0x17a4,0x17a5,0x17a6,0x17a7,
+ 0x17a8,0x17a9,0x17aa,0x17ab,0x17ac,0x17ad,0x17ae,0x17af,
+ 0x17b0,0x17b1,0x17b2,0x17b3,0x17b4,0x17b5,0x17b6,0x17b7,
+ 0x17b8,0x17b9,0x17ba,0x17bb,0x17bc,0x17bd,0x17be,0x17bf,
+ 0x17c0,0x17c1,0x17c2,0x17c3,0x17c4,0x17c5,0x17c6,0x17c7,
+ 0x17c8,0x17c9,0x17ca,0x17cb,0x17cc,0x17cd,0x17ce,0x17cf,
+ 0x17d0,0x17d1,0x17d2,0x17d3,0x17d4,0x17d5,0x17d6,0x17d7,
+ 0x17d8,0x17d9,0x17da,0x17db,0x17dc,0x17dd,0x17de,0x17df,
+ 0x17e0,0x17e1,0x17e2,0x17e3,0x17e4,0x17e5,0x17e6,0x17e7,
+ 0x17e8,0x17e9,0x17ea,0x17eb,0x17ec,0x17ed,0x17ee,0x17ef,
+ 0x17f0,0x17f1,0x17f2,0x17f3,0x17f4,0x17f5,0x17f6,0x17f7,
+ 0x17f8,0x17f9,0x17fa,0x17fb,0x17fc,0x17fd,0x17fe,0x17ff,
+ 0x1800,0x1801,0x1802,0x1803,0x1804,0x1805,0x1806,0x1807,
+ 0x1808,0x1809,0x180a,0x180b,0x180c,0x180d,0x180e,0x180f,
+ 0x1810,0x1811,0x1812,0x1813,0x1814,0x1815,0x1816,0x1817,
+ 0x1818,0x1819,0x181a,0x181b,0x181c,0x181d,0x181e,0x181f,
+ 0x1820,0x1821,0x1822,0x1823,0x1824,0x1825,0x1826,0x1827,
+ 0x1828,0x1829,0x182a,0x182b,0x182c,0x182d,0x182e,0x182f,
+ 0x1830,0x1831,0x1832,0x1833,0x1834,0x1835,0x1836,0x1837,
+ 0x1838,0x1839,0x183a,0x183b,0x183c,0x183d,0x183e,0x183f,
+ 0x1840,0x1841,0x1842,0x1843,0x1844,0x1845,0x1846,0x1847,
+ 0x1848,0x1849,0x184a,0x184b,0x184c,0x184d,0x184e,0x184f,
+ 0x1850,0x1851,0x1852,0x1853,0x1854,0x1855,0x1856,0x1857,
+ 0x1858,0x1859,0x185a,0x185b,0x185c,0x185d,0x185e,0x185f,
+ 0x1860,0x1861,0x1862,0x1863,0x1864,0x1865,0x1866,0x1867,
+ 0x1868,0x1869,0x186a,0x186b,0x186c,0x186d,0x186e,0x186f,
+ 0x1870,0x1871,0x1872,0x1873,0x1874,0x1875,0x1876,0x1877,
+ 0x1878,0x1879,0x187a,0x187b,0x187c,0x187d,0x187e,0x187f,
+ 0x1880,0x1881,0x1882,0x1883,0x1884,0x1885,0x1886,0x1887,
+ 0x1888,0x1889,0x188a,0x188b,0x188c,0x188d,0x188e,0x188f,
+ 0x1890,0x1891,0x1892,0x1893,0x1894,0x1895,0x1896,0x1897,
+ 0x1898,0x1899,0x189a,0x189b,0x189c,0x189d,0x189e,0x189f,
+ 0x18a0,0x18a1,0x18a2,0x18a3,0x18a4,0x18a5,0x18a6,0x18a7,
+ 0x18a8,0x18a9,0x18aa,0x18ab,0x18ac,0x18ad,0x18ae,0x18af,
+ 0x18b0,0x18b1,0x18b2,0x18b3,0x18b4,0x18b5,0x18b6,0x18b7,
+ 0x18b8,0x18b9,0x18ba,0x18bb,0x18bc,0x18bd,0x18be,0x18bf,
+ 0x18c0,0x18c1,0x18c2,0x18c3,0x18c4,0x18c5,0x18c6,0x18c7,
+ 0x18c8,0x18c9,0x18ca,0x18cb,0x18cc,0x18cd,0x18ce,0x18cf,
+ 0x18d0,0x18d1,0x18d2,0x18d3,0x18d4,0x18d5,0x18d6,0x18d7,
+ 0x18d8,0x18d9,0x18da,0x18db,0x18dc,0x18dd,0x18de,0x18df,
+ 0x18e0,0x18e1,0x18e2,0x18e3,0x18e4,0x18e5,0x18e6,0x18e7,
+ 0x18e8,0x18e9,0x18ea,0x18eb,0x18ec,0x18ed,0x18ee,0x18ef,
+ 0x18f0,0x18f1,0x18f2,0x18f3,0x18f4,0x18f5,0x18f6,0x18f7,
+ 0x18f8,0x18f9,0x18fa,0x18fb,0x18fc,0x18fd,0x18fe,0x18ff,
+ 0x1900,0x1901,0x1902,0x1903,0x1904,0x1905,0x1906,0x1907,
+ 0x1908,0x1909,0x190a,0x190b,0x190c,0x190d,0x190e,0x190f,
+ 0x1910,0x1911,0x1912,0x1913,0x1914,0x1915,0x1916,0x1917,
+ 0x1918,0x1919,0x191a,0x191b,0x191c,0x191d,0x191e,0x191f,
+ 0x1920,0x1921,0x1922,0x1923,0x1924,0x1925,0x1926,0x1927,
+ 0x1928,0x1929,0x192a,0x192b,0x192c,0x192d,0x192e,0x192f,
+ 0x1930,0x1931,0x1932,0x1933,0x1934,0x1935,0x1936,0x1937,
+ 0x1938,0x1939,0x193a,0x193b,0x193c,0x193d,0x193e,0x193f,
+ 0x1940,0x1941,0x1942,0x1943,0x1944,0x1945,0x1946,0x1947,
+ 0x1948,0x1949,0x194a,0x194b,0x194c,0x194d,0x194e,0x194f,
+ 0x1950,0x1951,0x1952,0x1953,0x1954,0x1955,0x1956,0x1957,
+ 0x1958,0x1959,0x195a,0x195b,0x195c,0x195d,0x195e,0x195f,
+ 0x1960,0x1961,0x1962,0x1963,0x1964,0x1965,0x1966,0x1967,
+ 0x1968,0x1969,0x196a,0x196b,0x196c,0x196d,0x196e,0x196f,
+ 0x1970,0x1971,0x1972,0x1973,0x1974,0x1975,0x1976,0x1977,
+ 0x1978,0x1979,0x197a,0x197b,0x197c,0x197d,0x197e,0x197f,
+ 0x1980,0x1981,0x1982,0x1983,0x1984,0x1985,0x1986,0x1987,
+ 0x1988,0x1989,0x198a,0x198b,0x198c,0x198d,0x198e,0x198f,
+ 0x1990,0x1991,0x1992,0x1993,0x1994,0x1995,0x1996,0x1997,
+ 0x1998,0x1999,0x199a,0x199b,0x199c,0x199d,0x199e,0x199f,
+ 0x19a0,0x19a1,0x19a2,0x19a3,0x19a4,0x19a5,0x19a6,0x19a7,
+ 0x19a8,0x19a9,0x19aa,0x19ab,0x19ac,0x19ad,0x19ae,0x19af,
+ 0x19b0,0x19b1,0x19b2,0x19b3,0x19b4,0x19b5,0x19b6,0x19b7,
+ 0x19b8,0x19b9,0x19ba,0x19bb,0x19bc,0x19bd,0x19be,0x19bf,
+ 0x19c0,0x19c1,0x19c2,0x19c3,0x19c4,0x19c5,0x19c6,0x19c7,
+ 0x19c8,0x19c9,0x19ca,0x19cb,0x19cc,0x19cd,0x19ce,0x19cf,
+ 0x19d0,0x19d1,0x19d2,0x19d3,0x19d4,0x19d5,0x19d6,0x19d7,
+ 0x19d8,0x19d9,0x19da,0x19db,0x19dc,0x19dd,0x19de,0x19df,
+ 0x19e0,0x19e1,0x19e2,0x19e3,0x19e4,0x19e5,0x19e6,0x19e7,
+ 0x19e8,0x19e9,0x19ea,0x19eb,0x19ec,0x19ed,0x19ee,0x19ef,
+ 0x19f0,0x19f1,0x19f2,0x19f3,0x19f4,0x19f5,0x19f6,0x19f7,
+ 0x19f8,0x19f9,0x19fa,0x19fb,0x19fc,0x19fd,0x19fe,0x19ff,
+ 0x1a00,0x1a01,0x1a02,0x1a03,0x1a04,0x1a05,0x1a06,0x1a07,
+ 0x1a08,0x1a09,0x1a0a,0x1a0b,0x1a0c,0x1a0d,0x1a0e,0x1a0f,
+ 0x1a10,0x1a11,0x1a12,0x1a13,0x1a14,0x1a15,0x1a16,0x1a17,
+ 0x1a18,0x1a19,0x1a1a,0x1a1b,0x1a1c,0x1a1d,0x1a1e,0x1a1f,
+ 0x1a20,0x1a21,0x1a22,0x1a23,0x1a24,0x1a25,0x1a26,0x1a27,
+ 0x1a28,0x1a29,0x1a2a,0x1a2b,0x1a2c,0x1a2d,0x1a2e,0x1a2f,
+ 0x1a30,0x1a31,0x1a32,0x1a33,0x1a34,0x1a35,0x1a36,0x1a37,
+ 0x1a38,0x1a39,0x1a3a,0x1a3b,0x1a3c,0x1a3d,0x1a3e,0x1a3f,
+ 0x1a40,0x1a41,0x1a42,0x1a43,0x1a44,0x1a45,0x1a46,0x1a47,
+ 0x1a48,0x1a49,0x1a4a,0x1a4b,0x1a4c,0x1a4d,0x1a4e,0x1a4f,
+ 0x1a50,0x1a51,0x1a52,0x1a53,0x1a54,0x1a55,0x1a56,0x1a57,
+ 0x1a58,0x1a59,0x1a5a,0x1a5b,0x1a5c,0x1a5d,0x1a5e,0x1a5f,
+ 0x1a60,0x1a61,0x1a62,0x1a63,0x1a64,0x1a65,0x1a66,0x1a67,
+ 0x1a68,0x1a69,0x1a6a,0x1a6b,0x1a6c,0x1a6d,0x1a6e,0x1a6f,
+ 0x1a70,0x1a71,0x1a72,0x1a73,0x1a74,0x1a75,0x1a76,0x1a77,
+ 0x1a78,0x1a79,0x1a7a,0x1a7b,0x1a7c,0x1a7d,0x1a7e,0x1a7f,
+ 0x1a80,0x1a81,0x1a82,0x1a83,0x1a84,0x1a85,0x1a86,0x1a87,
+ 0x1a88,0x1a89,0x1a8a,0x1a8b,0x1a8c,0x1a8d,0x1a8e,0x1a8f,
+ 0x1a90,0x1a91,0x1a92,0x1a93,0x1a94,0x1a95,0x1a96,0x1a97,
+ 0x1a98,0x1a99,0x1a9a,0x1a9b,0x1a9c,0x1a9d,0x1a9e,0x1a9f,
+ 0x1aa0,0x1aa1,0x1aa2,0x1aa3,0x1aa4,0x1aa5,0x1aa6,0x1aa7,
+ 0x1aa8,0x1aa9,0x1aaa,0x1aab,0x1aac,0x1aad,0x1aae,0x1aaf,
+ 0x1ab0,0x1ab1,0x1ab2,0x1ab3,0x1ab4,0x1ab5,0x1ab6,0x1ab7,
+ 0x1ab8,0x1ab9,0x1aba,0x1abb,0x1abc,0x1abd,0x1abe,0x1abf,
+ 0x1ac0,0x1ac1,0x1ac2,0x1ac3,0x1ac4,0x1ac5,0x1ac6,0x1ac7,
+ 0x1ac8,0x1ac9,0x1aca,0x1acb,0x1acc,0x1acd,0x1ace,0x1acf,
+ 0x1ad0,0x1ad1,0x1ad2,0x1ad3,0x1ad4,0x1ad5,0x1ad6,0x1ad7,
+ 0x1ad8,0x1ad9,0x1ada,0x1adb,0x1adc,0x1add,0x1ade,0x1adf,
+ 0x1ae0,0x1ae1,0x1ae2,0x1ae3,0x1ae4,0x1ae5,0x1ae6,0x1ae7,
+ 0x1ae8,0x1ae9,0x1aea,0x1aeb,0x1aec,0x1aed,0x1aee,0x1aef,
+ 0x1af0,0x1af1,0x1af2,0x1af3,0x1af4,0x1af5,0x1af6,0x1af7,
+ 0x1af8,0x1af9,0x1afa,0x1afb,0x1afc,0x1afd,0x1afe,0x1aff,
+ 0x1b00,0x1b01,0x1b02,0x1b03,0x1b04,0x1b05,0x1b06,0x1b07,
+ 0x1b08,0x1b09,0x1b0a,0x1b0b,0x1b0c,0x1b0d,0x1b0e,0x1b0f,
+ 0x1b10,0x1b11,0x1b12,0x1b13,0x1b14,0x1b15,0x1b16,0x1b17,
+ 0x1b18,0x1b19,0x1b1a,0x1b1b,0x1b1c,0x1b1d,0x1b1e,0x1b1f,
+ 0x1b20,0x1b21,0x1b22,0x1b23,0x1b24,0x1b25,0x1b26,0x1b27,
+ 0x1b28,0x1b29,0x1b2a,0x1b2b,0x1b2c,0x1b2d,0x1b2e,0x1b2f,
+ 0x1b30,0x1b31,0x1b32,0x1b33,0x1b34,0x1b35,0x1b36,0x1b37,
+ 0x1b38,0x1b39,0x1b3a,0x1b3b,0x1b3c,0x1b3d,0x1b3e,0x1b3f,
+ 0x1b40,0x1b41,0x1b42,0x1b43,0x1b44,0x1b45,0x1b46,0x1b47,
+ 0x1b48,0x1b49,0x1b4a,0x1b4b,0x1b4c,0x1b4d,0x1b4e,0x1b4f,
+ 0x1b50,0x1b51,0x1b52,0x1b53,0x1b54,0x1b55,0x1b56,0x1b57,
+ 0x1b58,0x1b59,0x1b5a,0x1b5b,0x1b5c,0x1b5d,0x1b5e,0x1b5f,
+ 0x1b60,0x1b61,0x1b62,0x1b63,0x1b64,0x1b65,0x1b66,0x1b67,
+ 0x1b68,0x1b69,0x1b6a,0x1b6b,0x1b6c,0x1b6d,0x1b6e,0x1b6f,
+ 0x1b70,0x1b71,0x1b72,0x1b73,0x1b74,0x1b75,0x1b76,0x1b77,
+ 0x1b78,0x1b79,0x1b7a,0x1b7b,0x1b7c,0x1b7d,0x1b7e,0x1b7f,
+ 0x1b80,0x1b81,0x1b82,0x1b83,0x1b84,0x1b85,0x1b86,0x1b87,
+ 0x1b88,0x1b89,0x1b8a,0x1b8b,0x1b8c,0x1b8d,0x1b8e,0x1b8f,
+ 0x1b90,0x1b91,0x1b92,0x1b93,0x1b94,0x1b95,0x1b96,0x1b97,
+ 0x1b98,0x1b99,0x1b9a,0x1b9b,0x1b9c,0x1b9d,0x1b9e,0x1b9f,
+ 0x1ba0,0x1ba1,0x1ba2,0x1ba3,0x1ba4,0x1ba5,0x1ba6,0x1ba7,
+ 0x1ba8,0x1ba9,0x1baa,0x1bab,0x1bac,0x1bad,0x1bae,0x1baf,
+ 0x1bb0,0x1bb1,0x1bb2,0x1bb3,0x1bb4,0x1bb5,0x1bb6,0x1bb7,
+ 0x1bb8,0x1bb9,0x1bba,0x1bbb,0x1bbc,0x1bbd,0x1bbe,0x1bbf,
+ 0x1bc0,0x1bc1,0x1bc2,0x1bc3,0x1bc4,0x1bc5,0x1bc6,0x1bc7,
+ 0x1bc8,0x1bc9,0x1bca,0x1bcb,0x1bcc,0x1bcd,0x1bce,0x1bcf,
+ 0x1bd0,0x1bd1,0x1bd2,0x1bd3,0x1bd4,0x1bd5,0x1bd6,0x1bd7,
+ 0x1bd8,0x1bd9,0x1bda,0x1bdb,0x1bdc,0x1bdd,0x1bde,0x1bdf,
+ 0x1be0,0x1be1,0x1be2,0x1be3,0x1be4,0x1be5,0x1be6,0x1be7,
+ 0x1be8,0x1be9,0x1bea,0x1beb,0x1bec,0x1bed,0x1bee,0x1bef,
+ 0x1bf0,0x1bf1,0x1bf2,0x1bf3,0x1bf4,0x1bf5,0x1bf6,0x1bf7,
+ 0x1bf8,0x1bf9,0x1bfa,0x1bfb,0x1bfc,0x1bfd,0x1bfe,0x1bff,
+ 0x1c00,0x1c01,0x1c02,0x1c03,0x1c04,0x1c05,0x1c06,0x1c07,
+ 0x1c08,0x1c09,0x1c0a,0x1c0b,0x1c0c,0x1c0d,0x1c0e,0x1c0f,
+ 0x1c10,0x1c11,0x1c12,0x1c13,0x1c14,0x1c15,0x1c16,0x1c17,
+ 0x1c18,0x1c19,0x1c1a,0x1c1b,0x1c1c,0x1c1d,0x1c1e,0x1c1f,
+ 0x1c20,0x1c21,0x1c22,0x1c23,0x1c24,0x1c25,0x1c26,0x1c27,
+ 0x1c28,0x1c29,0x1c2a,0x1c2b,0x1c2c,0x1c2d,0x1c2e,0x1c2f,
+ 0x1c30,0x1c31,0x1c32,0x1c33,0x1c34,0x1c35,0x1c36,0x1c37,
+ 0x1c38,0x1c39,0x1c3a,0x1c3b,0x1c3c,0x1c3d,0x1c3e,0x1c3f,
+ 0x1c40,0x1c41,0x1c42,0x1c43,0x1c44,0x1c45,0x1c46,0x1c47,
+ 0x1c48,0x1c49,0x1c4a,0x1c4b,0x1c4c,0x1c4d,0x1c4e,0x1c4f,
+ 0x1c50,0x1c51,0x1c52,0x1c53,0x1c54,0x1c55,0x1c56,0x1c57,
+ 0x1c58,0x1c59,0x1c5a,0x1c5b,0x1c5c,0x1c5d,0x1c5e,0x1c5f,
+ 0x1c60,0x1c61,0x1c62,0x1c63,0x1c64,0x1c65,0x1c66,0x1c67,
+ 0x1c68,0x1c69,0x1c6a,0x1c6b,0x1c6c,0x1c6d,0x1c6e,0x1c6f,
+ 0x1c70,0x1c71,0x1c72,0x1c73,0x1c74,0x1c75,0x1c76,0x1c77,
+ 0x1c78,0x1c79,0x1c7a,0x1c7b,0x1c7c,0x1c7d,0x1c7e,0x1c7f,
+ 0x1c80,0x1c81,0x1c82,0x1c83,0x1c84,0x1c85,0x1c86,0x1c87,
+ 0x1c88,0x1c89,0x1c8a,0x1c8b,0x1c8c,0x1c8d,0x1c8e,0x1c8f,
+ 0x1c90,0x1c91,0x1c92,0x1c93,0x1c94,0x1c95,0x1c96,0x1c97,
+ 0x1c98,0x1c99,0x1c9a,0x1c9b,0x1c9c,0x1c9d,0x1c9e,0x1c9f,
+ 0x1ca0,0x1ca1,0x1ca2,0x1ca3,0x1ca4,0x1ca5,0x1ca6,0x1ca7,
+ 0x1ca8,0x1ca9,0x1caa,0x1cab,0x1cac,0x1cad,0x1cae,0x1caf,
+ 0x1cb0,0x1cb1,0x1cb2,0x1cb3,0x1cb4,0x1cb5,0x1cb6,0x1cb7,
+ 0x1cb8,0x1cb9,0x1cba,0x1cbb,0x1cbc,0x1cbd,0x1cbe,0x1cbf,
+ 0x1cc0,0x1cc1,0x1cc2,0x1cc3,0x1cc4,0x1cc5,0x1cc6,0x1cc7,
+ 0x1cc8,0x1cc9,0x1cca,0x1ccb,0x1ccc,0x1ccd,0x1cce,0x1ccf,
+ 0x1cd0,0x1cd1,0x1cd2,0x1cd3,0x1cd4,0x1cd5,0x1cd6,0x1cd7,
+ 0x1cd8,0x1cd9,0x1cda,0x1cdb,0x1cdc,0x1cdd,0x1cde,0x1cdf,
+ 0x1ce0,0x1ce1,0x1ce2,0x1ce3,0x1ce4,0x1ce5,0x1ce6,0x1ce7,
+ 0x1ce8,0x1ce9,0x1cea,0x1ceb,0x1cec,0x1ced,0x1cee,0x1cef,
+ 0x1cf0,0x1cf1,0x1cf2,0x1cf3,0x1cf4,0x1cf5,0x1cf6,0x1cf7,
+ 0x1cf8,0x1cf9,0x1cfa,0x1cfb,0x1cfc,0x1cfd,0x1cfe,0x1cff,
+ 0x1d00,0x1d01,0x1d02,0x1d03,0x1d04,0x1d05,0x1d06,0x1d07,
+ 0x1d08,0x1d09,0x1d0a,0x1d0b,0x1d0c,0x1d0d,0x1d0e,0x1d0f,
+ 0x1d10,0x1d11,0x1d12,0x1d13,0x1d14,0x1d15,0x1d16,0x1d17,
+ 0x1d18,0x1d19,0x1d1a,0x1d1b,0x1d1c,0x1d1d,0x1d1e,0x1d1f,
+ 0x1d20,0x1d21,0x1d22,0x1d23,0x1d24,0x1d25,0x1d26,0x1d27,
+ 0x1d28,0x1d29,0x1d2a,0x1d2b,0x1d2c,0x1d2d,0x1d2e,0x1d2f,
+ 0x1d30,0x1d31,0x1d32,0x1d33,0x1d34,0x1d35,0x1d36,0x1d37,
+ 0x1d38,0x1d39,0x1d3a,0x1d3b,0x1d3c,0x1d3d,0x1d3e,0x1d3f,
+ 0x1d40,0x1d41,0x1d42,0x1d43,0x1d44,0x1d45,0x1d46,0x1d47,
+ 0x1d48,0x1d49,0x1d4a,0x1d4b,0x1d4c,0x1d4d,0x1d4e,0x1d4f,
+ 0x1d50,0x1d51,0x1d52,0x1d53,0x1d54,0x1d55,0x1d56,0x1d57,
+ 0x1d58,0x1d59,0x1d5a,0x1d5b,0x1d5c,0x1d5d,0x1d5e,0x1d5f,
+ 0x1d60,0x1d61,0x1d62,0x1d63,0x1d64,0x1d65,0x1d66,0x1d67,
+ 0x1d68,0x1d69,0x1d6a,0x1d6b,0x1d6c,0x1d6d,0x1d6e,0x1d6f,
+ 0x1d70,0x1d71,0x1d72,0x1d73,0x1d74,0x1d75,0x1d76,0x1d77,
+ 0x1d78,0x1d79,0x1d7a,0x1d7b,0x1d7c,0x2c63,0x1d7e,0x1d7f,
+ 0x1d80,0x1d81,0x1d82,0x1d83,0x1d84,0x1d85,0x1d86,0x1d87,
+ 0x1d88,0x1d89,0x1d8a,0x1d8b,0x1d8c,0x1d8d,0x1d8e,0x1d8f,
+ 0x1d90,0x1d91,0x1d92,0x1d93,0x1d94,0x1d95,0x1d96,0x1d97,
+ 0x1d98,0x1d99,0x1d9a,0x1d9b,0x1d9c,0x1d9d,0x1d9e,0x1d9f,
+ 0x1da0,0x1da1,0x1da2,0x1da3,0x1da4,0x1da5,0x1da6,0x1da7,
+ 0x1da8,0x1da9,0x1daa,0x1dab,0x1dac,0x1dad,0x1dae,0x1daf,
+ 0x1db0,0x1db1,0x1db2,0x1db3,0x1db4,0x1db5,0x1db6,0x1db7,
+ 0x1db8,0x1db9,0x1dba,0x1dbb,0x1dbc,0x1dbd,0x1dbe,0x1dbf,
+ 0x1dc0,0x1dc1,0x1dc2,0x1dc3,0x1dc4,0x1dc5,0x1dc6,0x1dc7,
+ 0x1dc8,0x1dc9,0x1dca,0x1dcb,0x1dcc,0x1dcd,0x1dce,0x1dcf,
+ 0x1dd0,0x1dd1,0x1dd2,0x1dd3,0x1dd4,0x1dd5,0x1dd6,0x1dd7,
+ 0x1dd8,0x1dd9,0x1dda,0x1ddb,0x1ddc,0x1ddd,0x1dde,0x1ddf,
+ 0x1de0,0x1de1,0x1de2,0x1de3,0x1de4,0x1de5,0x1de6,0x1de7,
+ 0x1de8,0x1de9,0x1dea,0x1deb,0x1dec,0x1ded,0x1dee,0x1def,
+ 0x1df0,0x1df1,0x1df2,0x1df3,0x1df4,0x1df5,0x1df6,0x1df7,
+ 0x1df8,0x1df9,0x1dfa,0x1dfb,0x1dfc,0x1dfd,0x1dfe,0x1dff,
+ 0x1e00,0x1e00,0x1e02,0x1e02,0x1e04,0x1e04,0x1e06,0x1e06,
+ 0x1e08,0x1e08,0x1e0a,0x1e0a,0x1e0c,0x1e0c,0x1e0e,0x1e0e,
+ 0x1e10,0x1e10,0x1e12,0x1e12,0x1e14,0x1e14,0x1e16,0x1e16,
+ 0x1e18,0x1e18,0x1e1a,0x1e1a,0x1e1c,0x1e1c,0x1e1e,0x1e1e,
+ 0x1e20,0x1e20,0x1e22,0x1e22,0x1e24,0x1e24,0x1e26,0x1e26,
+ 0x1e28,0x1e28,0x1e2a,0x1e2a,0x1e2c,0x1e2c,0x1e2e,0x1e2e,
+ 0x1e30,0x1e30,0x1e32,0x1e32,0x1e34,0x1e34,0x1e36,0x1e36,
+ 0x1e38,0x1e38,0x1e3a,0x1e3a,0x1e3c,0x1e3c,0x1e3e,0x1e3e,
+ 0x1e40,0x1e40,0x1e42,0x1e42,0x1e44,0x1e44,0x1e46,0x1e46,
+ 0x1e48,0x1e48,0x1e4a,0x1e4a,0x1e4c,0x1e4c,0x1e4e,0x1e4e,
+ 0x1e50,0x1e50,0x1e52,0x1e52,0x1e54,0x1e54,0x1e56,0x1e56,
+ 0x1e58,0x1e58,0x1e5a,0x1e5a,0x1e5c,0x1e5c,0x1e5e,0x1e5e,
+ 0x1e60,0x1e60,0x1e62,0x1e62,0x1e64,0x1e64,0x1e66,0x1e66,
+ 0x1e68,0x1e68,0x1e6a,0x1e6a,0x1e6c,0x1e6c,0x1e6e,0x1e6e,
+ 0x1e70,0x1e70,0x1e72,0x1e72,0x1e74,0x1e74,0x1e76,0x1e76,
+ 0x1e78,0x1e78,0x1e7a,0x1e7a,0x1e7c,0x1e7c,0x1e7e,0x1e7e,
+ 0x1e80,0x1e80,0x1e82,0x1e82,0x1e84,0x1e84,0x1e86,0x1e86,
+ 0x1e88,0x1e88,0x1e8a,0x1e8a,0x1e8c,0x1e8c,0x1e8e,0x1e8e,
+ 0x1e90,0x1e90,0x1e92,0x1e92,0x1e94,0x1e94,0x1e96,0x1e97,
+ 0x1e98,0x1e99,0x1e9a,0x1e60,0x1e9c,0x1e9d,0x1e9e,0x1e9f,
+ 0x1ea0,0x1ea0,0x1ea2,0x1ea2,0x1ea4,0x1ea4,0x1ea6,0x1ea6,
+ 0x1ea8,0x1ea8,0x1eaa,0x1eaa,0x1eac,0x1eac,0x1eae,0x1eae,
+ 0x1eb0,0x1eb0,0x1eb2,0x1eb2,0x1eb4,0x1eb4,0x1eb6,0x1eb6,
+ 0x1eb8,0x1eb8,0x1eba,0x1eba,0x1ebc,0x1ebc,0x1ebe,0x1ebe,
+ 0x1ec0,0x1ec0,0x1ec2,0x1ec2,0x1ec4,0x1ec4,0x1ec6,0x1ec6,
+ 0x1ec8,0x1ec8,0x1eca,0x1eca,0x1ecc,0x1ecc,0x1ece,0x1ece,
+ 0x1ed0,0x1ed0,0x1ed2,0x1ed2,0x1ed4,0x1ed4,0x1ed6,0x1ed6,
+ 0x1ed8,0x1ed8,0x1eda,0x1eda,0x1edc,0x1edc,0x1ede,0x1ede,
+ 0x1ee0,0x1ee0,0x1ee2,0x1ee2,0x1ee4,0x1ee4,0x1ee6,0x1ee6,
+ 0x1ee8,0x1ee8,0x1eea,0x1eea,0x1eec,0x1eec,0x1eee,0x1eee,
+ 0x1ef0,0x1ef0,0x1ef2,0x1ef2,0x1ef4,0x1ef4,0x1ef6,0x1ef6,
+ 0x1ef8,0x1ef8,0x1efa,0x1efb,0x1efc,0x1efd,0x1efe,0x1eff,
+ 0x1f08,0x1f09,0x1f0a,0x1f0b,0x1f0c,0x1f0d,0x1f0e,0x1f0f,
+ 0x1f08,0x1f09,0x1f0a,0x1f0b,0x1f0c,0x1f0d,0x1f0e,0x1f0f,
+ 0x1f18,0x1f19,0x1f1a,0x1f1b,0x1f1c,0x1f1d,0x1f16,0x1f17,
+ 0x1f18,0x1f19,0x1f1a,0x1f1b,0x1f1c,0x1f1d,0x1f1e,0x1f1f,
+ 0x1f28,0x1f29,0x1f2a,0x1f2b,0x1f2c,0x1f2d,0x1f2e,0x1f2f,
+ 0x1f28,0x1f29,0x1f2a,0x1f2b,0x1f2c,0x1f2d,0x1f2e,0x1f2f,
+ 0x1f38,0x1f39,0x1f3a,0x1f3b,0x1f3c,0x1f3d,0x1f3e,0x1f3f,
+ 0x1f38,0x1f39,0x1f3a,0x1f3b,0x1f3c,0x1f3d,0x1f3e,0x1f3f,
+ 0x1f48,0x1f49,0x1f4a,0x1f4b,0x1f4c,0x1f4d,0x1f46,0x1f47,
+ 0x1f48,0x1f49,0x1f4a,0x1f4b,0x1f4c,0x1f4d,0x1f4e,0x1f4f,
+ 0x1f50,0x1f59,0x1f52,0x1f5b,0x1f54,0x1f5d,0x1f56,0x1f5f,
+ 0x1f58,0x1f59,0x1f5a,0x1f5b,0x1f5c,0x1f5d,0x1f5e,0x1f5f,
+ 0x1f68,0x1f69,0x1f6a,0x1f6b,0x1f6c,0x1f6d,0x1f6e,0x1f6f,
+ 0x1f68,0x1f69,0x1f6a,0x1f6b,0x1f6c,0x1f6d,0x1f6e,0x1f6f,
+ 0x1fba,0x1fbb,0x1fc8,0x1fc9,0x1fca,0x1fcb,0x1fda,0x1fdb,
+ 0x1ff8,0x1ff9,0x1fea,0x1feb,0x1ffa,0x1ffb,0x1f7e,0x1f7f,
+ 0x1f88,0x1f89,0x1f8a,0x1f8b,0x1f8c,0x1f8d,0x1f8e,0x1f8f,
+ 0x1f88,0x1f89,0x1f8a,0x1f8b,0x1f8c,0x1f8d,0x1f8e,0x1f8f,
+ 0x1f98,0x1f99,0x1f9a,0x1f9b,0x1f9c,0x1f9d,0x1f9e,0x1f9f,
+ 0x1f98,0x1f99,0x1f9a,0x1f9b,0x1f9c,0x1f9d,0x1f9e,0x1f9f,
+ 0x1fa8,0x1fa9,0x1faa,0x1fab,0x1fac,0x1fad,0x1fae,0x1faf,
+ 0x1fa8,0x1fa9,0x1faa,0x1fab,0x1fac,0x1fad,0x1fae,0x1faf,
+ 0x1fb8,0x1fb9,0x1fb2,0x1fbc,0x1fb4,0x1fb5,0x1fb6,0x1fb7,
+ 0x1fb8,0x1fb9,0x1fba,0x1fbb,0x1fbc,0x1fbd,0x0399,0x1fbf,
+ 0x1fc0,0x1fc1,0x1fc2,0x1fcc,0x1fc4,0x1fc5,0x1fc6,0x1fc7,
+ 0x1fc8,0x1fc9,0x1fca,0x1fcb,0x1fcc,0x1fcd,0x1fce,0x1fcf,
+ 0x1fd8,0x1fd9,0x1fd2,0x1fd3,0x1fd4,0x1fd5,0x1fd6,0x1fd7,
+ 0x1fd8,0x1fd9,0x1fda,0x1fdb,0x1fdc,0x1fdd,0x1fde,0x1fdf,
+ 0x1fe8,0x1fe9,0x1fe2,0x1fe3,0x1fe4,0x1fec,0x1fe6,0x1fe7,
+ 0x1fe8,0x1fe9,0x1fea,0x1feb,0x1fec,0x1fed,0x1fee,0x1fef,
+ 0x1ff0,0x1ff1,0x1ff2,0x1ffc,0x1ff4,0x1ff5,0x1ff6,0x1ff7,
+ 0x1ff8,0x1ff9,0x1ffa,0x1ffb,0x1ffc,0x1ffd,0x1ffe,0x1fff,
+ 0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,0x2006,0x2007,
+ 0x2008,0x2009,0x200a,0x200b,0x200c,0x200d,0x200e,0x200f,
+ 0x2010,0x2011,0x2012,0x2013,0x2014,0x2015,0x2016,0x2017,
+ 0x2018,0x2019,0x201a,0x201b,0x201c,0x201d,0x201e,0x201f,
+ 0x2020,0x2021,0x2022,0x2023,0x2024,0x2025,0x2026,0x2027,
+ 0x2028,0x2029,0x202a,0x202b,0x202c,0x202d,0x202e,0x202f,
+ 0x2030,0x2031,0x2032,0x2033,0x2034,0x2035,0x2036,0x2037,
+ 0x2038,0x2039,0x203a,0x203b,0x203c,0x203d,0x203e,0x203f,
+ 0x2040,0x2041,0x2042,0x2043,0x2044,0x2045,0x2046,0x2047,
+ 0x2048,0x2049,0x204a,0x204b,0x204c,0x204d,0x204e,0x204f,
+ 0x2050,0x2051,0x2052,0x2053,0x2054,0x2055,0x2056,0x2057,
+ 0x2058,0x2059,0x205a,0x205b,0x205c,0x205d,0x205e,0x205f,
+ 0x2060,0x2061,0x2062,0x2063,0x2064,0x2065,0x2066,0x2067,
+ 0x2068,0x2069,0x206a,0x206b,0x206c,0x206d,0x206e,0x206f,
+ 0x2070,0x2071,0x2072,0x2073,0x2074,0x2075,0x2076,0x2077,
+ 0x2078,0x2079,0x207a,0x207b,0x207c,0x207d,0x207e,0x207f,
+ 0x2080,0x2081,0x2082,0x2083,0x2084,0x2085,0x2086,0x2087,
+ 0x2088,0x2089,0x208a,0x208b,0x208c,0x208d,0x208e,0x208f,
+ 0x2090,0x2091,0x2092,0x2093,0x2094,0x2095,0x2096,0x2097,
+ 0x2098,0x2099,0x209a,0x209b,0x209c,0x209d,0x209e,0x209f,
+ 0x20a0,0x20a1,0x20a2,0x20a3,0x20a4,0x20a5,0x20a6,0x20a7,
+ 0x20a8,0x20a9,0x20aa,0x20ab,0x20ac,0x20ad,0x20ae,0x20af,
+ 0x20b0,0x20b1,0x20b2,0x20b3,0x20b4,0x20b5,0x20b6,0x20b7,
+ 0x20b8,0x20b9,0x20ba,0x20bb,0x20bc,0x20bd,0x20be,0x20bf,
+ 0x20c0,0x20c1,0x20c2,0x20c3,0x20c4,0x20c5,0x20c6,0x20c7,
+ 0x20c8,0x20c9,0x20ca,0x20cb,0x20cc,0x20cd,0x20ce,0x20cf,
+ 0x20d0,0x20d1,0x20d2,0x20d3,0x20d4,0x20d5,0x20d6,0x20d7,
+ 0x20d8,0x20d9,0x20da,0x20db,0x20dc,0x20dd,0x20de,0x20df,
+ 0x20e0,0x20e1,0x20e2,0x20e3,0x20e4,0x20e5,0x20e6,0x20e7,
+ 0x20e8,0x20e9,0x20ea,0x20eb,0x20ec,0x20ed,0x20ee,0x20ef,
+ 0x20f0,0x20f1,0x20f2,0x20f3,0x20f4,0x20f5,0x20f6,0x20f7,
+ 0x20f8,0x20f9,0x20fa,0x20fb,0x20fc,0x20fd,0x20fe,0x20ff,
+ 0x2100,0x2101,0x2102,0x2103,0x2104,0x2105,0x2106,0x2107,
+ 0x2108,0x2109,0x210a,0x210b,0x210c,0x210d,0x210e,0x210f,
+ 0x2110,0x2111,0x2112,0x2113,0x2114,0x2115,0x2116,0x2117,
+ 0x2118,0x2119,0x211a,0x211b,0x211c,0x211d,0x211e,0x211f,
+ 0x2120,0x2121,0x2122,0x2123,0x2124,0x2125,0x2126,0x2127,
+ 0x2128,0x2129,0x212a,0x212b,0x212c,0x212d,0x212e,0x212f,
+ 0x2130,0x2131,0x2132,0x2133,0x2134,0x2135,0x2136,0x2137,
+ 0x2138,0x2139,0x213a,0x213b,0x213c,0x213d,0x213e,0x213f,
+ 0x2140,0x2141,0x2142,0x2143,0x2144,0x2145,0x2146,0x2147,
+ 0x2148,0x2149,0x214a,0x214b,0x214c,0x214d,0x2132,0x214f,
+ 0x2150,0x2151,0x2152,0x2153,0x2154,0x2155,0x2156,0x2157,
+ 0x2158,0x2159,0x215a,0x215b,0x215c,0x215d,0x215e,0x215f,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,
+ 0x2168,0x2169,0x216a,0x216b,0x216c,0x216d,0x216e,0x216f,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,
+ 0x2168,0x2169,0x216a,0x216b,0x216c,0x216d,0x216e,0x216f,
+ 0x2180,0x2181,0x2182,0x2183,0x2183,0x2185,0x2186,0x2187,
+ 0x2188,0x2189,0x218a,0x218b,0x218c,0x218d,0x218e,0x218f,
+ 0x2190,0x2191,0x2192,0x2193,0x2194,0x2195,0x2196,0x2197,
+ 0x2198,0x2199,0x219a,0x219b,0x219c,0x219d,0x219e,0x219f,
+ 0x21a0,0x21a1,0x21a2,0x21a3,0x21a4,0x21a5,0x21a6,0x21a7,
+ 0x21a8,0x21a9,0x21aa,0x21ab,0x21ac,0x21ad,0x21ae,0x21af,
+ 0x21b0,0x21b1,0x21b2,0x21b3,0x21b4,0x21b5,0x21b6,0x21b7,
+ 0x21b8,0x21b9,0x21ba,0x21bb,0x21bc,0x21bd,0x21be,0x21bf,
+ 0x21c0,0x21c1,0x21c2,0x21c3,0x21c4,0x21c5,0x21c6,0x21c7,
+ 0x21c8,0x21c9,0x21ca,0x21cb,0x21cc,0x21cd,0x21ce,0x21cf,
+ 0x21d0,0x21d1,0x21d2,0x21d3,0x21d4,0x21d5,0x21d6,0x21d7,
+ 0x21d8,0x21d9,0x21da,0x21db,0x21dc,0x21dd,0x21de,0x21df,
+ 0x21e0,0x21e1,0x21e2,0x21e3,0x21e4,0x21e5,0x21e6,0x21e7,
+ 0x21e8,0x21e9,0x21ea,0x21eb,0x21ec,0x21ed,0x21ee,0x21ef,
+ 0x21f0,0x21f1,0x21f2,0x21f3,0x21f4,0x21f5,0x21f6,0x21f7,
+ 0x21f8,0x21f9,0x21fa,0x21fb,0x21fc,0x21fd,0x21fe,0x21ff,
+ 0x2200,0x2201,0x2202,0x2203,0x2204,0x2205,0x2206,0x2207,
+ 0x2208,0x2209,0x220a,0x220b,0x220c,0x220d,0x220e,0x220f,
+ 0x2210,0x2211,0x2212,0x2213,0x2214,0x2215,0x2216,0x2217,
+ 0x2218,0x2219,0x221a,0x221b,0x221c,0x221d,0x221e,0x221f,
+ 0x2220,0x2221,0x2222,0x2223,0x2224,0x2225,0x2226,0x2227,
+ 0x2228,0x2229,0x222a,0x222b,0x222c,0x222d,0x222e,0x222f,
+ 0x2230,0x2231,0x2232,0x2233,0x2234,0x2235,0x2236,0x2237,
+ 0x2238,0x2239,0x223a,0x223b,0x223c,0x223d,0x223e,0x223f,
+ 0x2240,0x2241,0x2242,0x2243,0x2244,0x2245,0x2246,0x2247,
+ 0x2248,0x2249,0x224a,0x224b,0x224c,0x224d,0x224e,0x224f,
+ 0x2250,0x2251,0x2252,0x2253,0x2254,0x2255,0x2256,0x2257,
+ 0x2258,0x2259,0x225a,0x225b,0x225c,0x225d,0x225e,0x225f,
+ 0x2260,0x2261,0x2262,0x2263,0x2264,0x2265,0x2266,0x2267,
+ 0x2268,0x2269,0x226a,0x226b,0x226c,0x226d,0x226e,0x226f,
+ 0x2270,0x2271,0x2272,0x2273,0x2274,0x2275,0x2276,0x2277,
+ 0x2278,0x2279,0x227a,0x227b,0x227c,0x227d,0x227e,0x227f,
+ 0x2280,0x2281,0x2282,0x2283,0x2284,0x2285,0x2286,0x2287,
+ 0x2288,0x2289,0x228a,0x228b,0x228c,0x228d,0x228e,0x228f,
+ 0x2290,0x2291,0x2292,0x2293,0x2294,0x2295,0x2296,0x2297,
+ 0x2298,0x2299,0x229a,0x229b,0x229c,0x229d,0x229e,0x229f,
+ 0x22a0,0x22a1,0x22a2,0x22a3,0x22a4,0x22a5,0x22a6,0x22a7,
+ 0x22a8,0x22a9,0x22aa,0x22ab,0x22ac,0x22ad,0x22ae,0x22af,
+ 0x22b0,0x22b1,0x22b2,0x22b3,0x22b4,0x22b5,0x22b6,0x22b7,
+ 0x22b8,0x22b9,0x22ba,0x22bb,0x22bc,0x22bd,0x22be,0x22bf,
+ 0x22c0,0x22c1,0x22c2,0x22c3,0x22c4,0x22c5,0x22c6,0x22c7,
+ 0x22c8,0x22c9,0x22ca,0x22cb,0x22cc,0x22cd,0x22ce,0x22cf,
+ 0x22d0,0x22d1,0x22d2,0x22d3,0x22d4,0x22d5,0x22d6,0x22d7,
+ 0x22d8,0x22d9,0x22da,0x22db,0x22dc,0x22dd,0x22de,0x22df,
+ 0x22e0,0x22e1,0x22e2,0x22e3,0x22e4,0x22e5,0x22e6,0x22e7,
+ 0x22e8,0x22e9,0x22ea,0x22eb,0x22ec,0x22ed,0x22ee,0x22ef,
+ 0x22f0,0x22f1,0x22f2,0x22f3,0x22f4,0x22f5,0x22f6,0x22f7,
+ 0x22f8,0x22f9,0x22fa,0x22fb,0x22fc,0x22fd,0x22fe,0x22ff,
+ 0x2300,0x2301,0x2302,0x2303,0x2304,0x2305,0x2306,0x2307,
+ 0x2308,0x2309,0x230a,0x230b,0x230c,0x230d,0x230e,0x230f,
+ 0x2310,0x2311,0x2312,0x2313,0x2314,0x2315,0x2316,0x2317,
+ 0x2318,0x2319,0x231a,0x231b,0x231c,0x231d,0x231e,0x231f,
+ 0x2320,0x2321,0x2322,0x2323,0x2324,0x2325,0x2326,0x2327,
+ 0x2328,0x2329,0x232a,0x232b,0x232c,0x232d,0x232e,0x232f,
+ 0x2330,0x2331,0x2332,0x2333,0x2334,0x2335,0x2336,0x2337,
+ 0x2338,0x2339,0x233a,0x233b,0x233c,0x233d,0x233e,0x233f,
+ 0x2340,0x2341,0x2342,0x2343,0x2344,0x2345,0x2346,0x2347,
+ 0x2348,0x2349,0x234a,0x234b,0x234c,0x234d,0x234e,0x234f,
+ 0x2350,0x2351,0x2352,0x2353,0x2354,0x2355,0x2356,0x2357,
+ 0x2358,0x2359,0x235a,0x235b,0x235c,0x235d,0x235e,0x235f,
+ 0x2360,0x2361,0x2362,0x2363,0x2364,0x2365,0x2366,0x2367,
+ 0x2368,0x2369,0x236a,0x236b,0x236c,0x236d,0x236e,0x236f,
+ 0x2370,0x2371,0x2372,0x2373,0x2374,0x2375,0x2376,0x2377,
+ 0x2378,0x2379,0x237a,0x237b,0x237c,0x237d,0x237e,0x237f,
+ 0x2380,0x2381,0x2382,0x2383,0x2384,0x2385,0x2386,0x2387,
+ 0x2388,0x2389,0x238a,0x238b,0x238c,0x238d,0x238e,0x238f,
+ 0x2390,0x2391,0x2392,0x2393,0x2394,0x2395,0x2396,0x2397,
+ 0x2398,0x2399,0x239a,0x239b,0x239c,0x239d,0x239e,0x239f,
+ 0x23a0,0x23a1,0x23a2,0x23a3,0x23a4,0x23a5,0x23a6,0x23a7,
+ 0x23a8,0x23a9,0x23aa,0x23ab,0x23ac,0x23ad,0x23ae,0x23af,
+ 0x23b0,0x23b1,0x23b2,0x23b3,0x23b4,0x23b5,0x23b6,0x23b7,
+ 0x23b8,0x23b9,0x23ba,0x23bb,0x23bc,0x23bd,0x23be,0x23bf,
+ 0x23c0,0x23c1,0x23c2,0x23c3,0x23c4,0x23c5,0x23c6,0x23c7,
+ 0x23c8,0x23c9,0x23ca,0x23cb,0x23cc,0x23cd,0x23ce,0x23cf,
+ 0x23d0,0x23d1,0x23d2,0x23d3,0x23d4,0x23d5,0x23d6,0x23d7,
+ 0x23d8,0x23d9,0x23da,0x23db,0x23dc,0x23dd,0x23de,0x23df,
+ 0x23e0,0x23e1,0x23e2,0x23e3,0x23e4,0x23e5,0x23e6,0x23e7,
+ 0x23e8,0x23e9,0x23ea,0x23eb,0x23ec,0x23ed,0x23ee,0x23ef,
+ 0x23f0,0x23f1,0x23f2,0x23f3,0x23f4,0x23f5,0x23f6,0x23f7,
+ 0x23f8,0x23f9,0x23fa,0x23fb,0x23fc,0x23fd,0x23fe,0x23ff,
+ 0x2400,0x2401,0x2402,0x2403,0x2404,0x2405,0x2406,0x2407,
+ 0x2408,0x2409,0x240a,0x240b,0x240c,0x240d,0x240e,0x240f,
+ 0x2410,0x2411,0x2412,0x2413,0x2414,0x2415,0x2416,0x2417,
+ 0x2418,0x2419,0x241a,0x241b,0x241c,0x241d,0x241e,0x241f,
+ 0x2420,0x2421,0x2422,0x2423,0x2424,0x2425,0x2426,0x2427,
+ 0x2428,0x2429,0x242a,0x242b,0x242c,0x242d,0x242e,0x242f,
+ 0x2430,0x2431,0x2432,0x2433,0x2434,0x2435,0x2436,0x2437,
+ 0x2438,0x2439,0x243a,0x243b,0x243c,0x243d,0x243e,0x243f,
+ 0x2440,0x2441,0x2442,0x2443,0x2444,0x2445,0x2446,0x2447,
+ 0x2448,0x2449,0x244a,0x244b,0x244c,0x244d,0x244e,0x244f,
+ 0x2450,0x2451,0x2452,0x2453,0x2454,0x2455,0x2456,0x2457,
+ 0x2458,0x2459,0x245a,0x245b,0x245c,0x245d,0x245e,0x245f,
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,
+ 0x2468,0x2469,0x246a,0x246b,0x246c,0x246d,0x246e,0x246f,
+ 0x2470,0x2471,0x2472,0x2473,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,
+ 0x2480,0x2481,0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,
+ 0x2488,0x2489,0x248a,0x248b,0x248c,0x248d,0x248e,0x248f,
+ 0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,
+ 0x2498,0x2499,0x249a,0x249b,0x249c,0x249d,0x249e,0x249f,
+ 0x24a0,0x24a1,0x24a2,0x24a3,0x24a4,0x24a5,0x24a6,0x24a7,
+ 0x24a8,0x24a9,0x24aa,0x24ab,0x24ac,0x24ad,0x24ae,0x24af,
+ 0x24b0,0x24b1,0x24b2,0x24b3,0x24b4,0x24b5,0x24b6,0x24b7,
+ 0x24b8,0x24b9,0x24ba,0x24bb,0x24bc,0x24bd,0x24be,0x24bf,
+ 0x24c0,0x24c1,0x24c2,0x24c3,0x24c4,0x24c5,0x24c6,0x24c7,
+ 0x24c8,0x24c9,0x24ca,0x24cb,0x24cc,0x24cd,0x24ce,0x24cf,
+ 0x24b6,0x24b7,0x24b8,0x24b9,0x24ba,0x24bb,0x24bc,0x24bd,
+ 0x24be,0x24bf,0x24c0,0x24c1,0x24c2,0x24c3,0x24c4,0x24c5,
+ 0x24c6,0x24c7,0x24c8,0x24c9,0x24ca,0x24cb,0x24cc,0x24cd,
+ 0x24ce,0x24cf,0x24ea,0x24eb,0x24ec,0x24ed,0x24ee,0x24ef,
+ 0x24f0,0x24f1,0x24f2,0x24f3,0x24f4,0x24f5,0x24f6,0x24f7,
+ 0x24f8,0x24f9,0x24fa,0x24fb,0x24fc,0x24fd,0x24fe,0x24ff,
+ 0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,0x2507,
+ 0x2508,0x2509,0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,
+ 0x2510,0x2511,0x2512,0x2513,0x2514,0x2515,0x2516,0x2517,
+ 0x2518,0x2519,0x251a,0x251b,0x251c,0x251d,0x251e,0x251f,
+ 0x2520,0x2521,0x2522,0x2523,0x2524,0x2525,0x2526,0x2527,
+ 0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,0x252f,
+ 0x2530,0x2531,0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,
+ 0x2538,0x2539,0x253a,0x253b,0x253c,0x253d,0x253e,0x253f,
+ 0x2540,0x2541,0x2542,0x2543,0x2544,0x2545,0x2546,0x2547,
+ 0x2548,0x2549,0x254a,0x254b,0x254c,0x254d,0x254e,0x254f,
+ 0x2550,0x2551,0x2552,0x2553,0x2554,0x2555,0x2556,0x2557,
+ 0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,0x255f,
+ 0x2560,0x2561,0x2562,0x2563,0x2564,0x2565,0x2566,0x2567,
+ 0x2568,0x2569,0x256a,0x256b,0x256c,0x256d,0x256e,0x256f,
+ 0x2570,0x2571,0x2572,0x2573,0x2574,0x2575,0x2576,0x2577,
+ 0x2578,0x2579,0x257a,0x257b,0x257c,0x257d,0x257e,0x257f,
+ 0x2580,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,
+ 0x2588,0x2589,0x258a,0x258b,0x258c,0x258d,0x258e,0x258f,
+ 0x2590,0x2591,0x2592,0x2593,0x2594,0x2595,0x2596,0x2597,
+ 0x2598,0x2599,0x259a,0x259b,0x259c,0x259d,0x259e,0x259f,
+ 0x25a0,0x25a1,0x25a2,0x25a3,0x25a4,0x25a5,0x25a6,0x25a7,
+ 0x25a8,0x25a9,0x25aa,0x25ab,0x25ac,0x25ad,0x25ae,0x25af,
+ 0x25b0,0x25b1,0x25b2,0x25b3,0x25b4,0x25b5,0x25b6,0x25b7,
+ 0x25b8,0x25b9,0x25ba,0x25bb,0x25bc,0x25bd,0x25be,0x25bf,
+ 0x25c0,0x25c1,0x25c2,0x25c3,0x25c4,0x25c5,0x25c6,0x25c7,
+ 0x25c8,0x25c9,0x25ca,0x25cb,0x25cc,0x25cd,0x25ce,0x25cf,
+ 0x25d0,0x25d1,0x25d2,0x25d3,0x25d4,0x25d5,0x25d6,0x25d7,
+ 0x25d8,0x25d9,0x25da,0x25db,0x25dc,0x25dd,0x25de,0x25df,
+ 0x25e0,0x25e1,0x25e2,0x25e3,0x25e4,0x25e5,0x25e6,0x25e7,
+ 0x25e8,0x25e9,0x25ea,0x25eb,0x25ec,0x25ed,0x25ee,0x25ef,
+ 0x25f0,0x25f1,0x25f2,0x25f3,0x25f4,0x25f5,0x25f6,0x25f7,
+ 0x25f8,0x25f9,0x25fa,0x25fb,0x25fc,0x25fd,0x25fe,0x25ff,
+ 0x2600,0x2601,0x2602,0x2603,0x2604,0x2605,0x2606,0x2607,
+ 0x2608,0x2609,0x260a,0x260b,0x260c,0x260d,0x260e,0x260f,
+ 0x2610,0x2611,0x2612,0x2613,0x2614,0x2615,0x2616,0x2617,
+ 0x2618,0x2619,0x261a,0x261b,0x261c,0x261d,0x261e,0x261f,
+ 0x2620,0x2621,0x2622,0x2623,0x2624,0x2625,0x2626,0x2627,
+ 0x2628,0x2629,0x262a,0x262b,0x262c,0x262d,0x262e,0x262f,
+ 0x2630,0x2631,0x2632,0x2633,0x2634,0x2635,0x2636,0x2637,
+ 0x2638,0x2639,0x263a,0x263b,0x263c,0x263d,0x263e,0x263f,
+ 0x2640,0x2641,0x2642,0x2643,0x2644,0x2645,0x2646,0x2647,
+ 0x2648,0x2649,0x264a,0x264b,0x264c,0x264d,0x264e,0x264f,
+ 0x2650,0x2651,0x2652,0x2653,0x2654,0x2655,0x2656,0x2657,
+ 0x2658,0x2659,0x265a,0x265b,0x265c,0x265d,0x265e,0x265f,
+ 0x2660,0x2661,0x2662,0x2663,0x2664,0x2665,0x2666,0x2667,
+ 0x2668,0x2669,0x266a,0x266b,0x266c,0x266d,0x266e,0x266f,
+ 0x2670,0x2671,0x2672,0x2673,0x2674,0x2675,0x2676,0x2677,
+ 0x2678,0x2679,0x267a,0x267b,0x267c,0x267d,0x267e,0x267f,
+ 0x2680,0x2681,0x2682,0x2683,0x2684,0x2685,0x2686,0x2687,
+ 0x2688,0x2689,0x268a,0x268b,0x268c,0x268d,0x268e,0x268f,
+ 0x2690,0x2691,0x2692,0x2693,0x2694,0x2695,0x2696,0x2697,
+ 0x2698,0x2699,0x269a,0x269b,0x269c,0x269d,0x269e,0x269f,
+ 0x26a0,0x26a1,0x26a2,0x26a3,0x26a4,0x26a5,0x26a6,0x26a7,
+ 0x26a8,0x26a9,0x26aa,0x26ab,0x26ac,0x26ad,0x26ae,0x26af,
+ 0x26b0,0x26b1,0x26b2,0x26b3,0x26b4,0x26b5,0x26b6,0x26b7,
+ 0x26b8,0x26b9,0x26ba,0x26bb,0x26bc,0x26bd,0x26be,0x26bf,
+ 0x26c0,0x26c1,0x26c2,0x26c3,0x26c4,0x26c5,0x26c6,0x26c7,
+ 0x26c8,0x26c9,0x26ca,0x26cb,0x26cc,0x26cd,0x26ce,0x26cf,
+ 0x26d0,0x26d1,0x26d2,0x26d3,0x26d4,0x26d5,0x26d6,0x26d7,
+ 0x26d8,0x26d9,0x26da,0x26db,0x26dc,0x26dd,0x26de,0x26df,
+ 0x26e0,0x26e1,0x26e2,0x26e3,0x26e4,0x26e5,0x26e6,0x26e7,
+ 0x26e8,0x26e9,0x26ea,0x26eb,0x26ec,0x26ed,0x26ee,0x26ef,
+ 0x26f0,0x26f1,0x26f2,0x26f3,0x26f4,0x26f5,0x26f6,0x26f7,
+ 0x26f8,0x26f9,0x26fa,0x26fb,0x26fc,0x26fd,0x26fe,0x26ff,
+ 0x2700,0x2701,0x2702,0x2703,0x2704,0x2705,0x2706,0x2707,
+ 0x2708,0x2709,0x270a,0x270b,0x270c,0x270d,0x270e,0x270f,
+ 0x2710,0x2711,0x2712,0x2713,0x2714,0x2715,0x2716,0x2717,
+ 0x2718,0x2719,0x271a,0x271b,0x271c,0x271d,0x271e,0x271f,
+ 0x2720,0x2721,0x2722,0x2723,0x2724,0x2725,0x2726,0x2727,
+ 0x2728,0x2729,0x272a,0x272b,0x272c,0x272d,0x272e,0x272f,
+ 0x2730,0x2731,0x2732,0x2733,0x2734,0x2735,0x2736,0x2737,
+ 0x2738,0x2739,0x273a,0x273b,0x273c,0x273d,0x273e,0x273f,
+ 0x2740,0x2741,0x2742,0x2743,0x2744,0x2745,0x2746,0x2747,
+ 0x2748,0x2749,0x274a,0x274b,0x274c,0x274d,0x274e,0x274f,
+ 0x2750,0x2751,0x2752,0x2753,0x2754,0x2755,0x2756,0x2757,
+ 0x2758,0x2759,0x275a,0x275b,0x275c,0x275d,0x275e,0x275f,
+ 0x2760,0x2761,0x2762,0x2763,0x2764,0x2765,0x2766,0x2767,
+ 0x2768,0x2769,0x276a,0x276b,0x276c,0x276d,0x276e,0x276f,
+ 0x2770,0x2771,0x2772,0x2773,0x2774,0x2775,0x2776,0x2777,
+ 0x2778,0x2779,0x277a,0x277b,0x277c,0x277d,0x277e,0x277f,
+ 0x2780,0x2781,0x2782,0x2783,0x2784,0x2785,0x2786,0x2787,
+ 0x2788,0x2789,0x278a,0x278b,0x278c,0x278d,0x278e,0x278f,
+ 0x2790,0x2791,0x2792,0x2793,0x2794,0x2795,0x2796,0x2797,
+ 0x2798,0x2799,0x279a,0x279b,0x279c,0x279d,0x279e,0x279f,
+ 0x27a0,0x27a1,0x27a2,0x27a3,0x27a4,0x27a5,0x27a6,0x27a7,
+ 0x27a8,0x27a9,0x27aa,0x27ab,0x27ac,0x27ad,0x27ae,0x27af,
+ 0x27b0,0x27b1,0x27b2,0x27b3,0x27b4,0x27b5,0x27b6,0x27b7,
+ 0x27b8,0x27b9,0x27ba,0x27bb,0x27bc,0x27bd,0x27be,0x27bf,
+ 0x27c0,0x27c1,0x27c2,0x27c3,0x27c4,0x27c5,0x27c6,0x27c7,
+ 0x27c8,0x27c9,0x27ca,0x27cb,0x27cc,0x27cd,0x27ce,0x27cf,
+ 0x27d0,0x27d1,0x27d2,0x27d3,0x27d4,0x27d5,0x27d6,0x27d7,
+ 0x27d8,0x27d9,0x27da,0x27db,0x27dc,0x27dd,0x27de,0x27df,
+ 0x27e0,0x27e1,0x27e2,0x27e3,0x27e4,0x27e5,0x27e6,0x27e7,
+ 0x27e8,0x27e9,0x27ea,0x27eb,0x27ec,0x27ed,0x27ee,0x27ef,
+ 0x27f0,0x27f1,0x27f2,0x27f3,0x27f4,0x27f5,0x27f6,0x27f7,
+ 0x27f8,0x27f9,0x27fa,0x27fb,0x27fc,0x27fd,0x27fe,0x27ff,
+ 0x2800,0x2801,0x2802,0x2803,0x2804,0x2805,0x2806,0x2807,
+ 0x2808,0x2809,0x280a,0x280b,0x280c,0x280d,0x280e,0x280f,
+ 0x2810,0x2811,0x2812,0x2813,0x2814,0x2815,0x2816,0x2817,
+ 0x2818,0x2819,0x281a,0x281b,0x281c,0x281d,0x281e,0x281f,
+ 0x2820,0x2821,0x2822,0x2823,0x2824,0x2825,0x2826,0x2827,
+ 0x2828,0x2829,0x282a,0x282b,0x282c,0x282d,0x282e,0x282f,
+ 0x2830,0x2831,0x2832,0x2833,0x2834,0x2835,0x2836,0x2837,
+ 0x2838,0x2839,0x283a,0x283b,0x283c,0x283d,0x283e,0x283f,
+ 0x2840,0x2841,0x2842,0x2843,0x2844,0x2845,0x2846,0x2847,
+ 0x2848,0x2849,0x284a,0x284b,0x284c,0x284d,0x284e,0x284f,
+ 0x2850,0x2851,0x2852,0x2853,0x2854,0x2855,0x2856,0x2857,
+ 0x2858,0x2859,0x285a,0x285b,0x285c,0x285d,0x285e,0x285f,
+ 0x2860,0x2861,0x2862,0x2863,0x2864,0x2865,0x2866,0x2867,
+ 0x2868,0x2869,0x286a,0x286b,0x286c,0x286d,0x286e,0x286f,
+ 0x2870,0x2871,0x2872,0x2873,0x2874,0x2875,0x2876,0x2877,
+ 0x2878,0x2879,0x287a,0x287b,0x287c,0x287d,0x287e,0x287f,
+ 0x2880,0x2881,0x2882,0x2883,0x2884,0x2885,0x2886,0x2887,
+ 0x2888,0x2889,0x288a,0x288b,0x288c,0x288d,0x288e,0x288f,
+ 0x2890,0x2891,0x2892,0x2893,0x2894,0x2895,0x2896,0x2897,
+ 0x2898,0x2899,0x289a,0x289b,0x289c,0x289d,0x289e,0x289f,
+ 0x28a0,0x28a1,0x28a2,0x28a3,0x28a4,0x28a5,0x28a6,0x28a7,
+ 0x28a8,0x28a9,0x28aa,0x28ab,0x28ac,0x28ad,0x28ae,0x28af,
+ 0x28b0,0x28b1,0x28b2,0x28b3,0x28b4,0x28b5,0x28b6,0x28b7,
+ 0x28b8,0x28b9,0x28ba,0x28bb,0x28bc,0x28bd,0x28be,0x28bf,
+ 0x28c0,0x28c1,0x28c2,0x28c3,0x28c4,0x28c5,0x28c6,0x28c7,
+ 0x28c8,0x28c9,0x28ca,0x28cb,0x28cc,0x28cd,0x28ce,0x28cf,
+ 0x28d0,0x28d1,0x28d2,0x28d3,0x28d4,0x28d5,0x28d6,0x28d7,
+ 0x28d8,0x28d9,0x28da,0x28db,0x28dc,0x28dd,0x28de,0x28df,
+ 0x28e0,0x28e1,0x28e2,0x28e3,0x28e4,0x28e5,0x28e6,0x28e7,
+ 0x28e8,0x28e9,0x28ea,0x28eb,0x28ec,0x28ed,0x28ee,0x28ef,
+ 0x28f0,0x28f1,0x28f2,0x28f3,0x28f4,0x28f5,0x28f6,0x28f7,
+ 0x28f8,0x28f9,0x28fa,0x28fb,0x28fc,0x28fd,0x28fe,0x28ff,
+ 0x2900,0x2901,0x2902,0x2903,0x2904,0x2905,0x2906,0x2907,
+ 0x2908,0x2909,0x290a,0x290b,0x290c,0x290d,0x290e,0x290f,
+ 0x2910,0x2911,0x2912,0x2913,0x2914,0x2915,0x2916,0x2917,
+ 0x2918,0x2919,0x291a,0x291b,0x291c,0x291d,0x291e,0x291f,
+ 0x2920,0x2921,0x2922,0x2923,0x2924,0x2925,0x2926,0x2927,
+ 0x2928,0x2929,0x292a,0x292b,0x292c,0x292d,0x292e,0x292f,
+ 0x2930,0x2931,0x2932,0x2933,0x2934,0x2935,0x2936,0x2937,
+ 0x2938,0x2939,0x293a,0x293b,0x293c,0x293d,0x293e,0x293f,
+ 0x2940,0x2941,0x2942,0x2943,0x2944,0x2945,0x2946,0x2947,
+ 0x2948,0x2949,0x294a,0x294b,0x294c,0x294d,0x294e,0x294f,
+ 0x2950,0x2951,0x2952,0x2953,0x2954,0x2955,0x2956,0x2957,
+ 0x2958,0x2959,0x295a,0x295b,0x295c,0x295d,0x295e,0x295f,
+ 0x2960,0x2961,0x2962,0x2963,0x2964,0x2965,0x2966,0x2967,
+ 0x2968,0x2969,0x296a,0x296b,0x296c,0x296d,0x296e,0x296f,
+ 0x2970,0x2971,0x2972,0x2973,0x2974,0x2975,0x2976,0x2977,
+ 0x2978,0x2979,0x297a,0x297b,0x297c,0x297d,0x297e,0x297f,
+ 0x2980,0x2981,0x2982,0x2983,0x2984,0x2985,0x2986,0x2987,
+ 0x2988,0x2989,0x298a,0x298b,0x298c,0x298d,0x298e,0x298f,
+ 0x2990,0x2991,0x2992,0x2993,0x2994,0x2995,0x2996,0x2997,
+ 0x2998,0x2999,0x299a,0x299b,0x299c,0x299d,0x299e,0x299f,
+ 0x29a0,0x29a1,0x29a2,0x29a3,0x29a4,0x29a5,0x29a6,0x29a7,
+ 0x29a8,0x29a9,0x29aa,0x29ab,0x29ac,0x29ad,0x29ae,0x29af,
+ 0x29b0,0x29b1,0x29b2,0x29b3,0x29b4,0x29b5,0x29b6,0x29b7,
+ 0x29b8,0x29b9,0x29ba,0x29bb,0x29bc,0x29bd,0x29be,0x29bf,
+ 0x29c0,0x29c1,0x29c2,0x29c3,0x29c4,0x29c5,0x29c6,0x29c7,
+ 0x29c8,0x29c9,0x29ca,0x29cb,0x29cc,0x29cd,0x29ce,0x29cf,
+ 0x29d0,0x29d1,0x29d2,0x29d3,0x29d4,0x29d5,0x29d6,0x29d7,
+ 0x29d8,0x29d9,0x29da,0x29db,0x29dc,0x29dd,0x29de,0x29df,
+ 0x29e0,0x29e1,0x29e2,0x29e3,0x29e4,0x29e5,0x29e6,0x29e7,
+ 0x29e8,0x29e9,0x29ea,0x29eb,0x29ec,0x29ed,0x29ee,0x29ef,
+ 0x29f0,0x29f1,0x29f2,0x29f3,0x29f4,0x29f5,0x29f6,0x29f7,
+ 0x29f8,0x29f9,0x29fa,0x29fb,0x29fc,0x29fd,0x29fe,0x29ff,
+ 0x2a00,0x2a01,0x2a02,0x2a03,0x2a04,0x2a05,0x2a06,0x2a07,
+ 0x2a08,0x2a09,0x2a0a,0x2a0b,0x2a0c,0x2a0d,0x2a0e,0x2a0f,
+ 0x2a10,0x2a11,0x2a12,0x2a13,0x2a14,0x2a15,0x2a16,0x2a17,
+ 0x2a18,0x2a19,0x2a1a,0x2a1b,0x2a1c,0x2a1d,0x2a1e,0x2a1f,
+ 0x2a20,0x2a21,0x2a22,0x2a23,0x2a24,0x2a25,0x2a26,0x2a27,
+ 0x2a28,0x2a29,0x2a2a,0x2a2b,0x2a2c,0x2a2d,0x2a2e,0x2a2f,
+ 0x2a30,0x2a31,0x2a32,0x2a33,0x2a34,0x2a35,0x2a36,0x2a37,
+ 0x2a38,0x2a39,0x2a3a,0x2a3b,0x2a3c,0x2a3d,0x2a3e,0x2a3f,
+ 0x2a40,0x2a41,0x2a42,0x2a43,0x2a44,0x2a45,0x2a46,0x2a47,
+ 0x2a48,0x2a49,0x2a4a,0x2a4b,0x2a4c,0x2a4d,0x2a4e,0x2a4f,
+ 0x2a50,0x2a51,0x2a52,0x2a53,0x2a54,0x2a55,0x2a56,0x2a57,
+ 0x2a58,0x2a59,0x2a5a,0x2a5b,0x2a5c,0x2a5d,0x2a5e,0x2a5f,
+ 0x2a60,0x2a61,0x2a62,0x2a63,0x2a64,0x2a65,0x2a66,0x2a67,
+ 0x2a68,0x2a69,0x2a6a,0x2a6b,0x2a6c,0x2a6d,0x2a6e,0x2a6f,
+ 0x2a70,0x2a71,0x2a72,0x2a73,0x2a74,0x2a75,0x2a76,0x2a77,
+ 0x2a78,0x2a79,0x2a7a,0x2a7b,0x2a7c,0x2a7d,0x2a7e,0x2a7f,
+ 0x2a80,0x2a81,0x2a82,0x2a83,0x2a84,0x2a85,0x2a86,0x2a87,
+ 0x2a88,0x2a89,0x2a8a,0x2a8b,0x2a8c,0x2a8d,0x2a8e,0x2a8f,
+ 0x2a90,0x2a91,0x2a92,0x2a93,0x2a94,0x2a95,0x2a96,0x2a97,
+ 0x2a98,0x2a99,0x2a9a,0x2a9b,0x2a9c,0x2a9d,0x2a9e,0x2a9f,
+ 0x2aa0,0x2aa1,0x2aa2,0x2aa3,0x2aa4,0x2aa5,0x2aa6,0x2aa7,
+ 0x2aa8,0x2aa9,0x2aaa,0x2aab,0x2aac,0x2aad,0x2aae,0x2aaf,
+ 0x2ab0,0x2ab1,0x2ab2,0x2ab3,0x2ab4,0x2ab5,0x2ab6,0x2ab7,
+ 0x2ab8,0x2ab9,0x2aba,0x2abb,0x2abc,0x2abd,0x2abe,0x2abf,
+ 0x2ac0,0x2ac1,0x2ac2,0x2ac3,0x2ac4,0x2ac5,0x2ac6,0x2ac7,
+ 0x2ac8,0x2ac9,0x2aca,0x2acb,0x2acc,0x2acd,0x2ace,0x2acf,
+ 0x2ad0,0x2ad1,0x2ad2,0x2ad3,0x2ad4,0x2ad5,0x2ad6,0x2ad7,
+ 0x2ad8,0x2ad9,0x2ada,0x2adb,0x2adc,0x2add,0x2ade,0x2adf,
+ 0x2ae0,0x2ae1,0x2ae2,0x2ae3,0x2ae4,0x2ae5,0x2ae6,0x2ae7,
+ 0x2ae8,0x2ae9,0x2aea,0x2aeb,0x2aec,0x2aed,0x2aee,0x2aef,
+ 0x2af0,0x2af1,0x2af2,0x2af3,0x2af4,0x2af5,0x2af6,0x2af7,
+ 0x2af8,0x2af9,0x2afa,0x2afb,0x2afc,0x2afd,0x2afe,0x2aff,
+ 0x2b00,0x2b01,0x2b02,0x2b03,0x2b04,0x2b05,0x2b06,0x2b07,
+ 0x2b08,0x2b09,0x2b0a,0x2b0b,0x2b0c,0x2b0d,0x2b0e,0x2b0f,
+ 0x2b10,0x2b11,0x2b12,0x2b13,0x2b14,0x2b15,0x2b16,0x2b17,
+ 0x2b18,0x2b19,0x2b1a,0x2b1b,0x2b1c,0x2b1d,0x2b1e,0x2b1f,
+ 0x2b20,0x2b21,0x2b22,0x2b23,0x2b24,0x2b25,0x2b26,0x2b27,
+ 0x2b28,0x2b29,0x2b2a,0x2b2b,0x2b2c,0x2b2d,0x2b2e,0x2b2f,
+ 0x2b30,0x2b31,0x2b32,0x2b33,0x2b34,0x2b35,0x2b36,0x2b37,
+ 0x2b38,0x2b39,0x2b3a,0x2b3b,0x2b3c,0x2b3d,0x2b3e,0x2b3f,
+ 0x2b40,0x2b41,0x2b42,0x2b43,0x2b44,0x2b45,0x2b46,0x2b47,
+ 0x2b48,0x2b49,0x2b4a,0x2b4b,0x2b4c,0x2b4d,0x2b4e,0x2b4f,
+ 0x2b50,0x2b51,0x2b52,0x2b53,0x2b54,0x2b55,0x2b56,0x2b57,
+ 0x2b58,0x2b59,0x2b5a,0x2b5b,0x2b5c,0x2b5d,0x2b5e,0x2b5f,
+ 0x2b60,0x2b61,0x2b62,0x2b63,0x2b64,0x2b65,0x2b66,0x2b67,
+ 0x2b68,0x2b69,0x2b6a,0x2b6b,0x2b6c,0x2b6d,0x2b6e,0x2b6f,
+ 0x2b70,0x2b71,0x2b72,0x2b73,0x2b74,0x2b75,0x2b76,0x2b77,
+ 0x2b78,0x2b79,0x2b7a,0x2b7b,0x2b7c,0x2b7d,0x2b7e,0x2b7f,
+ 0x2b80,0x2b81,0x2b82,0x2b83,0x2b84,0x2b85,0x2b86,0x2b87,
+ 0x2b88,0x2b89,0x2b8a,0x2b8b,0x2b8c,0x2b8d,0x2b8e,0x2b8f,
+ 0x2b90,0x2b91,0x2b92,0x2b93,0x2b94,0x2b95,0x2b96,0x2b97,
+ 0x2b98,0x2b99,0x2b9a,0x2b9b,0x2b9c,0x2b9d,0x2b9e,0x2b9f,
+ 0x2ba0,0x2ba1,0x2ba2,0x2ba3,0x2ba4,0x2ba5,0x2ba6,0x2ba7,
+ 0x2ba8,0x2ba9,0x2baa,0x2bab,0x2bac,0x2bad,0x2bae,0x2baf,
+ 0x2bb0,0x2bb1,0x2bb2,0x2bb3,0x2bb4,0x2bb5,0x2bb6,0x2bb7,
+ 0x2bb8,0x2bb9,0x2bba,0x2bbb,0x2bbc,0x2bbd,0x2bbe,0x2bbf,
+ 0x2bc0,0x2bc1,0x2bc2,0x2bc3,0x2bc4,0x2bc5,0x2bc6,0x2bc7,
+ 0x2bc8,0x2bc9,0x2bca,0x2bcb,0x2bcc,0x2bcd,0x2bce,0x2bcf,
+ 0x2bd0,0x2bd1,0x2bd2,0x2bd3,0x2bd4,0x2bd5,0x2bd6,0x2bd7,
+ 0x2bd8,0x2bd9,0x2bda,0x2bdb,0x2bdc,0x2bdd,0x2bde,0x2bdf,
+ 0x2be0,0x2be1,0x2be2,0x2be3,0x2be4,0x2be5,0x2be6,0x2be7,
+ 0x2be8,0x2be9,0x2bea,0x2beb,0x2bec,0x2bed,0x2bee,0x2bef,
+ 0x2bf0,0x2bf1,0x2bf2,0x2bf3,0x2bf4,0x2bf5,0x2bf6,0x2bf7,
+ 0x2bf8,0x2bf9,0x2bfa,0x2bfb,0x2bfc,0x2bfd,0x2bfe,0x2bff,
+ 0x2c00,0x2c01,0x2c02,0x2c03,0x2c04,0x2c05,0x2c06,0x2c07,
+ 0x2c08,0x2c09,0x2c0a,0x2c0b,0x2c0c,0x2c0d,0x2c0e,0x2c0f,
+ 0x2c10,0x2c11,0x2c12,0x2c13,0x2c14,0x2c15,0x2c16,0x2c17,
+ 0x2c18,0x2c19,0x2c1a,0x2c1b,0x2c1c,0x2c1d,0x2c1e,0x2c1f,
+ 0x2c20,0x2c21,0x2c22,0x2c23,0x2c24,0x2c25,0x2c26,0x2c27,
+ 0x2c28,0x2c29,0x2c2a,0x2c2b,0x2c2c,0x2c2d,0x2c2e,0x2c2f,
+ 0x2c00,0x2c01,0x2c02,0x2c03,0x2c04,0x2c05,0x2c06,0x2c07,
+ 0x2c08,0x2c09,0x2c0a,0x2c0b,0x2c0c,0x2c0d,0x2c0e,0x2c0f,
+ 0x2c10,0x2c11,0x2c12,0x2c13,0x2c14,0x2c15,0x2c16,0x2c17,
+ 0x2c18,0x2c19,0x2c1a,0x2c1b,0x2c1c,0x2c1d,0x2c1e,0x2c1f,
+ 0x2c20,0x2c21,0x2c22,0x2c23,0x2c24,0x2c25,0x2c26,0x2c27,
+ 0x2c28,0x2c29,0x2c2a,0x2c2b,0x2c2c,0x2c2d,0x2c2e,0x2c5f,
+ 0x2c60,0x2c60,0x2c62,0x2c63,0x2c64,0x023a,0x023e,0x2c67,
+ 0x2c67,0x2c69,0x2c69,0x2c6b,0x2c6b,0x2c6d,0x2c6e,0x2c6f,
+ 0x2c70,0x2c71,0x2c72,0x2c73,0x2c74,0x2c75,0x2c75,0x2c77,
+ 0x2c78,0x2c79,0x2c7a,0x2c7b,0x2c7c,0x2c7d,0x2c7e,0x2c7f,
+ 0x2c80,0x2c80,0x2c82,0x2c82,0x2c84,0x2c84,0x2c86,0x2c86,
+ 0x2c88,0x2c88,0x2c8a,0x2c8a,0x2c8c,0x2c8c,0x2c8e,0x2c8e,
+ 0x2c90,0x2c90,0x2c92,0x2c92,0x2c94,0x2c94,0x2c96,0x2c96,
+ 0x2c98,0x2c98,0x2c9a,0x2c9a,0x2c9c,0x2c9c,0x2c9e,0x2c9e,
+ 0x2ca0,0x2ca0,0x2ca2,0x2ca2,0x2ca4,0x2ca4,0x2ca6,0x2ca6,
+ 0x2ca8,0x2ca8,0x2caa,0x2caa,0x2cac,0x2cac,0x2cae,0x2cae,
+ 0x2cb0,0x2cb0,0x2cb2,0x2cb2,0x2cb4,0x2cb4,0x2cb6,0x2cb6,
+ 0x2cb8,0x2cb8,0x2cba,0x2cba,0x2cbc,0x2cbc,0x2cbe,0x2cbe,
+ 0x2cc0,0x2cc0,0x2cc2,0x2cc2,0x2cc4,0x2cc4,0x2cc6,0x2cc6,
+ 0x2cc8,0x2cc8,0x2cca,0x2cca,0x2ccc,0x2ccc,0x2cce,0x2cce,
+ 0x2cd0,0x2cd0,0x2cd2,0x2cd2,0x2cd4,0x2cd4,0x2cd6,0x2cd6,
+ 0x2cd8,0x2cd8,0x2cda,0x2cda,0x2cdc,0x2cdc,0x2cde,0x2cde,
+ 0x2ce0,0x2ce0,0x2ce2,0x2ce2,0x2ce4,0x2ce5,0x2ce6,0x2ce7,
+ 0x2ce8,0x2ce9,0x2cea,0x2ceb,0x2cec,0x2ced,0x2cee,0x2cef,
+ 0x2cf0,0x2cf1,0x2cf2,0x2cf3,0x2cf4,0x2cf5,0x2cf6,0x2cf7,
+ 0x2cf8,0x2cf9,0x2cfa,0x2cfb,0x2cfc,0x2cfd,0x2cfe,0x2cff,
+ 0x10a0,0x10a1,0x10a2,0x10a3,0x10a4,0x10a5,0x10a6,0x10a7,
+ 0x10a8,0x10a9,0x10aa,0x10ab,0x10ac,0x10ad,0x10ae,0x10af,
+ 0x10b0,0x10b1,0x10b2,0x10b3,0x10b4,0x10b5,0x10b6,0x10b7,
+ 0x10b8,0x10b9,0x10ba,0x10bb,0x10bc,0x10bd,0x10be,0x10bf,
+ 0x10c0,0x10c1,0x10c2,0x10c3,0x10c4,0x10c5
+};
diff --git a/imap/src/charset/viscii.c b/imap/src/charset/viscii.c
new file mode 100644
index 00000000..9cc24e68
--- /dev/null
+++ b/imap/src/charset/viscii.c
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VISCII conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 June 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* VISCII is the VIetnamese Standard Code for Information Interchange,
+ * defined by the Vietnamese Standardization Working Group, RFC-1456,
+ * May 1993.
+ */
+
+static const unsigned short visciitab[256] = {
+ 0x0000,0x0001,0x1eb2,0x0003,0x0004,0x1eb4,0x1eaa,0x0007,
+ 0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
+ 0x0010,0x0011,0x0012,0x0013,0x1ef6,0x0015,0x0016,0x0017,
+ 0x0018,0x1ef8,0x001a,0x001b,0x001c,0x001d,0x1ef4,0x001f,
+ 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,
+ 0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
+ 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,
+ 0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
+ 0x1ea0,0x1eae,0x1eb0,0x1eb6,0x1ea4,0x1ea6,0x1ea8,0x1eac,
+ 0x1ebc,0x1eb8,0x1ebe,0x1ec0,0x1ec2,0x1ec4,0x1ec6,0x1ed0,
+ 0x1ed2,0x1ed4,0x1ed6,0x1ed8,0x1ee2,0x1eda,0x1edc,0x1ede,
+ 0x1eca,0x1ece,0x1ecc,0x1ec8,0x1ee6,0x0168,0x1ee4,0x1ef2,
+ 0x00d5,0x1eaf,0x1eb1,0x1eb7,0x1ea5,0x1ea7,0x1ea9,0x1ead,
+ 0x1ebd,0x1eb9,0x1ebf,0x1ec1,0x1ec3,0x1ec5,0x1ec7,0x1ed1,
+ 0x1ed3,0x1ed5,0x1ed7,0x1ee0,0x01a0,0x1ed9,0x1edd,0x1edf,
+ 0x1ecb,0x1ef0,0x1ee8,0x1eea,0x1eec,0x01a1,0x1edb,0x01af,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x1ea2,0x0102,0x1eb3,0x1eb5,
+ 0x00c8,0x00c9,0x00ca,0x1eba,0x00cc,0x00cd,0x0128,0x1ef3,
+ 0x0110,0x1ee9,0x00d2,0x00d3,0x00d4,0x1ea1,0x1ef7,0x1eeb,
+ 0x1eed,0x00d9,0x00da,0x1ef9,0x1ef5,0x00dd,0x1ee1,0x01b0,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x1ea3,0x0103,0x1eef,0x1eab,
+ 0x00e8,0x00e9,0x00ea,0x1ebb,0x00ec,0x00ed,0x0129,0x1ec9,
+ 0x0111,0x1ef1,0x00f2,0x00f3,0x00f4,0x00f5,0x1ecf,0x1ecd,
+ 0x1ee5,0x00f9,0x00fa,0x0169,0x1ee7,0x00fd,0x1ee3,0x1eee
+};
diff --git a/imap/src/charset/widths.c b/imap/src/charset/widths.c
new file mode 100644
index 00000000..3b0655a9
--- /dev/null
+++ b/imap/src/charset/widths.c
@@ -0,0 +1,4136 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode width table (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 March 2006
+ * Last Edited: 1 March 2007
+ */
+
+/* Table of 2-bit character widths, indexed by Unicode codepoint and
+ * big-endian within bytes:
+ * 0 zero-width
+ * 1 single-width (half-width)
+ * 2 double-width (full-width)
+ * 3 ambiguous
+ */
+
+#define UCS4_WIDLEN 131072
+
+static const unsigned char ucs4_widthtab[32768] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0000 - U+001f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+0020 - U+003f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+0040 - U+005f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54, /* U+0060 - U+007f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0080 - U+009f */
+ 0xf5,0xd7,0xff,0x4d,0xff,0xff,0xff,0xff, /* U+00a0 - U+00bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+00c0 - U+00df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+00e0 - U+00ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0100 - U+011f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0120 - U+013f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0140 - U+015f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0160 - U+017f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0180 - U+019f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01a0 - U+01bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01c0 - U+01df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01e0 - U+01ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0200 - U+021f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0220 - U+023f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0240 - U+025f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0260 - U+027f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0280 - U+029f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02a0 - U+02bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02c0 - U+02df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02e0 - U+02ff */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0300 - U+031f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0320 - U+033f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0340 - U+035f */
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* U+0360 - U+037f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0380 - U+039f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03a0 - U+03bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03c0 - U+03df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03e0 - U+03ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0400 - U+041f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0420 - U+043f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0440 - U+045f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0460 - U+047f */
+ 0xfc,0x03,0x0f,0xff,0xff,0xff,0xff,0xff, /* U+0480 - U+049f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04a0 - U+04bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04c0 - U+04df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04e0 - U+04ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0500 - U+051f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0520 - U+053f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0540 - U+055f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0560 - U+057f */
+ 0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x00, /* U+0580 - U+059f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c, /* U+05a0 - U+05bf */
+ 0xc3,0x0c,0xff,0xff,0xff,0xff,0xff,0xff, /* U+05c0 - U+05df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+05e0 - U+05ff */
+ 0x00,0xff,0xff,0xff,0x00,0x0f,0xff,0xff, /* U+0600 - U+061f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0620 - U+063f */
+ 0xff,0xff,0xfc,0x00,0x00,0x00,0x00,0x03, /* U+0640 - U+065f */
+ 0xff,0xff,0xff,0xff,0x3f,0xff,0xff,0xff, /* U+0660 - U+067f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0680 - U+069f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+06a0 - U+06bf */
+ 0xff,0xff,0xff,0xff,0xff,0xf0,0x00,0x00, /* U+06c0 - U+06df */
+ 0x00,0x3c,0x30,0x0f,0xff,0xff,0xff,0xff, /* U+06e0 - U+06ff */
+ 0xff,0xff,0xff,0xfc,0xcf,0xff,0xff,0xff, /* U+0700 - U+071f */
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* U+0720 - U+073f */
+ 0x00,0x00,0x03,0xff,0xff,0xff,0xff,0xff, /* U+0740 - U+075f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0760 - U+077f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0780 - U+079f */
+ 0xff,0xf0,0x00,0x00,0x3f,0xff,0xff,0xff, /* U+07a0 - U+07bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+07c0 - U+07df */
+ 0xff,0xff,0xfc,0x00,0x00,0xff,0xff,0xff, /* U+07e0 - U+07ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0800 - U+081f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0820 - U+083f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0840 - U+085f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0860 - U+087f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0880 - U+089f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08a0 - U+08bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08c0 - U+08df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08e0 - U+08ff */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0900 - U+091f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0920 - U+093f */
+ 0xc0,0x00,0x3f,0xcf,0xc0,0x3f,0xff,0xff, /* U+0940 - U+095f */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0960 - U+097f */
+ 0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0980 - U+099f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+09a0 - U+09bf */
+ 0xc0,0x3f,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+09c0 - U+09df */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+09e0 - U+09ff */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0a00 - U+0a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0a20 - U+0a3f */
+ 0xc3,0xfc,0x3c,0x0f,0xff,0xff,0xff,0xff, /* U+0a40 - U+0a5f */
+ 0xff,0xff,0xff,0xff,0x0f,0xff,0xff,0xff, /* U+0a60 - U+0a7f */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0a80 - U+0a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0aa0 - U+0abf */
+ 0xc0,0x0c,0x3f,0xcf,0xff,0xff,0xff,0xff, /* U+0ac0 - U+0adf */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ae0 - U+0aff */
+ 0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b00 - U+0b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3c, /* U+0b20 - U+0b3f */
+ 0xc0,0xff,0xff,0xcf,0xff,0xf3,0xff,0xff, /* U+0b40 - U+0b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b60 - U+0b7f */
+ 0xf3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b80 - U+0b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ba0 - U+0bbf */
+ 0x3f,0xff,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+0bc0 - U+0bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0be0 - U+0bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c00 - U+0c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, /* U+0c20 - U+0c3f */
+ 0x3f,0xf0,0x30,0x0f,0xff,0xc3,0xff,0xff, /* U+0c40 - U+0c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c60 - U+0c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c80 - U+0c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0ca0 - U+0cbf */
+ 0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0xff, /* U+0cc0 - U+0cdf */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ce0 - U+0cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d00 - U+0d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d20 - U+0d3f */
+ 0xc0,0xff,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+0d40 - U+0d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d60 - U+0d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d80 - U+0d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0da0 - U+0dbf */
+ 0xff,0xff,0xf3,0xff,0xf0,0x33,0xff,0xff, /* U+0dc0 - U+0ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0de0 - U+0dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e00 - U+0e1f */
+ 0xff,0xff,0xff,0xff,0xcf,0x00,0x03,0xff, /* U+0e20 - U+0e3f */
+ 0xff,0xfc,0x00,0x03,0xff,0xff,0xff,0xff, /* U+0e40 - U+0e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e60 - U+0e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e80 - U+0e9f */
+ 0xff,0xff,0xff,0xff,0xcf,0x00,0x0c,0x3f, /* U+0ea0 - U+0ebf */
+ 0xff,0xff,0x00,0x0f,0xff,0xff,0xff,0xff, /* U+0ec0 - U+0edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ee0 - U+0eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff, /* U+0f00 - U+0f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xcc,0xcf,0xff, /* U+0f20 - U+0f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0f40 - U+0f5f */
+ 0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x03, /* U+0f60 - U+0f7f */
+ 0x00,0x30,0xff,0xff,0x00,0x00,0xc0,0x00, /* U+0f80 - U+0f9f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f, /* U+0fa0 - U+0fbf */
+ 0xff,0xf3,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0fc0 - U+0fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0fe0 - U+0fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1000 - U+101f */
+ 0xff,0xff,0xff,0xc0,0x33,0xf0,0xcf,0xff, /* U+1020 - U+103f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff, /* U+1040 - U+105f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1060 - U+107f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1080 - U+109f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a0 - U+10bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c0 - U+10df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e0 - U+10ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+1100 - U+111f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+1120 - U+113f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xfe, /* U+1140 - U+115f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1160 - U+117f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1180 - U+119f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a0 - U+11bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c0 - U+11df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e0 - U+11ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1200 - U+121f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1220 - U+123f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1240 - U+125f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1260 - U+127f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1280 - U+129f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a0 - U+12bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c0 - U+12df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e0 - U+12ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1300 - U+131f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1320 - U+133f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+1340 - U+135f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1360 - U+137f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1380 - U+139f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a0 - U+13bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c0 - U+13df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e0 - U+13ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1400 - U+141f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1420 - U+143f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1440 - U+145f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1460 - U+147f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1480 - U+149f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a0 - U+14bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c0 - U+14df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e0 - U+14ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1500 - U+151f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1520 - U+153f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1540 - U+155f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1560 - U+157f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1580 - U+159f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a0 - U+15bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c0 - U+15df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e0 - U+15ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1600 - U+161f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1620 - U+163f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1640 - U+165f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1660 - U+167f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1680 - U+169f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a0 - U+16bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c0 - U+16df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e0 - U+16ff */
+ 0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff, /* U+1700 - U+171f */
+ 0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff, /* U+1720 - U+173f */
+ 0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff, /* U+1740 - U+175f */
+ 0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff, /* U+1760 - U+177f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1780 - U+179f */
+ 0xff,0xff,0xff,0xff,0xff,0x0c,0x00,0x0f, /* U+17a0 - U+17bf */
+ 0xff,0xf3,0xc0,0x00,0x00,0xff,0xff,0xcf, /* U+17c0 - U+17df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e0 - U+17ff */
+ 0xff,0xff,0xfc,0x0f,0xff,0xff,0xff,0xff, /* U+1800 - U+181f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1820 - U+183f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1840 - U+185f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1860 - U+187f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1880 - U+189f */
+ 0xff,0xff,0xcf,0xff,0xff,0xff,0xff,0xff, /* U+18a0 - U+18bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c0 - U+18df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e0 - U+18ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1900 - U+191f */
+ 0x03,0xfc,0x00,0xff,0xf3,0xff,0xc0,0xff, /* U+1920 - U+193f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1940 - U+195f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1960 - U+197f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1980 - U+199f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a0 - U+19bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c0 - U+19df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e0 - U+19ff */
+ 0xff,0xff,0xff,0xff,0xff,0xfc,0x3f,0xff, /* U+1a00 - U+1a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a20 - U+1a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a40 - U+1a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a60 - U+1a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a80 - U+1a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa0 - U+1abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac0 - U+1adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae0 - U+1aff */
+ 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b00 - U+1b1f */
+ 0xff,0xff,0xff,0xff,0xff,0x30,0x03,0x3f, /* U+1b20 - U+1b3f */
+ 0xf3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b40 - U+1b5f */
+ 0xff,0xff,0xfc,0x00,0x00,0xff,0xff,0xff, /* U+1b60 - U+1b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b80 - U+1b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba0 - U+1bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc0 - U+1bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be0 - U+1bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c00 - U+1c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c20 - U+1c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c40 - U+1c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c60 - U+1c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c80 - U+1c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca0 - U+1cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc0 - U+1cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce0 - U+1cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d00 - U+1d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d20 - U+1d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d40 - U+1d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d60 - U+1d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d80 - U+1d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da0 - U+1dbf */
+ 0x00,0x00,0x03,0xff,0xff,0xff,0xff,0xff, /* U+1dc0 - U+1ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, /* U+1de0 - U+1dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e00 - U+1e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e20 - U+1e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e40 - U+1e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e60 - U+1e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e80 - U+1e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea0 - U+1ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec0 - U+1edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee0 - U+1eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f00 - U+1f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f20 - U+1f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f40 - U+1f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f60 - U+1f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f80 - U+1f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa0 - U+1fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc0 - U+1fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe0 - U+1fff */
+ 0xff,0xff,0xfc,0x00,0xff,0xff,0xff,0xff, /* U+2000 - U+201f */
+ 0xff,0xff,0xf0,0x03,0xff,0xff,0xff,0xff, /* U+2020 - U+203f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2040 - U+205f */
+ 0x00,0xff,0xf0,0x00,0xff,0xff,0xff,0xff, /* U+2060 - U+207f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2080 - U+209f */
+ 0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff, /* U+20a0 - U+20bf */
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* U+20c0 - U+20df */
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* U+20e0 - U+20ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2100 - U+211f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2120 - U+213f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2140 - U+215f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2160 - U+217f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2180 - U+219f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21a0 - U+21bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21c0 - U+21df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21e0 - U+21ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2200 - U+221f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2220 - U+223f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2240 - U+225f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2260 - U+227f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2280 - U+229f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22a0 - U+22bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22c0 - U+22df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22e0 - U+22ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2300 - U+231f */
+ 0xff,0xff,0xeb,0xff,0xff,0xff,0xff,0xff, /* U+2320 - U+233f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2340 - U+235f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2360 - U+237f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2380 - U+239f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23a0 - U+23bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23c0 - U+23df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23e0 - U+23ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2400 - U+241f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2420 - U+243f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2440 - U+245f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2460 - U+247f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2480 - U+249f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24a0 - U+24bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24c0 - U+24df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24e0 - U+24ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2500 - U+251f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2520 - U+253f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2540 - U+255f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2560 - U+257f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2580 - U+259f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25a0 - U+25bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25c0 - U+25df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25e0 - U+25ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2600 - U+261f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2620 - U+263f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2640 - U+265f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2660 - U+267f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2680 - U+269f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26a0 - U+26bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26c0 - U+26df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26e0 - U+26ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2700 - U+271f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2720 - U+273f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2740 - U+275f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2760 - U+277f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2780 - U+279f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+27a0 - U+27bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+27c0 - U+27df */
+ 0xff,0xf5,0x55,0xff,0xff,0xff,0xff,0xff, /* U+27e0 - U+27ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2800 - U+281f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2820 - U+283f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2840 - U+285f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2860 - U+287f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2880 - U+289f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28a0 - U+28bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28c0 - U+28df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28e0 - U+28ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2900 - U+291f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2920 - U+293f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2940 - U+295f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2960 - U+297f */
+ 0xff,0xd7,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2980 - U+299f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29a0 - U+29bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29c0 - U+29df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29e0 - U+29ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a00 - U+2a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a20 - U+2a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a40 - U+2a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a60 - U+2a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a80 - U+2a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2aa0 - U+2abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ac0 - U+2adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ae0 - U+2aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b00 - U+2b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b20 - U+2b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b40 - U+2b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b60 - U+2b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b80 - U+2b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ba0 - U+2bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2bc0 - U+2bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2be0 - U+2bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c00 - U+2c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c20 - U+2c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c40 - U+2c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c60 - U+2c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c80 - U+2c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ca0 - U+2cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2cc0 - U+2cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ce0 - U+2cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d00 - U+2d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d20 - U+2d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d40 - U+2d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d60 - U+2d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d80 - U+2d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2da0 - U+2dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2dc0 - U+2ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2de0 - U+2dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e00 - U+2e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e20 - U+2e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e40 - U+2e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e60 - U+2e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xaa, /* U+2e80 - U+2e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2ea0 - U+2ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2ec0 - U+2edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xff,0xff,0xff, /* U+2ee0 - U+2eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f00 - U+2f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f20 - U+2f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f40 - U+2f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f60 - U+2f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f80 - U+2f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2fa0 - U+2fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff,0xff, /* U+2fc0 - U+2fdf */
+ 0xff,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xff, /* U+2fe0 - U+2fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3000 - U+301f */
+ 0xaa,0xaa,0xa0,0x00,0xaa,0xaa,0xaa,0xab, /* U+3020 - U+303f */
+ 0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3040 - U+305f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3060 - U+307f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xc2,0xaa, /* U+3080 - U+309f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30a0 - U+30bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30c0 - U+30df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30e0 - U+30ff */
+ 0xff,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3100 - U+311f */
+ 0xaa,0xaa,0xaa,0xbf,0xea,0xaa,0xaa,0xaa, /* U+3120 - U+313f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3140 - U+315f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3160 - U+317f */
+ 0xaa,0xaa,0xaa,0xab,0xaa,0xaa,0xaa,0xaa, /* U+3180 - U+319f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xff,0xff, /* U+31a0 - U+31bf */
+ 0xaa,0xaa,0xaa,0xaa,0xff,0xff,0xff,0xff, /* U+31c0 - U+31df */
+ 0xff,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+31e0 - U+31ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab, /* U+3200 - U+321f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3220 - U+323f */
+ 0xaa,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+3240 - U+325f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3260 - U+327f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3280 - U+329f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+32a0 - U+32bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+32c0 - U+32df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab, /* U+32e0 - U+32ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3300 - U+331f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3320 - U+333f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3340 - U+335f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3360 - U+337f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3380 - U+339f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33a0 - U+33bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33c0 - U+33df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33e0 - U+33ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3400 - U+341f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3420 - U+343f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3440 - U+345f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3460 - U+347f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3480 - U+349f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34a0 - U+34bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34c0 - U+34df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34e0 - U+34ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3500 - U+351f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3520 - U+353f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3540 - U+355f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3560 - U+357f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3580 - U+359f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35a0 - U+35bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35c0 - U+35df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35e0 - U+35ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3600 - U+361f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3620 - U+363f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3640 - U+365f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3660 - U+367f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3680 - U+369f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36a0 - U+36bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36c0 - U+36df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36e0 - U+36ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3700 - U+371f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3720 - U+373f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3740 - U+375f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3760 - U+377f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3780 - U+379f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37a0 - U+37bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37c0 - U+37df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37e0 - U+37ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3800 - U+381f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3820 - U+383f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3840 - U+385f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3860 - U+387f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3880 - U+389f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38a0 - U+38bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38c0 - U+38df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38e0 - U+38ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3900 - U+391f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3920 - U+393f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3940 - U+395f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3960 - U+397f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3980 - U+399f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39a0 - U+39bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39c0 - U+39df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39e0 - U+39ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a00 - U+3a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a20 - U+3a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a40 - U+3a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a60 - U+3a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a80 - U+3a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3aa0 - U+3abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ac0 - U+3adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ae0 - U+3aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b00 - U+3b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b20 - U+3b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b40 - U+3b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b60 - U+3b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b80 - U+3b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ba0 - U+3bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3bc0 - U+3bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3be0 - U+3bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c00 - U+3c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c20 - U+3c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c40 - U+3c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c60 - U+3c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c80 - U+3c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ca0 - U+3cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3cc0 - U+3cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ce0 - U+3cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d00 - U+3d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d20 - U+3d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d40 - U+3d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d60 - U+3d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d80 - U+3d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3da0 - U+3dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3dc0 - U+3ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3de0 - U+3dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e00 - U+3e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e20 - U+3e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e40 - U+3e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e60 - U+3e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e80 - U+3e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ea0 - U+3ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ec0 - U+3edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ee0 - U+3eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f00 - U+3f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f20 - U+3f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f40 - U+3f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f60 - U+3f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f80 - U+3f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fa0 - U+3fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fc0 - U+3fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fe0 - U+3fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4000 - U+401f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4020 - U+403f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4040 - U+405f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4060 - U+407f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4080 - U+409f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40a0 - U+40bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40c0 - U+40df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40e0 - U+40ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4100 - U+411f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4120 - U+413f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4140 - U+415f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4160 - U+417f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4180 - U+419f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41a0 - U+41bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41c0 - U+41df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41e0 - U+41ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4200 - U+421f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4220 - U+423f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4240 - U+425f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4260 - U+427f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4280 - U+429f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42a0 - U+42bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42c0 - U+42df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42e0 - U+42ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4300 - U+431f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4320 - U+433f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4340 - U+435f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4360 - U+437f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4380 - U+439f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43a0 - U+43bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43c0 - U+43df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43e0 - U+43ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4400 - U+441f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4420 - U+443f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4440 - U+445f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4460 - U+447f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4480 - U+449f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44a0 - U+44bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44c0 - U+44df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44e0 - U+44ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4500 - U+451f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4520 - U+453f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4540 - U+455f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4560 - U+457f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4580 - U+459f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45a0 - U+45bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45c0 - U+45df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45e0 - U+45ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4600 - U+461f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4620 - U+463f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4640 - U+465f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4660 - U+467f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4680 - U+469f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46a0 - U+46bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46c0 - U+46df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46e0 - U+46ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4700 - U+471f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4720 - U+473f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4740 - U+475f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4760 - U+477f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4780 - U+479f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47a0 - U+47bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47c0 - U+47df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47e0 - U+47ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4800 - U+481f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4820 - U+483f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4840 - U+485f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4860 - U+487f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4880 - U+489f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48a0 - U+48bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48c0 - U+48df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48e0 - U+48ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4900 - U+491f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4920 - U+493f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4940 - U+495f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4960 - U+497f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4980 - U+499f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49a0 - U+49bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49c0 - U+49df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49e0 - U+49ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a00 - U+4a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a20 - U+4a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a40 - U+4a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a60 - U+4a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a80 - U+4a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4aa0 - U+4abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ac0 - U+4adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ae0 - U+4aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b00 - U+4b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b20 - U+4b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b40 - U+4b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b60 - U+4b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b80 - U+4b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ba0 - U+4bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4bc0 - U+4bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4be0 - U+4bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c00 - U+4c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c20 - U+4c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c40 - U+4c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c60 - U+4c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c80 - U+4c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ca0 - U+4cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4cc0 - U+4cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ce0 - U+4cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d00 - U+4d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d20 - U+4d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d40 - U+4d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d60 - U+4d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d80 - U+4d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff,0xff, /* U+4da0 - U+4dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+4dc0 - U+4ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+4de0 - U+4dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e00 - U+4e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e20 - U+4e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e40 - U+4e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e60 - U+4e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e80 - U+4e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ea0 - U+4ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ec0 - U+4edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ee0 - U+4eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f00 - U+4f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f20 - U+4f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f40 - U+4f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f60 - U+4f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f80 - U+4f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fa0 - U+4fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fc0 - U+4fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fe0 - U+4fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5000 - U+501f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5020 - U+503f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5040 - U+505f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5060 - U+507f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5080 - U+509f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50a0 - U+50bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50c0 - U+50df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50e0 - U+50ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5100 - U+511f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5120 - U+513f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5140 - U+515f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5160 - U+517f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5180 - U+519f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51a0 - U+51bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51c0 - U+51df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51e0 - U+51ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5200 - U+521f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5220 - U+523f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5240 - U+525f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5260 - U+527f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5280 - U+529f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52a0 - U+52bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52c0 - U+52df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52e0 - U+52ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5300 - U+531f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5320 - U+533f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5340 - U+535f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5360 - U+537f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5380 - U+539f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53a0 - U+53bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53c0 - U+53df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53e0 - U+53ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5400 - U+541f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5420 - U+543f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5440 - U+545f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5460 - U+547f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5480 - U+549f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54a0 - U+54bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54c0 - U+54df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54e0 - U+54ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5500 - U+551f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5520 - U+553f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5540 - U+555f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5560 - U+557f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5580 - U+559f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55a0 - U+55bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55c0 - U+55df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55e0 - U+55ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5600 - U+561f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5620 - U+563f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5640 - U+565f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5660 - U+567f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5680 - U+569f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56a0 - U+56bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56c0 - U+56df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56e0 - U+56ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5700 - U+571f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5720 - U+573f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5740 - U+575f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5760 - U+577f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5780 - U+579f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57a0 - U+57bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57c0 - U+57df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57e0 - U+57ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5800 - U+581f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5820 - U+583f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5840 - U+585f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5860 - U+587f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5880 - U+589f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58a0 - U+58bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58c0 - U+58df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58e0 - U+58ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5900 - U+591f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5920 - U+593f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5940 - U+595f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5960 - U+597f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5980 - U+599f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59a0 - U+59bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59c0 - U+59df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59e0 - U+59ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a00 - U+5a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a20 - U+5a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a40 - U+5a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a60 - U+5a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a80 - U+5a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5aa0 - U+5abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ac0 - U+5adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ae0 - U+5aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b00 - U+5b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b20 - U+5b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b40 - U+5b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b60 - U+5b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b80 - U+5b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ba0 - U+5bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5bc0 - U+5bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5be0 - U+5bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c00 - U+5c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c20 - U+5c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c40 - U+5c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c60 - U+5c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c80 - U+5c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ca0 - U+5cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5cc0 - U+5cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ce0 - U+5cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d00 - U+5d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d20 - U+5d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d40 - U+5d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d60 - U+5d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d80 - U+5d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5da0 - U+5dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5dc0 - U+5ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5de0 - U+5dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e00 - U+5e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e20 - U+5e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e40 - U+5e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e60 - U+5e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e80 - U+5e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ea0 - U+5ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ec0 - U+5edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ee0 - U+5eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f00 - U+5f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f20 - U+5f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f40 - U+5f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f60 - U+5f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f80 - U+5f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fa0 - U+5fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fc0 - U+5fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fe0 - U+5fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6000 - U+601f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6020 - U+603f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6040 - U+605f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6060 - U+607f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6080 - U+609f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60a0 - U+60bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60c0 - U+60df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60e0 - U+60ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6100 - U+611f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6120 - U+613f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6140 - U+615f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6160 - U+617f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6180 - U+619f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61a0 - U+61bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61c0 - U+61df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61e0 - U+61ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6200 - U+621f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6220 - U+623f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6240 - U+625f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6260 - U+627f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6280 - U+629f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62a0 - U+62bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62c0 - U+62df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62e0 - U+62ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6300 - U+631f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6320 - U+633f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6340 - U+635f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6360 - U+637f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6380 - U+639f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63a0 - U+63bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63c0 - U+63df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63e0 - U+63ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6400 - U+641f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6420 - U+643f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6440 - U+645f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6460 - U+647f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6480 - U+649f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64a0 - U+64bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64c0 - U+64df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64e0 - U+64ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6500 - U+651f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6520 - U+653f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6540 - U+655f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6560 - U+657f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6580 - U+659f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65a0 - U+65bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65c0 - U+65df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65e0 - U+65ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6600 - U+661f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6620 - U+663f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6640 - U+665f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6660 - U+667f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6680 - U+669f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66a0 - U+66bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66c0 - U+66df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66e0 - U+66ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6700 - U+671f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6720 - U+673f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6740 - U+675f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6760 - U+677f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6780 - U+679f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67a0 - U+67bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67c0 - U+67df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67e0 - U+67ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6800 - U+681f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6820 - U+683f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6840 - U+685f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6860 - U+687f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6880 - U+689f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68a0 - U+68bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68c0 - U+68df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68e0 - U+68ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6900 - U+691f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6920 - U+693f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6940 - U+695f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6960 - U+697f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6980 - U+699f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69a0 - U+69bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69c0 - U+69df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69e0 - U+69ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a00 - U+6a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a20 - U+6a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a40 - U+6a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a60 - U+6a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a80 - U+6a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6aa0 - U+6abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ac0 - U+6adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ae0 - U+6aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b00 - U+6b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b20 - U+6b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b40 - U+6b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b60 - U+6b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b80 - U+6b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ba0 - U+6bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6bc0 - U+6bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6be0 - U+6bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c00 - U+6c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c20 - U+6c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c40 - U+6c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c60 - U+6c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c80 - U+6c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ca0 - U+6cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6cc0 - U+6cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ce0 - U+6cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d00 - U+6d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d20 - U+6d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d40 - U+6d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d60 - U+6d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d80 - U+6d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6da0 - U+6dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6dc0 - U+6ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6de0 - U+6dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e00 - U+6e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e20 - U+6e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e40 - U+6e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e60 - U+6e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e80 - U+6e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ea0 - U+6ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ec0 - U+6edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ee0 - U+6eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f00 - U+6f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f20 - U+6f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f40 - U+6f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f60 - U+6f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f80 - U+6f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fa0 - U+6fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fc0 - U+6fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fe0 - U+6fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7000 - U+701f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7020 - U+703f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7040 - U+705f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7060 - U+707f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7080 - U+709f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70a0 - U+70bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70c0 - U+70df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70e0 - U+70ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7100 - U+711f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7120 - U+713f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7140 - U+715f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7160 - U+717f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7180 - U+719f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71a0 - U+71bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71c0 - U+71df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71e0 - U+71ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7200 - U+721f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7220 - U+723f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7240 - U+725f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7260 - U+727f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7280 - U+729f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72a0 - U+72bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72c0 - U+72df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72e0 - U+72ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7300 - U+731f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7320 - U+733f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7340 - U+735f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7360 - U+737f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7380 - U+739f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73a0 - U+73bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73c0 - U+73df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73e0 - U+73ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7400 - U+741f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7420 - U+743f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7440 - U+745f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7460 - U+747f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7480 - U+749f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74a0 - U+74bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74c0 - U+74df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74e0 - U+74ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7500 - U+751f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7520 - U+753f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7540 - U+755f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7560 - U+757f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7580 - U+759f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75a0 - U+75bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75c0 - U+75df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75e0 - U+75ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7600 - U+761f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7620 - U+763f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7640 - U+765f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7660 - U+767f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7680 - U+769f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76a0 - U+76bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76c0 - U+76df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76e0 - U+76ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7700 - U+771f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7720 - U+773f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7740 - U+775f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7760 - U+777f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7780 - U+779f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77a0 - U+77bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77c0 - U+77df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77e0 - U+77ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7800 - U+781f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7820 - U+783f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7840 - U+785f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7860 - U+787f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7880 - U+789f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78a0 - U+78bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78c0 - U+78df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78e0 - U+78ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7900 - U+791f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7920 - U+793f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7940 - U+795f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7960 - U+797f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7980 - U+799f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79a0 - U+79bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79c0 - U+79df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79e0 - U+79ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a00 - U+7a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a20 - U+7a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a40 - U+7a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a60 - U+7a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a80 - U+7a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7aa0 - U+7abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ac0 - U+7adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ae0 - U+7aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b00 - U+7b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b20 - U+7b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b40 - U+7b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b60 - U+7b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b80 - U+7b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ba0 - U+7bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7bc0 - U+7bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7be0 - U+7bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c00 - U+7c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c20 - U+7c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c40 - U+7c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c60 - U+7c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c80 - U+7c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ca0 - U+7cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7cc0 - U+7cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ce0 - U+7cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d00 - U+7d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d20 - U+7d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d40 - U+7d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d60 - U+7d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d80 - U+7d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7da0 - U+7dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7dc0 - U+7ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7de0 - U+7dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e00 - U+7e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e20 - U+7e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e40 - U+7e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e60 - U+7e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e80 - U+7e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ea0 - U+7ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ec0 - U+7edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ee0 - U+7eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f00 - U+7f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f20 - U+7f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f40 - U+7f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f60 - U+7f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f80 - U+7f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fa0 - U+7fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fc0 - U+7fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fe0 - U+7fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8000 - U+801f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8020 - U+803f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8040 - U+805f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8060 - U+807f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8080 - U+809f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80a0 - U+80bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80c0 - U+80df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80e0 - U+80ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8100 - U+811f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8120 - U+813f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8140 - U+815f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8160 - U+817f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8180 - U+819f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81a0 - U+81bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81c0 - U+81df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81e0 - U+81ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8200 - U+821f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8220 - U+823f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8240 - U+825f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8260 - U+827f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8280 - U+829f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82a0 - U+82bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82c0 - U+82df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82e0 - U+82ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8300 - U+831f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8320 - U+833f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8340 - U+835f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8360 - U+837f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8380 - U+839f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83a0 - U+83bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83c0 - U+83df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83e0 - U+83ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8400 - U+841f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8420 - U+843f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8440 - U+845f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8460 - U+847f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8480 - U+849f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84a0 - U+84bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84c0 - U+84df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84e0 - U+84ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8500 - U+851f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8520 - U+853f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8540 - U+855f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8560 - U+857f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8580 - U+859f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85a0 - U+85bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85c0 - U+85df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85e0 - U+85ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8600 - U+861f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8620 - U+863f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8640 - U+865f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8660 - U+867f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8680 - U+869f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86a0 - U+86bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86c0 - U+86df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86e0 - U+86ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8700 - U+871f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8720 - U+873f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8740 - U+875f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8760 - U+877f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8780 - U+879f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87a0 - U+87bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87c0 - U+87df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87e0 - U+87ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8800 - U+881f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8820 - U+883f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8840 - U+885f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8860 - U+887f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8880 - U+889f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88a0 - U+88bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88c0 - U+88df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88e0 - U+88ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8900 - U+891f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8920 - U+893f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8940 - U+895f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8960 - U+897f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8980 - U+899f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89a0 - U+89bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89c0 - U+89df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89e0 - U+89ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a00 - U+8a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a20 - U+8a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a40 - U+8a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a60 - U+8a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a80 - U+8a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8aa0 - U+8abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ac0 - U+8adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ae0 - U+8aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b00 - U+8b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b20 - U+8b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b40 - U+8b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b60 - U+8b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b80 - U+8b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ba0 - U+8bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8bc0 - U+8bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8be0 - U+8bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c00 - U+8c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c20 - U+8c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c40 - U+8c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c60 - U+8c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c80 - U+8c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ca0 - U+8cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8cc0 - U+8cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ce0 - U+8cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d00 - U+8d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d20 - U+8d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d40 - U+8d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d60 - U+8d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d80 - U+8d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8da0 - U+8dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8dc0 - U+8ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8de0 - U+8dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e00 - U+8e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e20 - U+8e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e40 - U+8e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e60 - U+8e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e80 - U+8e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ea0 - U+8ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ec0 - U+8edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ee0 - U+8eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f00 - U+8f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f20 - U+8f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f40 - U+8f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f60 - U+8f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f80 - U+8f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fa0 - U+8fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fc0 - U+8fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fe0 - U+8fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9000 - U+901f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9020 - U+903f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9040 - U+905f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9060 - U+907f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9080 - U+909f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90a0 - U+90bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90c0 - U+90df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90e0 - U+90ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9100 - U+911f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9120 - U+913f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9140 - U+915f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9160 - U+917f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9180 - U+919f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91a0 - U+91bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91c0 - U+91df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91e0 - U+91ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9200 - U+921f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9220 - U+923f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9240 - U+925f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9260 - U+927f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9280 - U+929f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92a0 - U+92bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92c0 - U+92df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92e0 - U+92ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9300 - U+931f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9320 - U+933f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9340 - U+935f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9360 - U+937f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9380 - U+939f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93a0 - U+93bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93c0 - U+93df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93e0 - U+93ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9400 - U+941f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9420 - U+943f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9440 - U+945f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9460 - U+947f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9480 - U+949f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94a0 - U+94bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94c0 - U+94df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94e0 - U+94ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9500 - U+951f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9520 - U+953f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9540 - U+955f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9560 - U+957f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9580 - U+959f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95a0 - U+95bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95c0 - U+95df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95e0 - U+95ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9600 - U+961f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9620 - U+963f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9640 - U+965f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9660 - U+967f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9680 - U+969f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96a0 - U+96bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96c0 - U+96df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96e0 - U+96ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9700 - U+971f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9720 - U+973f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9740 - U+975f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9760 - U+977f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9780 - U+979f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97a0 - U+97bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97c0 - U+97df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97e0 - U+97ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9800 - U+981f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9820 - U+983f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9840 - U+985f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9860 - U+987f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9880 - U+989f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98a0 - U+98bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98c0 - U+98df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98e0 - U+98ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9900 - U+991f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9920 - U+993f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9940 - U+995f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9960 - U+997f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9980 - U+999f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99a0 - U+99bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99c0 - U+99df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99e0 - U+99ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a00 - U+9a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a20 - U+9a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a40 - U+9a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a60 - U+9a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a80 - U+9a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9aa0 - U+9abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ac0 - U+9adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ae0 - U+9aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b00 - U+9b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b20 - U+9b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b40 - U+9b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b60 - U+9b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b80 - U+9b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ba0 - U+9bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9bc0 - U+9bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9be0 - U+9bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c00 - U+9c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c20 - U+9c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c40 - U+9c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c60 - U+9c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c80 - U+9c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ca0 - U+9cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9cc0 - U+9cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ce0 - U+9cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d00 - U+9d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d20 - U+9d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d40 - U+9d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d60 - U+9d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d80 - U+9d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9da0 - U+9dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9dc0 - U+9ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9de0 - U+9dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e00 - U+9e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e20 - U+9e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e40 - U+9e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e60 - U+9e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e80 - U+9e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ea0 - U+9ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ec0 - U+9edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ee0 - U+9eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f00 - U+9f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f20 - U+9f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f40 - U+9f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f60 - U+9f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f80 - U+9f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xff, /* U+9fa0 - U+9fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+9fc0 - U+9fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+9fe0 - U+9fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a000 - U+a01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a020 - U+a03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a040 - U+a05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a060 - U+a07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a080 - U+a09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0a0 - U+a0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0c0 - U+a0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0e0 - U+a0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a100 - U+a11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a120 - U+a13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a140 - U+a15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a160 - U+a17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a180 - U+a19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1a0 - U+a1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1c0 - U+a1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1e0 - U+a1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a200 - U+a21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a220 - U+a23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a240 - U+a25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a260 - U+a27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a280 - U+a29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2a0 - U+a2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2c0 - U+a2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2e0 - U+a2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a300 - U+a31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a320 - U+a33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a340 - U+a35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a360 - U+a37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a380 - U+a39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3a0 - U+a3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3c0 - U+a3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3e0 - U+a3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a400 - U+a41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a420 - U+a43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a440 - U+a45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a460 - U+a47f */
+ 0xaa,0xaa,0xaa,0xbf,0xaa,0xaa,0xaa,0xaa, /* U+a480 - U+a49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a4a0 - U+a4bf */
+ 0xaa,0xab,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a4c0 - U+a4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a4e0 - U+a4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a500 - U+a51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a520 - U+a53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a540 - U+a55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a560 - U+a57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a580 - U+a59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5a0 - U+a5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5c0 - U+a5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5e0 - U+a5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a600 - U+a61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a620 - U+a63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a640 - U+a65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a660 - U+a67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a680 - U+a69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6a0 - U+a6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6c0 - U+a6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6e0 - U+a6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a700 - U+a71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a720 - U+a73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a740 - U+a75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a760 - U+a77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a780 - U+a79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7a0 - U+a7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7c0 - U+a7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7e0 - U+a7ff */
+ 0xf3,0xf3,0xfc,0xff,0xff,0xff,0xff,0xff, /* U+a800 - U+a81f */
+ 0xff,0xc3,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a820 - U+a83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a840 - U+a85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a860 - U+a87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a880 - U+a89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8a0 - U+a8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8c0 - U+a8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8e0 - U+a8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a900 - U+a91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a920 - U+a93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a940 - U+a95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a960 - U+a97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a980 - U+a99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9a0 - U+a9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9c0 - U+a9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9e0 - U+a9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa00 - U+aa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa20 - U+aa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa40 - U+aa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa60 - U+aa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa80 - U+aa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aaa0 - U+aabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aac0 - U+aadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aae0 - U+aaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab00 - U+ab1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab20 - U+ab3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab40 - U+ab5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab60 - U+ab7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab80 - U+ab9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aba0 - U+abbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+abc0 - U+abdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+abe0 - U+abff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac00 - U+ac1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac20 - U+ac3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac40 - U+ac5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac60 - U+ac7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac80 - U+ac9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aca0 - U+acbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+acc0 - U+acdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ace0 - U+acff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad00 - U+ad1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad20 - U+ad3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad40 - U+ad5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad60 - U+ad7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad80 - U+ad9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ada0 - U+adbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+adc0 - U+addf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ade0 - U+adff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae00 - U+ae1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae20 - U+ae3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae40 - U+ae5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae60 - U+ae7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae80 - U+ae9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aea0 - U+aebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aec0 - U+aedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aee0 - U+aeff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af00 - U+af1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af20 - U+af3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af40 - U+af5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af60 - U+af7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af80 - U+af9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afa0 - U+afbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afc0 - U+afdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afe0 - U+afff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b000 - U+b01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b020 - U+b03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b040 - U+b05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b060 - U+b07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b080 - U+b09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0a0 - U+b0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0c0 - U+b0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0e0 - U+b0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b100 - U+b11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b120 - U+b13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b140 - U+b15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b160 - U+b17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b180 - U+b19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1a0 - U+b1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1c0 - U+b1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1e0 - U+b1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b200 - U+b21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b220 - U+b23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b240 - U+b25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b260 - U+b27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b280 - U+b29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2a0 - U+b2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2c0 - U+b2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2e0 - U+b2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b300 - U+b31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b320 - U+b33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b340 - U+b35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b360 - U+b37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b380 - U+b39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3a0 - U+b3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3c0 - U+b3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3e0 - U+b3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b400 - U+b41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b420 - U+b43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b440 - U+b45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b460 - U+b47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b480 - U+b49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4a0 - U+b4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4c0 - U+b4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4e0 - U+b4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b500 - U+b51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b520 - U+b53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b540 - U+b55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b560 - U+b57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b580 - U+b59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5a0 - U+b5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5c0 - U+b5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5e0 - U+b5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b600 - U+b61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b620 - U+b63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b640 - U+b65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b660 - U+b67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b680 - U+b69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6a0 - U+b6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6c0 - U+b6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6e0 - U+b6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b700 - U+b71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b720 - U+b73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b740 - U+b75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b760 - U+b77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b780 - U+b79f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7a0 - U+b7bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7c0 - U+b7df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7e0 - U+b7ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b800 - U+b81f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b820 - U+b83f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b840 - U+b85f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b860 - U+b87f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b880 - U+b89f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8a0 - U+b8bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8c0 - U+b8df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8e0 - U+b8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b900 - U+b91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b920 - U+b93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b940 - U+b95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b960 - U+b97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b980 - U+b99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9a0 - U+b9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9c0 - U+b9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9e0 - U+b9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba00 - U+ba1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba20 - U+ba3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba40 - U+ba5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba60 - U+ba7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba80 - U+ba9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+baa0 - U+babf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bac0 - U+badf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bae0 - U+baff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb00 - U+bb1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb20 - U+bb3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb40 - U+bb5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb60 - U+bb7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb80 - U+bb9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bba0 - U+bbbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bbc0 - U+bbdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bbe0 - U+bbff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc00 - U+bc1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc20 - U+bc3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc40 - U+bc5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc60 - U+bc7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc80 - U+bc9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bca0 - U+bcbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bcc0 - U+bcdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bce0 - U+bcff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd00 - U+bd1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd20 - U+bd3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd40 - U+bd5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd60 - U+bd7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd80 - U+bd9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bda0 - U+bdbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bdc0 - U+bddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bde0 - U+bdff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be00 - U+be1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be20 - U+be3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be40 - U+be5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be60 - U+be7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be80 - U+be9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bea0 - U+bebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bec0 - U+bedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bee0 - U+beff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf00 - U+bf1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf20 - U+bf3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf40 - U+bf5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf60 - U+bf7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf80 - U+bf9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfa0 - U+bfbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfc0 - U+bfdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfe0 - U+bfff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c000 - U+c01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c020 - U+c03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c040 - U+c05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c060 - U+c07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c080 - U+c09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0a0 - U+c0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0c0 - U+c0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0e0 - U+c0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c100 - U+c11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c120 - U+c13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c140 - U+c15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c160 - U+c17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c180 - U+c19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1a0 - U+c1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1c0 - U+c1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1e0 - U+c1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c200 - U+c21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c220 - U+c23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c240 - U+c25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c260 - U+c27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c280 - U+c29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2a0 - U+c2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2c0 - U+c2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2e0 - U+c2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c300 - U+c31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c320 - U+c33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c340 - U+c35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c360 - U+c37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c380 - U+c39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3a0 - U+c3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3c0 - U+c3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3e0 - U+c3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c400 - U+c41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c420 - U+c43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c440 - U+c45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c460 - U+c47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c480 - U+c49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4a0 - U+c4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4c0 - U+c4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4e0 - U+c4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c500 - U+c51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c520 - U+c53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c540 - U+c55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c560 - U+c57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c580 - U+c59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5a0 - U+c5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5c0 - U+c5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5e0 - U+c5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c600 - U+c61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c620 - U+c63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c640 - U+c65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c660 - U+c67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c680 - U+c69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6a0 - U+c6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6c0 - U+c6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6e0 - U+c6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c700 - U+c71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c720 - U+c73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c740 - U+c75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c760 - U+c77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c780 - U+c79f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7a0 - U+c7bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7c0 - U+c7df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7e0 - U+c7ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c800 - U+c81f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c820 - U+c83f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c840 - U+c85f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c860 - U+c87f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c880 - U+c89f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8a0 - U+c8bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8c0 - U+c8df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8e0 - U+c8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c900 - U+c91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c920 - U+c93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c940 - U+c95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c960 - U+c97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c980 - U+c99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9a0 - U+c9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9c0 - U+c9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9e0 - U+c9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca00 - U+ca1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca20 - U+ca3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca40 - U+ca5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca60 - U+ca7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca80 - U+ca9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+caa0 - U+cabf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cac0 - U+cadf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cae0 - U+caff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb00 - U+cb1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb20 - U+cb3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb40 - U+cb5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb60 - U+cb7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb80 - U+cb9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cba0 - U+cbbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cbc0 - U+cbdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cbe0 - U+cbff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc00 - U+cc1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc20 - U+cc3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc40 - U+cc5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc60 - U+cc7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc80 - U+cc9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cca0 - U+ccbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ccc0 - U+ccdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cce0 - U+ccff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd00 - U+cd1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd20 - U+cd3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd40 - U+cd5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd60 - U+cd7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd80 - U+cd9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cda0 - U+cdbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cdc0 - U+cddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cde0 - U+cdff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce00 - U+ce1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce20 - U+ce3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce40 - U+ce5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce60 - U+ce7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce80 - U+ce9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cea0 - U+cebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cec0 - U+cedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cee0 - U+ceff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf00 - U+cf1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf20 - U+cf3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf40 - U+cf5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf60 - U+cf7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf80 - U+cf9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfa0 - U+cfbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfc0 - U+cfdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfe0 - U+cfff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d000 - U+d01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d020 - U+d03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d040 - U+d05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d060 - U+d07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d080 - U+d09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0a0 - U+d0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0c0 - U+d0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0e0 - U+d0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d100 - U+d11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d120 - U+d13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d140 - U+d15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d160 - U+d17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d180 - U+d19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1a0 - U+d1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1c0 - U+d1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1e0 - U+d1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d200 - U+d21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d220 - U+d23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d240 - U+d25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d260 - U+d27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d280 - U+d29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2a0 - U+d2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2c0 - U+d2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2e0 - U+d2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d300 - U+d31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d320 - U+d33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d340 - U+d35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d360 - U+d37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d380 - U+d39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3a0 - U+d3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3c0 - U+d3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3e0 - U+d3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d400 - U+d41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d420 - U+d43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d440 - U+d45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d460 - U+d47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d480 - U+d49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4a0 - U+d4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4c0 - U+d4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4e0 - U+d4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d500 - U+d51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d520 - U+d53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d540 - U+d55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d560 - U+d57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d580 - U+d59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5a0 - U+d5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5c0 - U+d5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5e0 - U+d5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d600 - U+d61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d620 - U+d63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d640 - U+d65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d660 - U+d67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d680 - U+d69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6a0 - U+d6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6c0 - U+d6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6e0 - U+d6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d700 - U+d71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d720 - U+d73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d740 - U+d75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d760 - U+d77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d780 - U+d79f */
+ 0xaa,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7a0 - U+d7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7c0 - U+d7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7e0 - U+d7ff */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d800 - U+d81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d820 - U+d83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d840 - U+d85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d860 - U+d87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d880 - U+d89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8a0 - U+d8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8c0 - U+d8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8e0 - U+d8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d900 - U+d91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d920 - U+d93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d940 - U+d95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d960 - U+d97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d980 - U+d99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9a0 - U+d9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9c0 - U+d9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9e0 - U+d9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da00 - U+da1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da20 - U+da3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da40 - U+da5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da60 - U+da7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da80 - U+da9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+daa0 - U+dabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dac0 - U+dadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dae0 - U+daff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db00 - U+db1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db20 - U+db3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db40 - U+db5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+db60 - U+db7f */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db80 - U+db9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dba0 - U+dbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dbc0 - U+dbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+dbe0 - U+dbff */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc00 - U+dc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc20 - U+dc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc40 - U+dc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc60 - U+dc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc80 - U+dc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dca0 - U+dcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dcc0 - U+dcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dce0 - U+dcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd00 - U+dd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd20 - U+dd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd40 - U+dd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd60 - U+dd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd80 - U+dd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dda0 - U+ddbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ddc0 - U+dddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dde0 - U+ddff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de00 - U+de1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de20 - U+de3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de40 - U+de5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de60 - U+de7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de80 - U+de9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dea0 - U+debf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dec0 - U+dedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dee0 - U+deff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df00 - U+df1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df20 - U+df3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df40 - U+df5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df60 - U+df7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df80 - U+df9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dfa0 - U+dfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dfc0 - U+dfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+dfe0 - U+dfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e000 - U+e01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e020 - U+e03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e040 - U+e05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e060 - U+e07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e080 - U+e09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0a0 - U+e0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0c0 - U+e0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0e0 - U+e0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e100 - U+e11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e120 - U+e13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e140 - U+e15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e160 - U+e17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e180 - U+e19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1a0 - U+e1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1c0 - U+e1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1e0 - U+e1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e200 - U+e21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e220 - U+e23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e240 - U+e25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e260 - U+e27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e280 - U+e29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2a0 - U+e2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2c0 - U+e2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2e0 - U+e2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e300 - U+e31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e320 - U+e33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e340 - U+e35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e360 - U+e37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e380 - U+e39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3a0 - U+e3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3c0 - U+e3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3e0 - U+e3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e400 - U+e41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e420 - U+e43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e440 - U+e45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e460 - U+e47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e480 - U+e49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4a0 - U+e4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4c0 - U+e4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4e0 - U+e4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e500 - U+e51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e520 - U+e53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e540 - U+e55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e560 - U+e57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e580 - U+e59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5a0 - U+e5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5c0 - U+e5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5e0 - U+e5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e600 - U+e61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e620 - U+e63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e640 - U+e65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e660 - U+e67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e680 - U+e69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6a0 - U+e6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6c0 - U+e6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6e0 - U+e6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e700 - U+e71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e720 - U+e73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e740 - U+e75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e760 - U+e77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e780 - U+e79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7a0 - U+e7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7c0 - U+e7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7e0 - U+e7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e800 - U+e81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e820 - U+e83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e840 - U+e85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e860 - U+e87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e880 - U+e89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8a0 - U+e8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8c0 - U+e8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8e0 - U+e8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e900 - U+e91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e920 - U+e93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e940 - U+e95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e960 - U+e97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e980 - U+e99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9a0 - U+e9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9c0 - U+e9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9e0 - U+e9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea00 - U+ea1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea20 - U+ea3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea40 - U+ea5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea60 - U+ea7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea80 - U+ea9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eaa0 - U+eabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eac0 - U+eadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eae0 - U+eaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb00 - U+eb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb20 - U+eb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb40 - U+eb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb60 - U+eb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb80 - U+eb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eba0 - U+ebbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ebc0 - U+ebdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ebe0 - U+ebff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec00 - U+ec1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec20 - U+ec3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec40 - U+ec5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec60 - U+ec7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec80 - U+ec9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eca0 - U+ecbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ecc0 - U+ecdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ece0 - U+ecff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed00 - U+ed1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed20 - U+ed3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed40 - U+ed5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed60 - U+ed7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed80 - U+ed9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eda0 - U+edbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+edc0 - U+eddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ede0 - U+edff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee00 - U+ee1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee20 - U+ee3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee40 - U+ee5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee60 - U+ee7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee80 - U+ee9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eea0 - U+eebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eec0 - U+eedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eee0 - U+eeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef00 - U+ef1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef20 - U+ef3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef40 - U+ef5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef60 - U+ef7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef80 - U+ef9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efa0 - U+efbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efc0 - U+efdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efe0 - U+efff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f000 - U+f01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f020 - U+f03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f040 - U+f05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f060 - U+f07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f080 - U+f09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0a0 - U+f0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0c0 - U+f0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0e0 - U+f0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f100 - U+f11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f120 - U+f13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f140 - U+f15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f160 - U+f17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f180 - U+f19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1a0 - U+f1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1c0 - U+f1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1e0 - U+f1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f200 - U+f21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f220 - U+f23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f240 - U+f25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f260 - U+f27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f280 - U+f29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2a0 - U+f2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2c0 - U+f2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2e0 - U+f2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f300 - U+f31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f320 - U+f33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f340 - U+f35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f360 - U+f37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f380 - U+f39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3a0 - U+f3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3c0 - U+f3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3e0 - U+f3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f400 - U+f41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f420 - U+f43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f440 - U+f45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f460 - U+f47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f480 - U+f49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4a0 - U+f4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4c0 - U+f4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4e0 - U+f4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f500 - U+f51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f520 - U+f53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f540 - U+f55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f560 - U+f57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f580 - U+f59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5a0 - U+f5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5c0 - U+f5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5e0 - U+f5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f600 - U+f61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f620 - U+f63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f640 - U+f65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f660 - U+f67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f680 - U+f69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6a0 - U+f6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6c0 - U+f6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6e0 - U+f6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f700 - U+f71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f720 - U+f73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f740 - U+f75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f760 - U+f77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f780 - U+f79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7a0 - U+f7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7c0 - U+f7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7e0 - U+f7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f800 - U+f81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f820 - U+f83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f840 - U+f85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f860 - U+f87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f880 - U+f89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8a0 - U+f8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8c0 - U+f8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8e0 - U+f8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f900 - U+f91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f920 - U+f93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f940 - U+f95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f960 - U+f97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f980 - U+f99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9a0 - U+f9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9c0 - U+f9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9e0 - U+f9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa00 - U+fa1f */
+ 0xaa,0xaa,0xaa,0xaf,0xaa,0xaa,0xaa,0xaa, /* U+fa20 - U+fa3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa40 - U+fa5f */
+ 0xaa,0xaa,0xab,0xff,0xaa,0xaa,0xaa,0xaa, /* U+fa60 - U+fa7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa80 - U+fa9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+faa0 - U+fabf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff, /* U+fac0 - U+fadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fae0 - U+faff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf3, /* U+fb00 - U+fb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb20 - U+fb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb40 - U+fb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb60 - U+fb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb80 - U+fb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fba0 - U+fbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fbc0 - U+fbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fbe0 - U+fbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc00 - U+fc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc20 - U+fc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc40 - U+fc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc60 - U+fc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc80 - U+fc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fca0 - U+fcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fcc0 - U+fcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fce0 - U+fcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd00 - U+fd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd20 - U+fd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd40 - U+fd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd60 - U+fd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd80 - U+fd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fda0 - U+fdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fdc0 - U+fddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fde0 - U+fdff */
+ 0x00,0x00,0x00,0x00,0xaa,0xaa,0xaf,0xff, /* U+fe00 - U+fe1f */
+ 0x00,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+fe20 - U+fe3f */
+ 0xaa,0xaa,0xaa,0xaa,0xab,0xaa,0xaa,0xaa, /* U+fe40 - U+fe5f */
+ 0xaa,0xab,0xaa,0xff,0xff,0xff,0xff,0xff, /* U+fe60 - U+fe7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fe80 - U+fe9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fea0 - U+febf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fec0 - U+fedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+fee0 - U+feff */
+ 0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff00 - U+ff1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff20 - U+ff3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff40 - U+ff5f */
+ 0x95,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+ff60 - U+ff7f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+ff80 - U+ff9f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x57, /* U+ffa0 - U+ffbf */
+ 0xf5,0x55,0xf5,0x55,0xf5,0x55,0xf5,0x7f, /* U+ffc0 - U+ffdf */
+ 0xaa,0xab,0x55,0x57,0xff,0xff,0xc0,0xff, /* U+ffe0 - U+ffff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10000 - U+1001f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10020 - U+1003f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10040 - U+1005f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10060 - U+1007f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10080 - U+1009f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100a0 - U+100bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100c0 - U+100df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100e0 - U+100ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10100 - U+1011f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10120 - U+1013f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10140 - U+1015f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10160 - U+1017f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10180 - U+1019f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101a0 - U+101bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101c0 - U+101df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101e0 - U+101ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10200 - U+1021f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10220 - U+1023f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10240 - U+1025f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10260 - U+1027f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10280 - U+1029f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102a0 - U+102bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102c0 - U+102df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102e0 - U+102ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10300 - U+1031f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10320 - U+1033f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10340 - U+1035f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10360 - U+1037f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10380 - U+1039f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103a0 - U+103bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103c0 - U+103df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103e0 - U+103ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10400 - U+1041f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10420 - U+1043f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10440 - U+1045f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10460 - U+1047f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10480 - U+1049f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104a0 - U+104bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104c0 - U+104df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104e0 - U+104ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10500 - U+1051f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10520 - U+1053f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10540 - U+1055f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10560 - U+1057f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10580 - U+1059f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105a0 - U+105bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105c0 - U+105df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105e0 - U+105ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10600 - U+1061f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10620 - U+1063f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10640 - U+1065f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10660 - U+1067f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10680 - U+1069f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106a0 - U+106bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106c0 - U+106df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106e0 - U+106ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10700 - U+1071f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10720 - U+1073f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10740 - U+1075f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10760 - U+1077f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10780 - U+1079f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107a0 - U+107bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107c0 - U+107df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107e0 - U+107ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10800 - U+1081f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10820 - U+1083f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10840 - U+1085f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10860 - U+1087f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10880 - U+1089f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108a0 - U+108bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108c0 - U+108df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108e0 - U+108ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10900 - U+1091f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10920 - U+1093f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10940 - U+1095f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10960 - U+1097f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10980 - U+1099f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109a0 - U+109bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109c0 - U+109df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109e0 - U+109ff */
+ 0xc0,0xc3,0xff,0x00,0xff,0xff,0xff,0xff, /* U+10a00 - U+10a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x03,0xfc, /* U+10a20 - U+10a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a40 - U+10a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a60 - U+10a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a80 - U+10a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10aa0 - U+10abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ac0 - U+10adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ae0 - U+10aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b00 - U+10b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b20 - U+10b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b40 - U+10b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b60 - U+10b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b80 - U+10b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ba0 - U+10bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10bc0 - U+10bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10be0 - U+10bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c00 - U+10c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c20 - U+10c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c40 - U+10c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c60 - U+10c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c80 - U+10c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ca0 - U+10cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10cc0 - U+10cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ce0 - U+10cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d00 - U+10d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d20 - U+10d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d40 - U+10d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d60 - U+10d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d80 - U+10d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10da0 - U+10dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10dc0 - U+10ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10de0 - U+10dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e00 - U+10e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e20 - U+10e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e40 - U+10e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e60 - U+10e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e80 - U+10e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ea0 - U+10ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ec0 - U+10edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ee0 - U+10eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f00 - U+10f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f20 - U+10f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f40 - U+10f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f60 - U+10f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f80 - U+10f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fa0 - U+10fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fc0 - U+10fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fe0 - U+10fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11000 - U+1101f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11020 - U+1103f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11040 - U+1105f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11060 - U+1107f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11080 - U+1109f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110a0 - U+110bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110c0 - U+110df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110e0 - U+110ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11100 - U+1111f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11120 - U+1113f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11140 - U+1115f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11160 - U+1117f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11180 - U+1119f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111a0 - U+111bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111c0 - U+111df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111e0 - U+111ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11200 - U+1121f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11220 - U+1123f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11240 - U+1125f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11260 - U+1127f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11280 - U+1129f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112a0 - U+112bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112c0 - U+112df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112e0 - U+112ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11300 - U+1131f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11320 - U+1133f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11340 - U+1135f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11360 - U+1137f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11380 - U+1139f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113a0 - U+113bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113c0 - U+113df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113e0 - U+113ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11400 - U+1141f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11420 - U+1143f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11440 - U+1145f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11460 - U+1147f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11480 - U+1149f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114a0 - U+114bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114c0 - U+114df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114e0 - U+114ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11500 - U+1151f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11520 - U+1153f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11540 - U+1155f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11560 - U+1157f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11580 - U+1159f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115a0 - U+115bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115c0 - U+115df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115e0 - U+115ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11600 - U+1161f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11620 - U+1163f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11640 - U+1165f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11660 - U+1167f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11680 - U+1169f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116a0 - U+116bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116c0 - U+116df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116e0 - U+116ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11700 - U+1171f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11720 - U+1173f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11740 - U+1175f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11760 - U+1177f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11780 - U+1179f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117a0 - U+117bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117c0 - U+117df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117e0 - U+117ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11800 - U+1181f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11820 - U+1183f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11840 - U+1185f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11860 - U+1187f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11880 - U+1189f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118a0 - U+118bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118c0 - U+118df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118e0 - U+118ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11900 - U+1191f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11920 - U+1193f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11940 - U+1195f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11960 - U+1197f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11980 - U+1199f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119a0 - U+119bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119c0 - U+119df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119e0 - U+119ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a00 - U+11a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a20 - U+11a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a40 - U+11a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a60 - U+11a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a80 - U+11a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11aa0 - U+11abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ac0 - U+11adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ae0 - U+11aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b00 - U+11b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b20 - U+11b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b40 - U+11b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b60 - U+11b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b80 - U+11b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ba0 - U+11bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11bc0 - U+11bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11be0 - U+11bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c00 - U+11c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c20 - U+11c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c40 - U+11c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c60 - U+11c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c80 - U+11c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ca0 - U+11cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11cc0 - U+11cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ce0 - U+11cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d00 - U+11d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d20 - U+11d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d40 - U+11d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d60 - U+11d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d80 - U+11d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11da0 - U+11dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11dc0 - U+11ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11de0 - U+11dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e00 - U+11e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e20 - U+11e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e40 - U+11e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e60 - U+11e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e80 - U+11e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ea0 - U+11ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ec0 - U+11edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ee0 - U+11eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f00 - U+11f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f20 - U+11f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f40 - U+11f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f60 - U+11f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f80 - U+11f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fa0 - U+11fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fc0 - U+11fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fe0 - U+11fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12000 - U+1201f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12020 - U+1203f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12040 - U+1205f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12060 - U+1207f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12080 - U+1209f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120a0 - U+120bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120c0 - U+120df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120e0 - U+120ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12100 - U+1211f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12120 - U+1213f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12140 - U+1215f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12160 - U+1217f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12180 - U+1219f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121a0 - U+121bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121c0 - U+121df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121e0 - U+121ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12200 - U+1221f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12220 - U+1223f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12240 - U+1225f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12260 - U+1227f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12280 - U+1229f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122a0 - U+122bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122c0 - U+122df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122e0 - U+122ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12300 - U+1231f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12320 - U+1233f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12340 - U+1235f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12360 - U+1237f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12380 - U+1239f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123a0 - U+123bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123c0 - U+123df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123e0 - U+123ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12400 - U+1241f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12420 - U+1243f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12440 - U+1245f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12460 - U+1247f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12480 - U+1249f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124a0 - U+124bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124c0 - U+124df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124e0 - U+124ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12500 - U+1251f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12520 - U+1253f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12540 - U+1255f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12560 - U+1257f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12580 - U+1259f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125a0 - U+125bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125c0 - U+125df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125e0 - U+125ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12600 - U+1261f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12620 - U+1263f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12640 - U+1265f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12660 - U+1267f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12680 - U+1269f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126a0 - U+126bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126c0 - U+126df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126e0 - U+126ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12700 - U+1271f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12720 - U+1273f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12740 - U+1275f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12760 - U+1277f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12780 - U+1279f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127a0 - U+127bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127c0 - U+127df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127e0 - U+127ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12800 - U+1281f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12820 - U+1283f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12840 - U+1285f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12860 - U+1287f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12880 - U+1289f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128a0 - U+128bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128c0 - U+128df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128e0 - U+128ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12900 - U+1291f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12920 - U+1293f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12940 - U+1295f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12960 - U+1297f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12980 - U+1299f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129a0 - U+129bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129c0 - U+129df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129e0 - U+129ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a00 - U+12a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a20 - U+12a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a40 - U+12a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a60 - U+12a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a80 - U+12a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12aa0 - U+12abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ac0 - U+12adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ae0 - U+12aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b00 - U+12b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b20 - U+12b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b40 - U+12b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b60 - U+12b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b80 - U+12b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ba0 - U+12bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12bc0 - U+12bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12be0 - U+12bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c00 - U+12c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c20 - U+12c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c40 - U+12c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c60 - U+12c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c80 - U+12c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ca0 - U+12cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12cc0 - U+12cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ce0 - U+12cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d00 - U+12d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d20 - U+12d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d40 - U+12d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d60 - U+12d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d80 - U+12d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12da0 - U+12dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12dc0 - U+12ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12de0 - U+12dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e00 - U+12e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e20 - U+12e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e40 - U+12e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e60 - U+12e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e80 - U+12e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ea0 - U+12ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ec0 - U+12edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ee0 - U+12eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f00 - U+12f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f20 - U+12f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f40 - U+12f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f60 - U+12f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f80 - U+12f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fa0 - U+12fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fc0 - U+12fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fe0 - U+12fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13000 - U+1301f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13020 - U+1303f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13040 - U+1305f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13060 - U+1307f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13080 - U+1309f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130a0 - U+130bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130c0 - U+130df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130e0 - U+130ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13100 - U+1311f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13120 - U+1313f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13140 - U+1315f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13160 - U+1317f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13180 - U+1319f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131a0 - U+131bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131c0 - U+131df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131e0 - U+131ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13200 - U+1321f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13220 - U+1323f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13240 - U+1325f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13260 - U+1327f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13280 - U+1329f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132a0 - U+132bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132c0 - U+132df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132e0 - U+132ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13300 - U+1331f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13320 - U+1333f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13340 - U+1335f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13360 - U+1337f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13380 - U+1339f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133a0 - U+133bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133c0 - U+133df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133e0 - U+133ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13400 - U+1341f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13420 - U+1343f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13440 - U+1345f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13460 - U+1347f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13480 - U+1349f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134a0 - U+134bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134c0 - U+134df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134e0 - U+134ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13500 - U+1351f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13520 - U+1353f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13540 - U+1355f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13560 - U+1357f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13580 - U+1359f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135a0 - U+135bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135c0 - U+135df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135e0 - U+135ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13600 - U+1361f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13620 - U+1363f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13640 - U+1365f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13660 - U+1367f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13680 - U+1369f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136a0 - U+136bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136c0 - U+136df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136e0 - U+136ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13700 - U+1371f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13720 - U+1373f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13740 - U+1375f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13760 - U+1377f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13780 - U+1379f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137a0 - U+137bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137c0 - U+137df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137e0 - U+137ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13800 - U+1381f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13820 - U+1383f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13840 - U+1385f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13860 - U+1387f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13880 - U+1389f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138a0 - U+138bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138c0 - U+138df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138e0 - U+138ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13900 - U+1391f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13920 - U+1393f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13940 - U+1395f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13960 - U+1397f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13980 - U+1399f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139a0 - U+139bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139c0 - U+139df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139e0 - U+139ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a00 - U+13a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a20 - U+13a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a40 - U+13a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a60 - U+13a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a80 - U+13a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13aa0 - U+13abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ac0 - U+13adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ae0 - U+13aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b00 - U+13b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b20 - U+13b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b40 - U+13b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b60 - U+13b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b80 - U+13b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ba0 - U+13bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13bc0 - U+13bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13be0 - U+13bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c00 - U+13c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c20 - U+13c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c40 - U+13c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c60 - U+13c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c80 - U+13c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ca0 - U+13cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13cc0 - U+13cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ce0 - U+13cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d00 - U+13d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d20 - U+13d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d40 - U+13d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d60 - U+13d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d80 - U+13d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13da0 - U+13dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13dc0 - U+13ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13de0 - U+13dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e00 - U+13e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e20 - U+13e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e40 - U+13e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e60 - U+13e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e80 - U+13e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ea0 - U+13ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ec0 - U+13edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ee0 - U+13eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f00 - U+13f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f20 - U+13f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f40 - U+13f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f60 - U+13f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f80 - U+13f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fa0 - U+13fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fc0 - U+13fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fe0 - U+13fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14000 - U+1401f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14020 - U+1403f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14040 - U+1405f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14060 - U+1407f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14080 - U+1409f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140a0 - U+140bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140c0 - U+140df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140e0 - U+140ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14100 - U+1411f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14120 - U+1413f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14140 - U+1415f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14160 - U+1417f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14180 - U+1419f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141a0 - U+141bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141c0 - U+141df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141e0 - U+141ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14200 - U+1421f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14220 - U+1423f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14240 - U+1425f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14260 - U+1427f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14280 - U+1429f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142a0 - U+142bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142c0 - U+142df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142e0 - U+142ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14300 - U+1431f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14320 - U+1433f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14340 - U+1435f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14360 - U+1437f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14380 - U+1439f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143a0 - U+143bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143c0 - U+143df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143e0 - U+143ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14400 - U+1441f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14420 - U+1443f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14440 - U+1445f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14460 - U+1447f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14480 - U+1449f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144a0 - U+144bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144c0 - U+144df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144e0 - U+144ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14500 - U+1451f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14520 - U+1453f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14540 - U+1455f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14560 - U+1457f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14580 - U+1459f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145a0 - U+145bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145c0 - U+145df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145e0 - U+145ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14600 - U+1461f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14620 - U+1463f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14640 - U+1465f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14660 - U+1467f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14680 - U+1469f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146a0 - U+146bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146c0 - U+146df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146e0 - U+146ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14700 - U+1471f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14720 - U+1473f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14740 - U+1475f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14760 - U+1477f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14780 - U+1479f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147a0 - U+147bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147c0 - U+147df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147e0 - U+147ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14800 - U+1481f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14820 - U+1483f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14840 - U+1485f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14860 - U+1487f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14880 - U+1489f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148a0 - U+148bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148c0 - U+148df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148e0 - U+148ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14900 - U+1491f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14920 - U+1493f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14940 - U+1495f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14960 - U+1497f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14980 - U+1499f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149a0 - U+149bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149c0 - U+149df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149e0 - U+149ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a00 - U+14a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a20 - U+14a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a40 - U+14a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a60 - U+14a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a80 - U+14a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14aa0 - U+14abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ac0 - U+14adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ae0 - U+14aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b00 - U+14b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b20 - U+14b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b40 - U+14b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b60 - U+14b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b80 - U+14b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ba0 - U+14bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14bc0 - U+14bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14be0 - U+14bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c00 - U+14c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c20 - U+14c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c40 - U+14c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c60 - U+14c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c80 - U+14c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ca0 - U+14cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14cc0 - U+14cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ce0 - U+14cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d00 - U+14d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d20 - U+14d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d40 - U+14d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d60 - U+14d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d80 - U+14d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14da0 - U+14dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14dc0 - U+14ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14de0 - U+14dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e00 - U+14e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e20 - U+14e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e40 - U+14e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e60 - U+14e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e80 - U+14e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ea0 - U+14ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ec0 - U+14edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ee0 - U+14eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f00 - U+14f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f20 - U+14f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f40 - U+14f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f60 - U+14f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f80 - U+14f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fa0 - U+14fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fc0 - U+14fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fe0 - U+14fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15000 - U+1501f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15020 - U+1503f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15040 - U+1505f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15060 - U+1507f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15080 - U+1509f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150a0 - U+150bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150c0 - U+150df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150e0 - U+150ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15100 - U+1511f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15120 - U+1513f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15140 - U+1515f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15160 - U+1517f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15180 - U+1519f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151a0 - U+151bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151c0 - U+151df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151e0 - U+151ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15200 - U+1521f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15220 - U+1523f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15240 - U+1525f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15260 - U+1527f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15280 - U+1529f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152a0 - U+152bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152c0 - U+152df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152e0 - U+152ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15300 - U+1531f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15320 - U+1533f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15340 - U+1535f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15360 - U+1537f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15380 - U+1539f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153a0 - U+153bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153c0 - U+153df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153e0 - U+153ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15400 - U+1541f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15420 - U+1543f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15440 - U+1545f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15460 - U+1547f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15480 - U+1549f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154a0 - U+154bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154c0 - U+154df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154e0 - U+154ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15500 - U+1551f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15520 - U+1553f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15540 - U+1555f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15560 - U+1557f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15580 - U+1559f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155a0 - U+155bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155c0 - U+155df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155e0 - U+155ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15600 - U+1561f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15620 - U+1563f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15640 - U+1565f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15660 - U+1567f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15680 - U+1569f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156a0 - U+156bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156c0 - U+156df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156e0 - U+156ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15700 - U+1571f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15720 - U+1573f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15740 - U+1575f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15760 - U+1577f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15780 - U+1579f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157a0 - U+157bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157c0 - U+157df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157e0 - U+157ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15800 - U+1581f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15820 - U+1583f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15840 - U+1585f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15860 - U+1587f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15880 - U+1589f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158a0 - U+158bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158c0 - U+158df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158e0 - U+158ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15900 - U+1591f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15920 - U+1593f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15940 - U+1595f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15960 - U+1597f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15980 - U+1599f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159a0 - U+159bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159c0 - U+159df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159e0 - U+159ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a00 - U+15a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a20 - U+15a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a40 - U+15a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a60 - U+15a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a80 - U+15a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15aa0 - U+15abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ac0 - U+15adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ae0 - U+15aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b00 - U+15b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b20 - U+15b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b40 - U+15b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b60 - U+15b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b80 - U+15b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ba0 - U+15bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15bc0 - U+15bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15be0 - U+15bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c00 - U+15c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c20 - U+15c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c40 - U+15c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c60 - U+15c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c80 - U+15c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ca0 - U+15cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15cc0 - U+15cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ce0 - U+15cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d00 - U+15d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d20 - U+15d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d40 - U+15d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d60 - U+15d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d80 - U+15d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15da0 - U+15dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15dc0 - U+15ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15de0 - U+15dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e00 - U+15e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e20 - U+15e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e40 - U+15e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e60 - U+15e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e80 - U+15e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ea0 - U+15ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ec0 - U+15edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ee0 - U+15eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f00 - U+15f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f20 - U+15f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f40 - U+15f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f60 - U+15f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f80 - U+15f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fa0 - U+15fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fc0 - U+15fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fe0 - U+15fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16000 - U+1601f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16020 - U+1603f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16040 - U+1605f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16060 - U+1607f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16080 - U+1609f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160a0 - U+160bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160c0 - U+160df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160e0 - U+160ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16100 - U+1611f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16120 - U+1613f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16140 - U+1615f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16160 - U+1617f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16180 - U+1619f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161a0 - U+161bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161c0 - U+161df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161e0 - U+161ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16200 - U+1621f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16220 - U+1623f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16240 - U+1625f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16260 - U+1627f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16280 - U+1629f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162a0 - U+162bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162c0 - U+162df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162e0 - U+162ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16300 - U+1631f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16320 - U+1633f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16340 - U+1635f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16360 - U+1637f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16380 - U+1639f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163a0 - U+163bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163c0 - U+163df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163e0 - U+163ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16400 - U+1641f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16420 - U+1643f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16440 - U+1645f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16460 - U+1647f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16480 - U+1649f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164a0 - U+164bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164c0 - U+164df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164e0 - U+164ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16500 - U+1651f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16520 - U+1653f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16540 - U+1655f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16560 - U+1657f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16580 - U+1659f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165a0 - U+165bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165c0 - U+165df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165e0 - U+165ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16600 - U+1661f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16620 - U+1663f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16640 - U+1665f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16660 - U+1667f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16680 - U+1669f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166a0 - U+166bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166c0 - U+166df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166e0 - U+166ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16700 - U+1671f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16720 - U+1673f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16740 - U+1675f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16760 - U+1677f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16780 - U+1679f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167a0 - U+167bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167c0 - U+167df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167e0 - U+167ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16800 - U+1681f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16820 - U+1683f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16840 - U+1685f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16860 - U+1687f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16880 - U+1689f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168a0 - U+168bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168c0 - U+168df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168e0 - U+168ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16900 - U+1691f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16920 - U+1693f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16940 - U+1695f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16960 - U+1697f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16980 - U+1699f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169a0 - U+169bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169c0 - U+169df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169e0 - U+169ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a00 - U+16a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a20 - U+16a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a40 - U+16a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a60 - U+16a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a80 - U+16a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16aa0 - U+16abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ac0 - U+16adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ae0 - U+16aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b00 - U+16b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b20 - U+16b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b40 - U+16b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b60 - U+16b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b80 - U+16b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ba0 - U+16bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16bc0 - U+16bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16be0 - U+16bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c00 - U+16c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c20 - U+16c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c40 - U+16c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c60 - U+16c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c80 - U+16c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ca0 - U+16cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16cc0 - U+16cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ce0 - U+16cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d00 - U+16d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d20 - U+16d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d40 - U+16d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d60 - U+16d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d80 - U+16d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16da0 - U+16dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16dc0 - U+16ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16de0 - U+16dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e00 - U+16e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e20 - U+16e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e40 - U+16e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e60 - U+16e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e80 - U+16e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ea0 - U+16ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ec0 - U+16edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ee0 - U+16eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f00 - U+16f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f20 - U+16f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f40 - U+16f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f60 - U+16f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f80 - U+16f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fa0 - U+16fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fc0 - U+16fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fe0 - U+16fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17000 - U+1701f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17020 - U+1703f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17040 - U+1705f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17060 - U+1707f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17080 - U+1709f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170a0 - U+170bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170c0 - U+170df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170e0 - U+170ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17100 - U+1711f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17120 - U+1713f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17140 - U+1715f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17160 - U+1717f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17180 - U+1719f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171a0 - U+171bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171c0 - U+171df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171e0 - U+171ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17200 - U+1721f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17220 - U+1723f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17240 - U+1725f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17260 - U+1727f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17280 - U+1729f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172a0 - U+172bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172c0 - U+172df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172e0 - U+172ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17300 - U+1731f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17320 - U+1733f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17340 - U+1735f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17360 - U+1737f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17380 - U+1739f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173a0 - U+173bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173c0 - U+173df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173e0 - U+173ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17400 - U+1741f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17420 - U+1743f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17440 - U+1745f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17460 - U+1747f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17480 - U+1749f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174a0 - U+174bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174c0 - U+174df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174e0 - U+174ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17500 - U+1751f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17520 - U+1753f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17540 - U+1755f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17560 - U+1757f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17580 - U+1759f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175a0 - U+175bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175c0 - U+175df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175e0 - U+175ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17600 - U+1761f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17620 - U+1763f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17640 - U+1765f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17660 - U+1767f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17680 - U+1769f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176a0 - U+176bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176c0 - U+176df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176e0 - U+176ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17700 - U+1771f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17720 - U+1773f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17740 - U+1775f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17760 - U+1777f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17780 - U+1779f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177a0 - U+177bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177c0 - U+177df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177e0 - U+177ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17800 - U+1781f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17820 - U+1783f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17840 - U+1785f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17860 - U+1787f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17880 - U+1789f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178a0 - U+178bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178c0 - U+178df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178e0 - U+178ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17900 - U+1791f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17920 - U+1793f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17940 - U+1795f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17960 - U+1797f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17980 - U+1799f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179a0 - U+179bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179c0 - U+179df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179e0 - U+179ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a00 - U+17a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a20 - U+17a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a40 - U+17a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a60 - U+17a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a80 - U+17a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17aa0 - U+17abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ac0 - U+17adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ae0 - U+17aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b00 - U+17b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b20 - U+17b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b40 - U+17b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b60 - U+17b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b80 - U+17b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ba0 - U+17bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17bc0 - U+17bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17be0 - U+17bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c00 - U+17c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c20 - U+17c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c40 - U+17c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c60 - U+17c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c80 - U+17c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ca0 - U+17cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17cc0 - U+17cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ce0 - U+17cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d00 - U+17d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d20 - U+17d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d40 - U+17d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d60 - U+17d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d80 - U+17d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17da0 - U+17dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17dc0 - U+17ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17de0 - U+17dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e00 - U+17e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e20 - U+17e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e40 - U+17e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e60 - U+17e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e80 - U+17e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ea0 - U+17ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ec0 - U+17edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ee0 - U+17eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f00 - U+17f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f20 - U+17f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f40 - U+17f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f60 - U+17f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f80 - U+17f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fa0 - U+17fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fc0 - U+17fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fe0 - U+17fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18000 - U+1801f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18020 - U+1803f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18040 - U+1805f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18060 - U+1807f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18080 - U+1809f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180a0 - U+180bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180c0 - U+180df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180e0 - U+180ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18100 - U+1811f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18120 - U+1813f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18140 - U+1815f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18160 - U+1817f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18180 - U+1819f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181a0 - U+181bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181c0 - U+181df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181e0 - U+181ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18200 - U+1821f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18220 - U+1823f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18240 - U+1825f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18260 - U+1827f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18280 - U+1829f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182a0 - U+182bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182c0 - U+182df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182e0 - U+182ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18300 - U+1831f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18320 - U+1833f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18340 - U+1835f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18360 - U+1837f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18380 - U+1839f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183a0 - U+183bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183c0 - U+183df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183e0 - U+183ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18400 - U+1841f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18420 - U+1843f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18440 - U+1845f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18460 - U+1847f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18480 - U+1849f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184a0 - U+184bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184c0 - U+184df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184e0 - U+184ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18500 - U+1851f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18520 - U+1853f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18540 - U+1855f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18560 - U+1857f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18580 - U+1859f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185a0 - U+185bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185c0 - U+185df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185e0 - U+185ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18600 - U+1861f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18620 - U+1863f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18640 - U+1865f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18660 - U+1867f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18680 - U+1869f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186a0 - U+186bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186c0 - U+186df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186e0 - U+186ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18700 - U+1871f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18720 - U+1873f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18740 - U+1875f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18760 - U+1877f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18780 - U+1879f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187a0 - U+187bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187c0 - U+187df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187e0 - U+187ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18800 - U+1881f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18820 - U+1883f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18840 - U+1885f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18860 - U+1887f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18880 - U+1889f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188a0 - U+188bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188c0 - U+188df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188e0 - U+188ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18900 - U+1891f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18920 - U+1893f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18940 - U+1895f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18960 - U+1897f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18980 - U+1899f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189a0 - U+189bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189c0 - U+189df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189e0 - U+189ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a00 - U+18a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a20 - U+18a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a40 - U+18a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a60 - U+18a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a80 - U+18a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18aa0 - U+18abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ac0 - U+18adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ae0 - U+18aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b00 - U+18b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b20 - U+18b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b40 - U+18b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b60 - U+18b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b80 - U+18b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ba0 - U+18bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18bc0 - U+18bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18be0 - U+18bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c00 - U+18c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c20 - U+18c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c40 - U+18c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c60 - U+18c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c80 - U+18c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ca0 - U+18cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18cc0 - U+18cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ce0 - U+18cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d00 - U+18d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d20 - U+18d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d40 - U+18d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d60 - U+18d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d80 - U+18d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18da0 - U+18dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18dc0 - U+18ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18de0 - U+18dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e00 - U+18e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e20 - U+18e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e40 - U+18e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e60 - U+18e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e80 - U+18e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ea0 - U+18ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ec0 - U+18edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ee0 - U+18eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f00 - U+18f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f20 - U+18f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f40 - U+18f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f60 - U+18f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f80 - U+18f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fa0 - U+18fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fc0 - U+18fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fe0 - U+18fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19000 - U+1901f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19020 - U+1903f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19040 - U+1905f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19060 - U+1907f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19080 - U+1909f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190a0 - U+190bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190c0 - U+190df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190e0 - U+190ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19100 - U+1911f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19120 - U+1913f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19140 - U+1915f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19160 - U+1917f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19180 - U+1919f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191a0 - U+191bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191c0 - U+191df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191e0 - U+191ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19200 - U+1921f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19220 - U+1923f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19240 - U+1925f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19260 - U+1927f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19280 - U+1929f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192a0 - U+192bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192c0 - U+192df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192e0 - U+192ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19300 - U+1931f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19320 - U+1933f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19340 - U+1935f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19360 - U+1937f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19380 - U+1939f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193a0 - U+193bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193c0 - U+193df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193e0 - U+193ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19400 - U+1941f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19420 - U+1943f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19440 - U+1945f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19460 - U+1947f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19480 - U+1949f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194a0 - U+194bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194c0 - U+194df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194e0 - U+194ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19500 - U+1951f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19520 - U+1953f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19540 - U+1955f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19560 - U+1957f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19580 - U+1959f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195a0 - U+195bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195c0 - U+195df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195e0 - U+195ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19600 - U+1961f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19620 - U+1963f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19640 - U+1965f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19660 - U+1967f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19680 - U+1969f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196a0 - U+196bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196c0 - U+196df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196e0 - U+196ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19700 - U+1971f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19720 - U+1973f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19740 - U+1975f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19760 - U+1977f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19780 - U+1979f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197a0 - U+197bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197c0 - U+197df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197e0 - U+197ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19800 - U+1981f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19820 - U+1983f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19840 - U+1985f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19860 - U+1987f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19880 - U+1989f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198a0 - U+198bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198c0 - U+198df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198e0 - U+198ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19900 - U+1991f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19920 - U+1993f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19940 - U+1995f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19960 - U+1997f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19980 - U+1999f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199a0 - U+199bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199c0 - U+199df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199e0 - U+199ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a00 - U+19a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a20 - U+19a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a40 - U+19a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a60 - U+19a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a80 - U+19a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19aa0 - U+19abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ac0 - U+19adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ae0 - U+19aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b00 - U+19b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b20 - U+19b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b40 - U+19b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b60 - U+19b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b80 - U+19b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ba0 - U+19bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19bc0 - U+19bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19be0 - U+19bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c00 - U+19c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c20 - U+19c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c40 - U+19c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c60 - U+19c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c80 - U+19c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ca0 - U+19cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19cc0 - U+19cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ce0 - U+19cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d00 - U+19d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d20 - U+19d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d40 - U+19d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d60 - U+19d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d80 - U+19d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19da0 - U+19dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19dc0 - U+19ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19de0 - U+19dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e00 - U+19e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e20 - U+19e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e40 - U+19e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e60 - U+19e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e80 - U+19e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ea0 - U+19ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ec0 - U+19edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ee0 - U+19eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f00 - U+19f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f20 - U+19f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f40 - U+19f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f60 - U+19f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f80 - U+19f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fa0 - U+19fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fc0 - U+19fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fe0 - U+19fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a000 - U+1a01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a020 - U+1a03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a040 - U+1a05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a060 - U+1a07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a080 - U+1a09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0a0 - U+1a0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0c0 - U+1a0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0e0 - U+1a0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a100 - U+1a11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a120 - U+1a13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a140 - U+1a15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a160 - U+1a17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a180 - U+1a19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1a0 - U+1a1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1c0 - U+1a1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1e0 - U+1a1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a200 - U+1a21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a220 - U+1a23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a240 - U+1a25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a260 - U+1a27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a280 - U+1a29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2a0 - U+1a2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2c0 - U+1a2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2e0 - U+1a2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a300 - U+1a31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a320 - U+1a33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a340 - U+1a35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a360 - U+1a37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a380 - U+1a39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3a0 - U+1a3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3c0 - U+1a3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3e0 - U+1a3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a400 - U+1a41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a420 - U+1a43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a440 - U+1a45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a460 - U+1a47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a480 - U+1a49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4a0 - U+1a4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4c0 - U+1a4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4e0 - U+1a4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a500 - U+1a51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a520 - U+1a53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a540 - U+1a55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a560 - U+1a57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a580 - U+1a59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5a0 - U+1a5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5c0 - U+1a5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5e0 - U+1a5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a600 - U+1a61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a620 - U+1a63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a640 - U+1a65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a660 - U+1a67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a680 - U+1a69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6a0 - U+1a6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6c0 - U+1a6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6e0 - U+1a6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a700 - U+1a71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a720 - U+1a73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a740 - U+1a75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a760 - U+1a77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a780 - U+1a79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7a0 - U+1a7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7c0 - U+1a7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7e0 - U+1a7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a800 - U+1a81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a820 - U+1a83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a840 - U+1a85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a860 - U+1a87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a880 - U+1a89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8a0 - U+1a8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8c0 - U+1a8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8e0 - U+1a8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a900 - U+1a91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a920 - U+1a93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a940 - U+1a95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a960 - U+1a97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a980 - U+1a99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9a0 - U+1a9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9c0 - U+1a9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9e0 - U+1a9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa00 - U+1aa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa20 - U+1aa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa40 - U+1aa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa60 - U+1aa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa80 - U+1aa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aaa0 - U+1aabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aac0 - U+1aadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aae0 - U+1aaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab00 - U+1ab1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab20 - U+1ab3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab40 - U+1ab5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab60 - U+1ab7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab80 - U+1ab9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aba0 - U+1abbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1abc0 - U+1abdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1abe0 - U+1abff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac00 - U+1ac1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac20 - U+1ac3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac40 - U+1ac5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac60 - U+1ac7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac80 - U+1ac9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aca0 - U+1acbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1acc0 - U+1acdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ace0 - U+1acff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad00 - U+1ad1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad20 - U+1ad3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad40 - U+1ad5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad60 - U+1ad7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad80 - U+1ad9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ada0 - U+1adbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1adc0 - U+1addf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ade0 - U+1adff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae00 - U+1ae1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae20 - U+1ae3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae40 - U+1ae5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae60 - U+1ae7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae80 - U+1ae9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aea0 - U+1aebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aec0 - U+1aedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aee0 - U+1aeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af00 - U+1af1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af20 - U+1af3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af40 - U+1af5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af60 - U+1af7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af80 - U+1af9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afa0 - U+1afbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afc0 - U+1afdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afe0 - U+1afff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b000 - U+1b01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b020 - U+1b03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b040 - U+1b05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b060 - U+1b07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b080 - U+1b09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0a0 - U+1b0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0c0 - U+1b0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0e0 - U+1b0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b100 - U+1b11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b120 - U+1b13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b140 - U+1b15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b160 - U+1b17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b180 - U+1b19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1a0 - U+1b1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1c0 - U+1b1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1e0 - U+1b1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b200 - U+1b21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b220 - U+1b23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b240 - U+1b25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b260 - U+1b27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b280 - U+1b29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2a0 - U+1b2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2c0 - U+1b2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2e0 - U+1b2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b300 - U+1b31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b320 - U+1b33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b340 - U+1b35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b360 - U+1b37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b380 - U+1b39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3a0 - U+1b3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3c0 - U+1b3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3e0 - U+1b3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b400 - U+1b41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b420 - U+1b43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b440 - U+1b45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b460 - U+1b47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b480 - U+1b49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4a0 - U+1b4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4c0 - U+1b4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4e0 - U+1b4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b500 - U+1b51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b520 - U+1b53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b540 - U+1b55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b560 - U+1b57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b580 - U+1b59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5a0 - U+1b5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5c0 - U+1b5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5e0 - U+1b5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b600 - U+1b61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b620 - U+1b63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b640 - U+1b65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b660 - U+1b67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b680 - U+1b69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6a0 - U+1b6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6c0 - U+1b6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6e0 - U+1b6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b700 - U+1b71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b720 - U+1b73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b740 - U+1b75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b760 - U+1b77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b780 - U+1b79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7a0 - U+1b7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7c0 - U+1b7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7e0 - U+1b7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b800 - U+1b81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b820 - U+1b83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b840 - U+1b85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b860 - U+1b87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b880 - U+1b89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8a0 - U+1b8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8c0 - U+1b8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8e0 - U+1b8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b900 - U+1b91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b920 - U+1b93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b940 - U+1b95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b960 - U+1b97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b980 - U+1b99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9a0 - U+1b9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9c0 - U+1b9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9e0 - U+1b9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba00 - U+1ba1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba20 - U+1ba3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba40 - U+1ba5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba60 - U+1ba7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba80 - U+1ba9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1baa0 - U+1babf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bac0 - U+1badf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bae0 - U+1baff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb00 - U+1bb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb20 - U+1bb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb40 - U+1bb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb60 - U+1bb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb80 - U+1bb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bba0 - U+1bbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bbc0 - U+1bbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bbe0 - U+1bbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc00 - U+1bc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc20 - U+1bc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc40 - U+1bc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc60 - U+1bc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc80 - U+1bc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bca0 - U+1bcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bcc0 - U+1bcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bce0 - U+1bcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd00 - U+1bd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd20 - U+1bd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd40 - U+1bd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd60 - U+1bd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd80 - U+1bd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bda0 - U+1bdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bdc0 - U+1bddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bde0 - U+1bdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be00 - U+1be1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be20 - U+1be3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be40 - U+1be5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be60 - U+1be7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be80 - U+1be9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bea0 - U+1bebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bec0 - U+1bedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bee0 - U+1beff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf00 - U+1bf1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf20 - U+1bf3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf40 - U+1bf5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf60 - U+1bf7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf80 - U+1bf9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfa0 - U+1bfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfc0 - U+1bfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfe0 - U+1bfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c000 - U+1c01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c020 - U+1c03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c040 - U+1c05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c060 - U+1c07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c080 - U+1c09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0a0 - U+1c0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0c0 - U+1c0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0e0 - U+1c0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c100 - U+1c11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c120 - U+1c13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c140 - U+1c15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c160 - U+1c17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c180 - U+1c19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1a0 - U+1c1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1c0 - U+1c1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1e0 - U+1c1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c200 - U+1c21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c220 - U+1c23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c240 - U+1c25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c260 - U+1c27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c280 - U+1c29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2a0 - U+1c2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2c0 - U+1c2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2e0 - U+1c2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c300 - U+1c31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c320 - U+1c33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c340 - U+1c35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c360 - U+1c37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c380 - U+1c39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3a0 - U+1c3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3c0 - U+1c3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3e0 - U+1c3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c400 - U+1c41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c420 - U+1c43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c440 - U+1c45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c460 - U+1c47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c480 - U+1c49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4a0 - U+1c4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4c0 - U+1c4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4e0 - U+1c4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c500 - U+1c51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c520 - U+1c53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c540 - U+1c55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c560 - U+1c57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c580 - U+1c59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5a0 - U+1c5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5c0 - U+1c5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5e0 - U+1c5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c600 - U+1c61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c620 - U+1c63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c640 - U+1c65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c660 - U+1c67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c680 - U+1c69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6a0 - U+1c6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6c0 - U+1c6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6e0 - U+1c6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c700 - U+1c71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c720 - U+1c73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c740 - U+1c75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c760 - U+1c77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c780 - U+1c79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7a0 - U+1c7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7c0 - U+1c7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7e0 - U+1c7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c800 - U+1c81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c820 - U+1c83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c840 - U+1c85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c860 - U+1c87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c880 - U+1c89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8a0 - U+1c8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8c0 - U+1c8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8e0 - U+1c8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c900 - U+1c91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c920 - U+1c93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c940 - U+1c95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c960 - U+1c97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c980 - U+1c99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9a0 - U+1c9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9c0 - U+1c9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9e0 - U+1c9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca00 - U+1ca1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca20 - U+1ca3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca40 - U+1ca5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca60 - U+1ca7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca80 - U+1ca9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1caa0 - U+1cabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cac0 - U+1cadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cae0 - U+1caff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb00 - U+1cb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb20 - U+1cb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb40 - U+1cb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb60 - U+1cb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb80 - U+1cb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cba0 - U+1cbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cbc0 - U+1cbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cbe0 - U+1cbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc00 - U+1cc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc20 - U+1cc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc40 - U+1cc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc60 - U+1cc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc80 - U+1cc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cca0 - U+1ccbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ccc0 - U+1ccdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cce0 - U+1ccff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd00 - U+1cd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd20 - U+1cd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd40 - U+1cd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd60 - U+1cd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd80 - U+1cd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cda0 - U+1cdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cdc0 - U+1cddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cde0 - U+1cdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce00 - U+1ce1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce20 - U+1ce3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce40 - U+1ce5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce60 - U+1ce7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce80 - U+1ce9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cea0 - U+1cebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cec0 - U+1cedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cee0 - U+1ceff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf00 - U+1cf1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf20 - U+1cf3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf40 - U+1cf5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf60 - U+1cf7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf80 - U+1cf9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfa0 - U+1cfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfc0 - U+1cfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfe0 - U+1cfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d000 - U+1d01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d020 - U+1d03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d040 - U+1d05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d060 - U+1d07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d080 - U+1d09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0a0 - U+1d0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0c0 - U+1d0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0e0 - U+1d0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d100 - U+1d11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d120 - U+1d13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d140 - U+1d15f */
+ 0xff,0xfc,0x0f,0xff,0xfc,0x00,0x00,0x00, /* U+1d160 - U+1d17f */
+ 0x03,0xc0,0x00,0xff,0xff,0xff,0xff,0xff, /* U+1d180 - U+1d19f */
+ 0xff,0xff,0xf0,0x0f,0xff,0xff,0xff,0xff, /* U+1d1a0 - U+1d1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d1c0 - U+1d1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d1e0 - U+1d1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d200 - U+1d21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d220 - U+1d23f */
+ 0xf0,0x3f,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d240 - U+1d25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d260 - U+1d27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d280 - U+1d29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2a0 - U+1d2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2c0 - U+1d2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2e0 - U+1d2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d300 - U+1d31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d320 - U+1d33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d340 - U+1d35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d360 - U+1d37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d380 - U+1d39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3a0 - U+1d3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3c0 - U+1d3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3e0 - U+1d3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d400 - U+1d41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d420 - U+1d43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d440 - U+1d45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d460 - U+1d47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d480 - U+1d49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4a0 - U+1d4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4c0 - U+1d4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4e0 - U+1d4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d500 - U+1d51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d520 - U+1d53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d540 - U+1d55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d560 - U+1d57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d580 - U+1d59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5a0 - U+1d5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5c0 - U+1d5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5e0 - U+1d5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d600 - U+1d61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d620 - U+1d63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d640 - U+1d65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d660 - U+1d67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d680 - U+1d69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6a0 - U+1d6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6c0 - U+1d6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6e0 - U+1d6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d700 - U+1d71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d720 - U+1d73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d740 - U+1d75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d760 - U+1d77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d780 - U+1d79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7a0 - U+1d7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7c0 - U+1d7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7e0 - U+1d7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d800 - U+1d81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d820 - U+1d83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d840 - U+1d85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d860 - U+1d87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d880 - U+1d89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8a0 - U+1d8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8c0 - U+1d8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8e0 - U+1d8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d900 - U+1d91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d920 - U+1d93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d940 - U+1d95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d960 - U+1d97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d980 - U+1d99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9a0 - U+1d9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9c0 - U+1d9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9e0 - U+1d9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da00 - U+1da1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da20 - U+1da3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da40 - U+1da5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da60 - U+1da7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da80 - U+1da9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1daa0 - U+1dabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dac0 - U+1dadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dae0 - U+1daff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db00 - U+1db1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db20 - U+1db3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db40 - U+1db5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db60 - U+1db7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db80 - U+1db9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dba0 - U+1dbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dbc0 - U+1dbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dbe0 - U+1dbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc00 - U+1dc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc20 - U+1dc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc40 - U+1dc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc60 - U+1dc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc80 - U+1dc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dca0 - U+1dcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dcc0 - U+1dcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dce0 - U+1dcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd00 - U+1dd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd20 - U+1dd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd40 - U+1dd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd60 - U+1dd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd80 - U+1dd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dda0 - U+1ddbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ddc0 - U+1dddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dde0 - U+1ddff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de00 - U+1de1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de20 - U+1de3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de40 - U+1de5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de60 - U+1de7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de80 - U+1de9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dea0 - U+1debf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dec0 - U+1dedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dee0 - U+1deff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df00 - U+1df1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df20 - U+1df3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df40 - U+1df5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df60 - U+1df7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df80 - U+1df9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfa0 - U+1dfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfc0 - U+1dfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfe0 - U+1dfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e000 - U+1e01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e020 - U+1e03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e040 - U+1e05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e060 - U+1e07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e080 - U+1e09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0a0 - U+1e0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0c0 - U+1e0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0e0 - U+1e0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e100 - U+1e11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e120 - U+1e13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e140 - U+1e15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e160 - U+1e17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e180 - U+1e19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1a0 - U+1e1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1c0 - U+1e1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1e0 - U+1e1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e200 - U+1e21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e220 - U+1e23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e240 - U+1e25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e260 - U+1e27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e280 - U+1e29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2a0 - U+1e2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2c0 - U+1e2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2e0 - U+1e2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e300 - U+1e31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e320 - U+1e33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e340 - U+1e35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e360 - U+1e37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e380 - U+1e39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3a0 - U+1e3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3c0 - U+1e3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3e0 - U+1e3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e400 - U+1e41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e420 - U+1e43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e440 - U+1e45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e460 - U+1e47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e480 - U+1e49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4a0 - U+1e4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4c0 - U+1e4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4e0 - U+1e4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e500 - U+1e51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e520 - U+1e53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e540 - U+1e55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e560 - U+1e57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e580 - U+1e59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5a0 - U+1e5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5c0 - U+1e5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5e0 - U+1e5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e600 - U+1e61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e620 - U+1e63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e640 - U+1e65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e660 - U+1e67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e680 - U+1e69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6a0 - U+1e6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6c0 - U+1e6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6e0 - U+1e6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e700 - U+1e71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e720 - U+1e73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e740 - U+1e75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e760 - U+1e77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e780 - U+1e79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7a0 - U+1e7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7c0 - U+1e7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7e0 - U+1e7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e800 - U+1e81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e820 - U+1e83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e840 - U+1e85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e860 - U+1e87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e880 - U+1e89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8a0 - U+1e8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8c0 - U+1e8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8e0 - U+1e8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e900 - U+1e91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e920 - U+1e93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e940 - U+1e95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e960 - U+1e97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e980 - U+1e99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9a0 - U+1e9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9c0 - U+1e9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9e0 - U+1e9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea00 - U+1ea1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea20 - U+1ea3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea40 - U+1ea5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea60 - U+1ea7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea80 - U+1ea9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eaa0 - U+1eabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eac0 - U+1eadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eae0 - U+1eaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb00 - U+1eb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb20 - U+1eb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb40 - U+1eb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb60 - U+1eb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb80 - U+1eb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eba0 - U+1ebbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ebc0 - U+1ebdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ebe0 - U+1ebff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec00 - U+1ec1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec20 - U+1ec3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec40 - U+1ec5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec60 - U+1ec7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec80 - U+1ec9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eca0 - U+1ecbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ecc0 - U+1ecdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ece0 - U+1ecff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed00 - U+1ed1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed20 - U+1ed3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed40 - U+1ed5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed60 - U+1ed7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed80 - U+1ed9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eda0 - U+1edbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1edc0 - U+1eddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ede0 - U+1edff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee00 - U+1ee1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee20 - U+1ee3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee40 - U+1ee5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee60 - U+1ee7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee80 - U+1ee9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eea0 - U+1eebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eec0 - U+1eedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eee0 - U+1eeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef00 - U+1ef1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef20 - U+1ef3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef40 - U+1ef5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef60 - U+1ef7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef80 - U+1ef9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efa0 - U+1efbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efc0 - U+1efdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efe0 - U+1efff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f000 - U+1f01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f020 - U+1f03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f040 - U+1f05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f060 - U+1f07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f080 - U+1f09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0a0 - U+1f0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0c0 - U+1f0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0e0 - U+1f0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f100 - U+1f11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f120 - U+1f13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f140 - U+1f15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f160 - U+1f17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f180 - U+1f19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1a0 - U+1f1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1c0 - U+1f1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1e0 - U+1f1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f200 - U+1f21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f220 - U+1f23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f240 - U+1f25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f260 - U+1f27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f280 - U+1f29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2a0 - U+1f2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2c0 - U+1f2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2e0 - U+1f2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f300 - U+1f31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f320 - U+1f33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f340 - U+1f35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f360 - U+1f37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f380 - U+1f39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3a0 - U+1f3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3c0 - U+1f3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3e0 - U+1f3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f400 - U+1f41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f420 - U+1f43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f440 - U+1f45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f460 - U+1f47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f480 - U+1f49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4a0 - U+1f4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4c0 - U+1f4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4e0 - U+1f4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f500 - U+1f51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f520 - U+1f53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f540 - U+1f55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f560 - U+1f57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f580 - U+1f59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5a0 - U+1f5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5c0 - U+1f5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5e0 - U+1f5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f600 - U+1f61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f620 - U+1f63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f640 - U+1f65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f660 - U+1f67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f680 - U+1f69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6a0 - U+1f6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6c0 - U+1f6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6e0 - U+1f6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f700 - U+1f71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f720 - U+1f73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f740 - U+1f75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f760 - U+1f77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f780 - U+1f79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7a0 - U+1f7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7c0 - U+1f7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7e0 - U+1f7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f800 - U+1f81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f820 - U+1f83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f840 - U+1f85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f860 - U+1f87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f880 - U+1f89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8a0 - U+1f8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8c0 - U+1f8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8e0 - U+1f8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f900 - U+1f91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f920 - U+1f93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f940 - U+1f95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f960 - U+1f97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f980 - U+1f99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9a0 - U+1f9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9c0 - U+1f9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9e0 - U+1f9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa00 - U+1fa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa20 - U+1fa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa40 - U+1fa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa60 - U+1fa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa80 - U+1fa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1faa0 - U+1fabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fac0 - U+1fadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fae0 - U+1faff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb00 - U+1fb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb20 - U+1fb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb40 - U+1fb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb60 - U+1fb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb80 - U+1fb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fba0 - U+1fbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fbc0 - U+1fbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fbe0 - U+1fbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc00 - U+1fc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc20 - U+1fc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc40 - U+1fc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc60 - U+1fc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc80 - U+1fc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fca0 - U+1fcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fcc0 - U+1fcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fce0 - U+1fcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd00 - U+1fd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd20 - U+1fd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd40 - U+1fd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd60 - U+1fd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd80 - U+1fd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fda0 - U+1fdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fdc0 - U+1fddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fde0 - U+1fdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe00 - U+1fe1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe20 - U+1fe3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe40 - U+1fe5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe60 - U+1fe7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe80 - U+1fe9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fea0 - U+1febf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fec0 - U+1fedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fee0 - U+1feff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff00 - U+1ff1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff20 - U+1ff3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff40 - U+1ff5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff60 - U+1ff7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff80 - U+1ff9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ffa0 - U+1ffbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ffc0 - U+1ffdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff /* U+1ffe0 - U+1ffff */
+};
diff --git a/imap/src/charset/windows.c b/imap/src/charset/windows.c
new file mode 100644
index 00000000..0f8edc97
--- /dev/null
+++ b/imap/src/charset/windows.c
@@ -0,0 +1,228 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 16 October 2000
+ * Last Edited: 30 August 2006
+ */
+
+ /* Windows Thai */
+static const unsigned short windows_874tab[128] = {
+ 0x20ac,UBOGON,UBOGON,UBOGON,UBOGON,0x2026,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
+
+ /* Windows Latin-2 */
+static const unsigned short windows_1250tab[128] = {
+ 0x20ac,UBOGON,0x201a,UBOGON,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,0x0160,0x2039,0x015a,0x0164,0x017d,0x0179,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,0x0161,0x203a,0x015b,0x0165,0x017e,0x017a,
+ 0x00a0,0x02c7,0x02d8,0x0141,0x00a4,0x0104,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x015e,0x00ab,0x00ac,0x00ad,0x00ae,0x017b,
+ 0x00b0,0x00b1,0x02db,0x0142,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x0105,0x015f,0x00bb,0x013d,0x02dd,0x013e,0x017c,
+ 0x0154,0x00c1,0x00c2,0x0102,0x00c4,0x0139,0x0106,0x00c7,
+ 0x010c,0x00c9,0x0118,0x00cb,0x011a,0x00cd,0x00ce,0x010e,
+ 0x0110,0x0143,0x0147,0x00d3,0x00d4,0x0150,0x00d6,0x00d7,
+ 0x0158,0x016e,0x00da,0x0170,0x00dc,0x00dd,0x0162,0x00df,
+ 0x0155,0x00e1,0x00e2,0x0103,0x00e4,0x013a,0x0107,0x00e7,
+ 0x010d,0x00e9,0x0119,0x00eb,0x011b,0x00ed,0x00ee,0x010f,
+ 0x0111,0x0144,0x0148,0x00f3,0x00f4,0x0151,0x00f6,0x00f7,
+ 0x0159,0x016f,0x00fa,0x0171,0x00fc,0x00fd,0x0163,0x02d9
+};
+
+ /* Windows Cyrillic */
+static const unsigned short windows_1251tab[128] = {
+ 0x0402,0x0403,0x201a,0x0453,0x201e,0x2026,0x2020,0x2021,
+ 0x20ac,0x2030,0x0409,0x2039,0x040a,0x040c,0x040b,0x040f,
+ 0x0452,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,0x0459,0x203a,0x045a,0x045c,0x045b,0x045f,
+ 0x00a0,0x040e,0x045e,0x0408,0x00a4,0x0490,0x00a6,0x00a7,
+ 0x0401,0x00a9,0x0404,0x00ab,0x00ac,0x00ad,0x00ae,0x0407,
+ 0x00b0,0x00b1,0x0406,0x0456,0x0491,0x00b5,0x00b6,0x00b7,
+ 0x0451,0x2116,0x0454,0x00bb,0x0458,0x0405,0x0455,0x0457,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f
+};
+
+ /* Windows Latin-1 */
+static const unsigned short windows_1252tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0160,0x2039,0x0152,UBOGON,0x017d,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,0x0161,0x203a,0x0153,UBOGON,0x017e,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff
+};
+
+
+ /* Windows Greek */
+static const unsigned short windows_1253tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,UBOGON,0x2039,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,UBOGON,0x203a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0385,0x0386,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,UBOGON,0x00ab,0x00ac,0x00ad,0x00ae,0x2015,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x0384,0x00b5,0x00b6,0x00b7,
+ 0x0388,0x0389,0x038a,0x00bb,0x038c,0x00bd,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,UBOGON,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x03ac,0x03ad,0x03ae,0x03af,
+ 0x03b0,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,
+ 0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,
+ 0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,0x03ce,UBOGON
+};
+
+ /* Windows Turkish */
+static const unsigned short windows_1254tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0160,0x2039,0x0152,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,0x0161,0x203a,0x0153,UBOGON,UBOGON,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x011e,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x0130,0x015e,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x011f,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x0131,0x015f,0x00ff
+};
+
+ /* Windows Hebrew */
+static const unsigned short windows_1255tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,UBOGON,0x2039,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,UBOGON,0x203a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x20aa,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00d7,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00f7,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x05b0,0x05b1,0x05b2,0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,
+ 0x05b8,0x05b9,UBOGON,0x05bb,0x05bc,0x05bd,0x05be,0x05bf,
+ 0x05c0,0x05c1,0x05c2,0x05c3,0x05f0,0x05f1,0x05f2,0x05f3,
+ 0x05f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,UBOGON,UBOGON,0x200e,0x200f,UBOGON
+};
+
+ /* Windows Arabic */
+static const unsigned short windows_1256tab[128] = {
+ 0x20ac,0x067e,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0679,0x2039,0x0152,0x0686,0x0698,0x0688,
+ 0x06af,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x06a9,0x2122,0x0691,0x203a,0x0153,0x200c,0x200d,0x06ba,
+ 0x00a0,0x060c,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x06be,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x061b,0x00bb,0x00bc,0x00bd,0x00be,0x061f,
+ 0x06c1,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x00d7,
+ 0x0637,0x0638,0x0639,0x063a,0x0640,0x0641,0x0642,0x0643,
+ 0x00e0,0x0644,0x00e2,0x0645,0x0646,0x0647,0x0648,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x0649,0x064a,0x00ee,0x00ef,
+ 0x064b,0x064c,0x064d,0x064e,0x00f4,0x064f,0x0650,0x00f7,
+ 0x0651,0x00f9,0x0652,0x00fb,0x00fc,0x200e,0x200f,0x06d2
+};
+
+ /* Windows Baltic */
+static const unsigned short windows_1257tab[128] = {
+ 0x20ac,UBOGON,0x201a,UBOGON,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,UBOGON,0x2039,UBOGON,0x00a8,0x02c7,0x00b8,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,UBOGON,0x203a,UBOGON,0x00af,0x02db,UBOGON,
+ 0x00a0,UBOGON,0x00a2,0x00a3,0x00a4,UBOGON,0x00a6,0x00a7,
+ 0x00d8,0x00a9,0x0156,0x00ab,0x00ac,0x00ad,0x00ae,0x00c6,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00f8,0x00b9,0x0157,0x00bb,0x00bc,0x00bd,0x00be,0x00e6,
+ 0x0104,0x012e,0x0100,0x0106,0x00c4,0x00c5,0x0118,0x0112,
+ 0x010c,0x00c9,0x0179,0x0116,0x0122,0x0136,0x012a,0x013b,
+ 0x0160,0x0143,0x0145,0x00d3,0x014c,0x00d5,0x00d6,0x00d7,
+ 0x0172,0x0141,0x015a,0x016a,0x00dc,0x017b,0x017d,0x00df,
+ 0x0105,0x012f,0x0101,0x0107,0x00e4,0x00e5,0x0119,0x0113,
+ 0x010d,0x00e9,0x017a,0x0117,0x0123,0x0137,0x012b,0x013c,
+ 0x0161,0x0144,0x0146,0x00f3,0x014d,0x00f5,0x00f6,0x00f7,
+ 0x0173,0x0142,0x015b,0x016b,0x00fc,0x017c,0x017e,0x02d9
+};
+
+ /* Windows Vietnamese */
+static const unsigned short windows_1258tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,UBOGON,0x2039,0x0152,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,UBOGON,0x203a,0x0153,UBOGON,UBOGON,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x0102,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x0300,0x00cd,0x00ce,0x00cf,
+ 0x0110,0x00d1,0x0309,0x00d3,0x00d4,0x01a0,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x01af,0x0303,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x0103,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x0301,0x00ed,0x00ee,0x00ef,
+ 0x0111,0x00f1,0x0323,0x00f3,0x00f4,0x01a1,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x01b0,0x20ab,0x00ff
+};
diff --git a/imap/src/dmail/Makefile b/imap/src/dmail/Makefile
new file mode 100644
index 00000000..4aef24cf
--- /dev/null
+++ b/imap/src/dmail/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: dmail Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 April 1993
+# Last Edited: 10 September 2007
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+dmail: $(CCLIENTLIB) dmail.o dquota.o
+ $(CC) $(CFLAGS) -o dmail dmail.o dquota.o $(LDFLAGS)
+
+dmail.o: $C/mail.h $C/misc.h $C/osdep.h dquota.h
+
+dquota.o: dquota.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o dmail
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/dmail/dmail.1 b/imap/src/dmail/dmail.1
new file mode 100644
index 00000000..92cd894f
--- /dev/null
+++ b/imap/src/dmail/dmail.1
@@ -0,0 +1,121 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH DMAIL 1 "June 18, 2007"
+.SH NAME
+dmail \- procmail Mail Delivery Module
+.nh
+.SH SYNOPSIS
+.B dmail
+.I [\-D] [\-f from_name] [-s] [-k keyword_list] [user][+folder]
+.SH DESCRIPTION
+.I dmail
+delivers mail to a user's INBOX or a designated folder.
+.I dmail
+may be configured as a drop-in replacement for
+.IR binmail (1),
+.IR mail.local (1)
+for use with a mail delivery filter such as
+.IR procmail (1) .
+.PP
+Because of security considerations (see below)
+.I dmail
+is not intended to be used for direct delivery by the mailer daemon;
+.IR tmail (1)
+is the preferred tool for this purpose. If
+.I dmail
+is used for mailer daemon delivery, the mailer daemon must invoke
+.I dmail
+with the
+.I dmail
+process' user id set to the recipient's user id.
+.PP
+When
+.I dmail
+exits, it returns exit status values to enable
+.IR procmail (1)
+to determine whether a message was delivered successfully or had a
+temporary (requeue for later delivery) or permanent (return to sender)
+failure.
+.PP
+If the
+.I user
+name is present, it must be the same as the logged-in user name.
+.PP
+If the
+.I +folder
+extension is included in the user argument (or appears by itself if there
+is no user argument),
+.I dmail
+will attempt to deliver to the designated folder. If the folder does not
+exist or the extension is not included, the message is delivered to the
+user's INBOX.
+If delivery is to INBOX and no INBOX currently exists,
+.I dmail
+will create a new INBOX.
+.I dmail
+recognizes the format of an existing INBOX or folder, and appends the new
+message in that format.
+.PP
+The \fB-D\fR flag specifies debugging; this enables additional message
+telemetry.
+.PP
+The \fB-f\fR or \fB-r\fR flag is used to specify a Return-Path. The header
+.br
+ Return-Path: <\fIfrom_name\fR>
+.br
+is prepended to the message before delivery.
+.PP
+The
+.B -s
+flag specifies that the message will be flagged as being "seen".
+.PP
+The \fB-k\fR flag is used to specify delivery keywords, which are set on
+the message at delivery time if and
+.B only
+if the keywords are already defined in the mailbox. Multiple keywords can be
+specified by using a quoted string, e.g.,
+.br
+ dmail -k "$Junk Discard" +junkbox
+.br
+.SH RESTRICTIONS
+Absolute pathnames and
+.I ~user
+specifications are not permitted in
+.I +folder
+extensions.
+.SH SECURITY CONSIDERATIONS
+Unlike
+.I tmail
+you can use
+.I dmail
+to deliver to IMAP4 namespace names via
+.I +folder
+extensions. This means that it is possible to deliver to
+.IR mh (1)
+format mailboxes.
+.PP
+However, this can also include such namespaces as #shared, #public,
+and #ftp. In most cases, it is undesirable to allow anybody sending
+mail to the user to deliver to these namespaces. Consequently, there
+needs to be a rule in place in the configuration of either
+.IR sendmail (8)
+or
+.IR procmail (1)
+to prevent such abuse.
+.SH AUTHOR
+Mark Crispin, MRC@CAC.Washington.EDU
+.SH "SEE ALSO"
+binmail(1)
+.br
+procmail(1)
diff --git a/imap/src/dmail/dmail.c b/imap/src/dmail/dmail.c
new file mode 100644
index 00000000..f78b9576
--- /dev/null
+++ b/imap/src/dmail/dmail.c
@@ -0,0 +1,661 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 April 1993
+ * Last Edited: 30 October 2008
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "c-client.h"
+#include "dquota.h"
+
+
+/* Globals */
+
+char *version = "18"; /* dmail edit version */
+int debug = NIL; /* debugging (don't fork) */
+int flagseen = NIL; /* flag message as seen */
+int trycreate = NIL; /* flag saying gotta create before appending */
+int critical = NIL; /* flag saying in critical code */
+char *sender = NIL; /* message origin */
+char *keywords = NIL; /* keyword list */
+long precedence = 0; /* delivery precedence - used by quota hook */
+
+
+/* Function prototypes */
+
+void file_string_init (STRING *s,void *data,unsigned long size);
+char file_string_next (STRING *s);
+void file_string_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+int deliver (FILE *f,unsigned long msglen,char *user);
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ char *tmp);
+int delivery_unsafe (char *path,struct stat *sbuf,char *tmp);
+int fail (char *string,int code);
+
+
+/* File string driver for file stringstructs */
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Cache buffer for file stringstructs */
+
+#define CHUNKLEN 16384
+char chunk[CHUNKLEN];
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note fd */
+ s->size = size; /* note size */
+ s->chunk = chunk;
+ s->chunksize = (unsigned long) CHUNKLEN;
+ SETPOS (s,0); /* set initial position */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char file_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void file_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ fseek ((FILE *) s->data,s->offset,SEEK_SET);
+ fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
+ }
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ FILE *f = NIL;
+ int c,ret = 0;
+ unsigned long msglen;
+ char *s,tmp[MAILTMPLEN];
+ uid_t ruid = getuid ();
+ struct passwd *pwd = ruid ? getpwnam ("daemon") : NIL;
+ openlog ("dmail",LOG_PID,LOG_MAIL);
+ /* must not be root or daemon! */
+ if (!ruid || (pwd && (pwd->pw_uid == ruid)))
+ _exit (fail ("dmail may not be invoked by root or daemon",EX_USAGE));
+#include "linkage.c"
+ /* process all flags */
+ for (--argc; argc && (*(s = *++argv)) == '-'; argc--) switch (s[1]) {
+ case 'D': /* debug */
+ debug = T; /* extra debugging */
+ break;
+ case 's': /* deliver as seen */
+ flagseen = T;
+ break;
+ case 'f':
+ case 'r': /* flag giving return path */
+ if (sender) _exit (fail ("duplicate -r",EX_USAGE));
+ if (argc--) sender = cpystr (*++argv);
+ else _exit (fail ("missing argument to -r",EX_USAGE));
+ break;
+ case 'k':
+ if (keywords) _exit (fail ("duplicate -k",EX_USAGE));
+ if (argc--) keywords = cpystr (*++argv);
+ else _exit (fail ("missing argument to -k",EX_USAGE));
+ break;
+ case 'p':
+ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
+ else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
+ precedence = atol (s);
+ else _exit (fail ("missing argument to -p",EX_USAGE));
+ break;
+ default: /* anything else */
+ _exit (fail ("unknown switch",EX_USAGE));
+ }
+
+ if (argc > 1) _exit (fail ("too many recipients",EX_USAGE));
+ else if (!(f = tmpfile ())) _exit(fail ("can't make temp file",EX_TEMPFAIL));
+ /* build delivery headers */
+ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
+ /* start Received line: */
+ fprintf (f,"Received: via dmail-%s.%s for %s; ",CCLIENTVERSION,version,
+ (argc == 1) ? *argv : myusername ());
+ rfc822_date (tmp);
+ fputs (tmp,f);
+ fputs ("\015\012",f);
+ /* copy text from standard input */
+ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
+ (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
+ else if (s[-1] == '\015') { /* nuke leading "From " line */
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
+ while ((c = getchar ()) != EOF) putc (c,f);
+ }
+ else {
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) {
+ *s++ = '\015'; /* overwrite NL with CRLF */
+ *s++ = '\012';
+ *s = '\0'; /* tie off string */
+ fputs (tmp,f); /* write line */
+ }
+ }
+ /* copy text from standard input */
+ while ((c = getchar ()) != EOF) {
+ /* add CR if needed */
+ if (c == '\012') putc ('\015',f);
+ putc (c,f);
+ }
+ msglen = ftell (f); /* size of message */
+ fflush (f); /* make sure all changes written out */
+ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
+ else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
+ /* single delivery */
+ else ret = deliver (f,msglen,argc ? *argv : myusername ());
+ fclose (f); /* all done with temporary file */
+ _exit (ret); /* normal exit */
+ return 0; /* stupid gcc */
+}
+
+/* Deliver message to recipient list
+ * Accepts: file description of message temporary file
+ * size of message temporary file in bytes
+ * recipient name
+ * Returns: NIL if success, else error code
+ */
+
+int deliver (FILE *f,unsigned long msglen,char *user)
+{
+ MAILSTREAM *ds = NIL;
+ char *s,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
+ STRING st;
+ struct stat sbuf;
+ /* have a mailbox specifier? */
+ if (mailbox = strchr (user,'+')) {
+ *mailbox++ = '\0'; /* yes, tie off user name */
+ if (!*mailbox || !compare_cstring ((unsigned char *) mailbox,"INBOX"))
+ mailbox = NIL; /* user+ and user+INBOX same as user */
+ }
+ if (!*user) user = myusername ();
+ else if (strcmp (user,myusername ()))
+ return fail ("can't deliver to other user",EX_CANTCREAT);
+ sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
+ mm_dlog (tmp);
+ /* prepare stringstruct */
+ INIT (&st,file_string,(void *) f,msglen);
+ if (mailbox) { /* non-INBOX name */
+ switch (mailbox[0]) { /* make sure a valid name */
+ default: /* other names, try to deliver if not INBOX */
+ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
+ !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
+ !deliver_safely (NIL,&st,mailbox,path,tmp)) return NIL;
+ case '%': case '*': /* wildcards not valid */
+ case '/': /* absolute path names not valid */
+ case '~': /* user names not valid */
+ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
+ mm_log (tmp,WARN);
+ break;
+ }
+ mm_dlog ("retrying delivery to INBOX");
+ SETPOS (&st,0); /* rewind stringstruct just in case */
+ }
+
+ /* no -I, resolve "INBOX" into path */
+ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
+ /* clear box, get generic INBOX prototype */
+ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* standard system driver? */
+ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
+ strcpy (path,sysinbox ());/* use system INBOX */
+ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ }
+ else { /* other driver, try ~/INBOX */
+ if ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
+ !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ return deliver_safely (ds,&st,cpystr (tmp),path,tmp);
+ }
+ }
+ /* not dummy, deliver to driver imputed path */
+ if (strcmp (ds->dtb->name,"dummy"))
+ return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
+ deliver_safely (ds,&st,mailbox,path,tmp) :
+ fail ("unable to resolve INBOX path",EX_CANTCREAT);
+ /* dummy, empty imputed append path exist? */
+ if (ibxpath (ds = default_proto (T),&mailbox,path) &&
+ !lstat (path,&sbuf) && !sbuf.st_size)
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ /* impute path that we will create */
+ if (!ibxpath (ds = default_proto (NIL),&mailbox,path))
+ return fail ("unable to resolve INBOX",EX_CANTCREAT);
+ }
+ /* black box, must create, get create proto */
+ else if (lstat (path,&sbuf)) ds = default_proto (NIL);
+ else { /* black box, existing file */
+ /* empty file, get append prototype */
+ if (!sbuf.st_size) ds = default_proto (T);
+ /* non-empty, get prototype from its data */
+ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* error if unknown format */
+ if (!strcmp (ds->dtb->name,"phile"))
+ return fail ("unknown format INBOX",EX_UNAVAILABLE);
+ /* otherwise can deliver to it */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ }
+ sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
+ mm_dlog (tmp);
+ /* supplicate to the Evil One */
+ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
+ sprintf (tmp,"created %.80s",path);
+ mm_dlog (tmp);
+ /* deliver the message */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+}
+
+/* Resolve INBOX from driver prototype into mailbox name and filesystem path
+ * Accepts: driver prototype
+ * pointer to mailbox name string pointer
+ * buffer to return mailbox path
+ * Returns: T if success, NIL if error
+ */
+
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = T;
+ if (!ds) return NIL;
+ else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
+ strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
+ else if (!strcmp (ds->dtb->name,"tenex"))
+ ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mtx"))
+ ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mbox"))
+ ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
+ /* better not be a namespace driver */
+ else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
+ /* INBOX in home directory */
+ else ret = ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
+ if (ret) { /* don't bother if lossage */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
+ }
+ return ret;
+}
+
+/* Deliver safely
+ * Accepts: prototype stream to force mailbox format
+ * stringstruct of message temporary file or NIL for check only
+ * mailbox name
+ * filesystem path name
+ * scratch buffer for messages
+ * Returns: NIL if success, else error code
+ */
+
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ char *tmp)
+{
+ struct stat sbuf;
+ char *flags = NIL;
+ int i = delivery_unsafe (path,&sbuf,tmp);
+ if (i) return i; /* give up now if delivery unsafe */
+ /* directory, not file */
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
+ sprintf (tmp,"WARNING: directory %.80s is listable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ else { /* file, not directory */
+ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
+ sprintf (tmp,"WARNING: multiple links to file %.80s",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is executable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
+ mm_log (tmp,WARN);
+ }
+ /* check site-written quota procedure */
+ if (!dmail_quota (st,path,tmp,sender,precedence))
+ return fail (tmp,EX_CANTCREAT);
+ /* so far, so good */
+ sprintf (tmp,"%s appending to %.80s (%s %.80s)",
+ prt ? prt->dtb->name : "default",mailbox,
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
+ mm_dlog (tmp);
+ if (keywords) { /* any keywords requested? */
+ if (flagseen) sprintf (flags = tmp,"\\Seen %.1000s",keywords);
+ else flags = keywords;
+ }
+ else if (flagseen) flags = "\\Seen";
+ /* do the append now! */
+ if (!mail_append_full (prt,mailbox,flags,NIL,st)) {
+ sprintf (tmp,"message delivery failed to %.80s",path);
+ return fail (tmp,EX_CANTCREAT);
+ }
+ /* note success */
+ sprintf (tmp,"delivered to %.80s",path);
+ mm_log (tmp,NIL);
+ /* make sure nothing evil this way comes */
+ return delivery_unsafe (path,&sbuf,tmp);
+}
+
+/* Verify that delivery is safe
+ * Accepts: path name
+ * stat buffer
+ * scratch buffer for messages
+ * Returns: NIL if delivery is safe, error code if unsafe
+ */
+
+int delivery_unsafe (char *path,struct stat *sbuf,char *tmp)
+{
+ u_short type;
+ sprintf (tmp,"Verifying safe delivery to %.80s",path);
+ mm_dlog (tmp);
+ /* prepare message just in case */
+ sprintf (tmp,"delivery to %.80s unsafe: ",path);
+ /* unsafe if can't get its status */
+ if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
+ /* check file type */
+ else switch (sbuf->st_mode & S_IFMT) {
+ case S_IFDIR: /* directory is always OK */
+ return NIL;
+ case S_IFREG: /* file is unsafe if setuid */
+ if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
+ /* or setgid */
+ else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
+ else return NIL; /* otherwise safe */
+ break;
+ case S_IFCHR: strcat (tmp,"character special"); break;
+ case S_IFBLK: strcat (tmp,"block special"); break;
+ case S_IFLNK: strcat (tmp,"symbolic link"); break;
+ case S_IFSOCK: strcat (tmp,"socket"); break;
+ default:
+ sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
+ }
+ return fail (tmp,EX_CANTCREAT);
+}
+
+/* Report an error
+ * Accepts: string to output
+ */
+
+int fail (char *string,int code)
+{
+ mm_log (string,ERROR); /* pass up the string */
+ switch (code) {
+#if T
+ case EX_USAGE:
+ case EX_OSERR:
+ case EX_SOFTWARE:
+ case EX_NOUSER:
+ case EX_CANTCREAT:
+ code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
+ break;
+#endif
+ case -1: /* quota failure... */
+ code = EX_CANTCREAT; /* ...really returns this code */
+ break;
+ default:
+ break;
+ }
+ return code; /* error code to return */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ fatal ("mm_searched() call");
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_exists() call");
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_expunged() call");
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_list() call");
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_lsub() call");
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ fatal ("mm_status() call");
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ tmp[11] = '\0'; /* see if TRYCREATE */
+ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
+ else { /* ordinary logging */
+ fprintf (stderr,"%s\n",string);
+ switch (errflg) {
+ case NIL: /* no error */
+ syslog (LOG_INFO,"%s",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ syslog (LOG_WARNING,"%s",string);
+ break;
+ case ERROR: /* error */
+ default:
+ syslog (LOG_ERR,"%s",string);
+ break;
+ }
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ if (debug) fprintf (stderr,"%s\n",string);
+ syslog (LOG_DEBUG,"%s",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ fatal ("mm_login() call");
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string); /* shouldn't happen normally */
+}
diff --git a/imap/src/dmail/dquota.c b/imap/src/dmail/dquota.c
new file mode 100644
index 00000000..38267075
--- /dev/null
+++ b/imap/src/dmail/dquota.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+#include "c-client.h"
+
+/* Site-written routine to validate delivery per quota and policy
+ * Accepts: stringstruct of message temporary file
+ * filesystem path
+ * return path
+ * buffer to write error message
+ * precedence setting
+ * Returns: T if can deliver, or NIL if quota issue and must bounce
+ */
+
+long dmail_quota (STRING *msg,char *path,char *tmp,char *sender,
+ long precedence)
+{
+ return LONGT; /* dummy success return */
+}
diff --git a/imap/src/dmail/dquota.h b/imap/src/dmail/dquota.h
new file mode 100644
index 00000000..44280df3
--- /dev/null
+++ b/imap/src/dmail/dquota.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+/* Function prototypes */
+
+long dmail_quota (STRING *msg,char *path,char *tmp,char *sender,
+ long precedence);
diff --git a/imap/src/imapd/Makefile b/imap/src/imapd/Makefile
new file mode 100644
index 00000000..c3078f42
--- /dev/null
+++ b/imap/src/imapd/Makefile
@@ -0,0 +1,68 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=/etc/imapd.alert
+USERALERT=.imapalert
+SHUTDOWN=/etc/nologin
+ANO=/etc/anonymous.newsgroups
+NNTP=/etc/imapd.nntp
+SHELL= /bin/sh
+
+
+# Un-comment this to get somewhat better interoperability with Netscape. It
+# causes the "Manage Mail" menu item to open the given URL, e.g. to point to
+# an alternative IMAP client (e.g. Pine) or perhaps to a homebrew mail
+# account management page.
+#NSBD= -DNETSCAPE_BRAIN_DAMAGE=\"http://www.washington.edu/pine\"
+
+
+# Get local definitions from c-client directory
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS` $(NSBD) $(ENBD) -DANOFILE=\"$(ANO)\" \
+ -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" \
+ -DUSERALERTFILE=\"$(USERALERT)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+all: imapd
+
+imapd: $(CCLIENTLIB) imapd.o
+ $(CC) $(CFLAGS) -o imapd imapd.o $(LDFLAGS)
+
+imapd.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o imapd || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/imapd/imapd.8 b/imap/src/imapd/imapd.8
new file mode 100644
index 00000000..2d2f8c8e
--- /dev/null
+++ b/imap/src/imapd/imapd.8
@@ -0,0 +1,48 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH IMAPD 8 "August 30, 2006"
+.UC 5
+.SH NAME
+IMAPd \- Internet Message Access Protocol server
+.SH SYNOPSIS
+.B /usr/etc/imapd
+.SH DESCRIPTION
+.I imapd
+is a server which supports the
+.B IMAP4rev1
+remote mail access protocol as documented in RFC-3501.
+.I imapd
+is invoked by the internet server (see
+.IR inetd (8)),
+normally for requests to connect to the
+.B IMAP
+port as indicated by the
+.I /etc/services
+file (see
+.IR services (5)).
+Normally, this is port 143 for plaintext IMAP and 993 for SSL IMAP.
+.PP
+This daemons contains CRAM-MD5 support. See the md5.txt documentation
+file for additional information.
+.PP
+.I imapd
+can also be accessed via
+.IR rsh (1)
+by many Unix-based clients. To do this, the
+.I imapd
+binary must have a link to
+.I /etc/rimapd
+since this is where this software expects it to be located.
+.SH "SEE ALSO"
+rsh(1) ipopd(8)
diff --git a/imap/src/imapd/imapd.c b/imap/src/imapd/imapd.c
new file mode 100644
index 00000000..d3d15660
--- /dev/null
+++ b/imap/src/imapd/imapd.c
@@ -0,0 +1,4608 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP4rev1 server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 5 November 1990
+ * Last Edited: 3 March 2008
+ */
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <setjmp.h>
+#include <time.h>
+#include "c-client.h"
+#include "newsrc.h"
+#include <sys/stat.h>
+
+
+#define CRLF PSOUT ("\015\012") /* primary output terpri */
+
+
+/* Timeouts and timers */
+
+#define MINUTES *60
+
+#define LOGINTIMEOUT 3 MINUTES /* not logged in autologout timer */
+#define TIMEOUT 30 MINUTES /* RFC 3501 minimum autologout timer */
+#define INPUTTIMEOUT 5 MINUTES /* timer for additional command input */
+#define ALERTTIMER 1 MINUTES /* alert check timer */
+#define SHUTDOWNTIMER 1 MINUTES /* shutdown dally timer */
+#define IDLETIMER 1 MINUTES /* IDLE command poll timer */
+#define CHECKTIMER 15 MINUTES /* IDLE command last checkpoint timer */
+
+
+#define LITSTKLEN 20 /* length of literal stack */
+#define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size
+ * must be smaller than 4294967295
+ */
+#define MAXAPPENDTXT 0x40000000 /* maximum APPEND literal size
+ * must be smaller than 4294967295
+ */
+#define CMDLEN 65536 /* size of command buffer */
+
+
+/* Server states */
+
+#define LOGIN 0
+#define SELECT 1
+#define OPEN 2
+#define LOGOUT 3
+
+/* Body text fetching */
+
+typedef struct text_args {
+ char *section; /* body section */
+ STRINGLIST *lines; /* header lines */
+ unsigned long first; /* first octet to fetch */
+ unsigned long last; /* number of octets to fetch */
+ long flags; /* fetch flags */
+ long binary; /* binary flags */
+} TEXTARGS;
+
+#define FTB_BINARY 0x1 /* fetch as binary */
+#define FTB_SIZE 0x2 /* fetch size only */
+
+
+/* Append data */
+
+typedef struct append_data {
+ unsigned char *arg; /* append argument pointer */
+ char *flags; /* message flags */
+ char *date; /* message date */
+ char *msg; /* message text */
+ STRING *message; /* message stringstruct */
+} APPENDDATA;
+
+
+/* Message pointer */
+
+typedef struct msg_data {
+ MAILSTREAM *stream; /* stream */
+ unsigned long msgno; /* message number */
+ char *flags; /* current flags */
+ char *date; /* current date */
+ STRING *message; /* stringstruct of message */
+} MSGDATA;
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void ping_mailbox (unsigned long uid);
+time_t palert (char *file,time_t oldtime);
+void msg_string_init (STRING *s,void *data,unsigned long size);
+char msg_string_next (STRING *s);
+void msg_string_setpos (STRING *s,unsigned long i);
+void new_flags (MAILSTREAM *stream);
+void settimeout (unsigned int i);
+void clkint (void);
+void kodint (void);
+void hupint (void);
+void trmint (void);
+void staint (void);
+char *sout (char *s,char *t);
+char *nout (char *s,unsigned long n,unsigned long base);
+void slurp (char *s,int n,unsigned long timeout);
+void inliteral (char *s,unsigned long n);
+unsigned char *flush (void);
+void ioerror (FILE *f,char *reason);
+unsigned char *parse_astring (unsigned char **arg,unsigned long *i,
+ unsigned char *del);
+unsigned char *snarf (unsigned char **arg);
+unsigned char *snarf_base64 (unsigned char **arg);
+unsigned char *snarf_list (unsigned char **arg);
+STRINGLIST *parse_stringlist (unsigned char **s,int *list);
+unsigned long uidmax (MAILSTREAM *stream);
+long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth);
+long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long msgmsg,
+ unsigned long maxuid,unsigned long depth);
+long crit_date (unsigned short *date,unsigned char **arg);
+long crit_date_work (unsigned short *date,unsigned char **arg);
+long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima);
+long crit_number (unsigned long *number,unsigned char **arg);
+long crit_string (STRINGLIST **string,unsigned char **arg);
+
+void fetch (char *t,unsigned long uid);
+typedef void (*fetchfn_t) (unsigned long i,void *args);
+void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
+void fetch_bodystructure (unsigned long i,void *args);
+void fetch_body (unsigned long i,void *args);
+void fetch_body_part_mime (unsigned long i,void *args);
+void fetch_body_part_contents (unsigned long i,void *args);
+void fetch_body_part_binary (unsigned long i,void *args);
+void fetch_body_part_header (unsigned long i,void *args);
+void fetch_body_part_text (unsigned long i,void *args);
+void remember (unsigned long uid,char *id,SIZEDTEXT *st);
+void fetch_envelope (unsigned long i,void *args);
+void fetch_encoding (unsigned long i,void *args);
+void changed_flags (unsigned long i,int f);
+void fetch_flags (unsigned long i,void *args);
+void put_flag (int *c,char *s);
+void fetch_internaldate (unsigned long i,void *args);
+void fetch_uid (unsigned long i,void *args);
+void fetch_rfc822 (unsigned long i,void *args);
+void fetch_rfc822_header (unsigned long i,void *args);
+void fetch_rfc822_size (unsigned long i,void *args);
+void fetch_rfc822_text (unsigned long i,void *args);
+void penv (ENVELOPE *env);
+void pbodystructure (BODY *body);
+void pbody (BODY *body);
+void pparam (PARAMETER *param);
+void paddr (ADDRESS *a);
+void pset (SEARCHSET **set);
+void pnum (unsigned long i);
+void pstring (char *s);
+void pnstring (char *s);
+void pastring (char *s);
+void psizedquoted (SIZEDTEXT *s);
+void psizedliteral (SIZEDTEXT *s,STRING *st);
+void psizedstring (SIZEDTEXT *s,STRING *st);
+void psizedastring (SIZEDTEXT *s);
+void pastringlist (STRINGLIST *s);
+void pnstringorlist (STRINGLIST *s);
+void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
+ TEXTARGS *ta);
+void ptext (SIZEDTEXT *s,STRING *st);
+void pthread (THREADNODE *thr);
+void pcapability (long flag);
+long nameok (char *ref,char *name);
+char *bboardname (char *cmd,char *name);
+long isnewsproxy (char *name);
+long newsproxypattern (char *ref,char *pat,char *pattern,long flag);
+char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
+long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *sourceset,SEARCHSET *destset);
+void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set);
+char *referral (MAILSTREAM *stream,char *url,long code);
+void mm_list_work (char *what,int delimiter,char *name,long attributes);
+char *lasterror (void);
+
+/* Global storage */
+
+char *version = "404"; /* edit number of this server */
+char *logout = "Logout"; /* syslogreason for logout */
+char *goodbye = NIL; /* bye reason */
+time_t alerttime = 0; /* time of last alert */
+time_t sysalerttime = 0; /* time of last system alert */
+time_t useralerttime = 0; /* time of last user alert */
+time_t lastcheck = 0; /* time of last checkpoint */
+time_t shutdowntime = 0; /* time of last shutdown */
+int state = LOGIN; /* server state */
+int cancelled = NIL; /* authenticate cancelled */
+int trycreate = 0; /* saw a trycreate */
+int finding = NIL; /* doing old FIND command */
+int anonymous = 0; /* non-zero if anonymous */
+int critical = NIL; /* non-zero if in critical code */
+int quell_events = NIL; /* non-zero if in FETCH response */
+int existsquelled = NIL; /* non-zero if an EXISTS was quelled */
+int proxylist = NIL; /* doing a proxy LIST */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+DRIVER *curdriver = NIL; /* note current driver */
+MAILSTREAM *tstream = NIL; /* temporary mailbox stream */
+unsigned int nflags = 0; /* current number of keywords */
+unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
+unsigned long recent = 0xffffffff;
+char *nntpproxy = NIL; /* NNTP proxy name */
+unsigned char *user = NIL; /* user name */
+unsigned char *pass = NIL; /* password */
+unsigned char *initial = NIL; /* initial response */
+unsigned char cmdbuf[CMDLEN]; /* command buffer */
+char *status = "starting up"; /* server status */
+char *tag; /* tag portion of command */
+unsigned char *cmd; /* command portion of command */
+unsigned char *arg; /* pointer to current argument of command */
+char *lstwrn = NIL; /* last warning message from c-client */
+char *lsterr = NIL; /* last error message from c-client */
+char *lstref = NIL; /* last referral from c-client */
+char *response = NIL; /* command response */
+struct {
+ unsigned long size; /* size of current LITERAL+ */
+ unsigned int ok : 1; /* LITERAL+ in effect */
+} litplus;
+int litsp = 0; /* literal stack pointer */
+char *litstk[LITSTKLEN]; /* stack to hold literals */
+unsigned long uidvalidity = 0; /* last reported UID validity */
+unsigned long lastuid = 0; /* last fetched uid */
+char *lastid = NIL; /* last fetched body id for this message */
+char *lastsel = NIL; /* last selected mailbox name */
+SIZEDTEXT lastst = {NIL,0}; /* last sizedtext */
+unsigned long cauidvalidity = 0;/* UIDVALIDITY for COPYUID/APPENDUID */
+SEARCHSET *csset = NIL; /* COPYUID source set */
+SEARCHSET *caset = NIL; /* COPYUID/APPENDUID destination set */
+jmp_buf jmpenv; /* stack context for setjmp */
+
+
+/* Response texts which appear in multiple places */
+
+char *win = "%.80s OK ";
+char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012";
+char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012";
+char *lose = "%.80s NO ";
+char *logwin = "%.80s OK [";
+char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s\015\012";
+char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.900s\015\012";
+char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012";
+char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012";
+char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012";
+char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012";
+char *badatt = "%.80s BAD Bogus attribute list in %.80s\015\012";
+char *badbin = "%.80s BAD Syntax error in binary specifier\015\012";
+
+/* Message string driver for message stringstructs */
+
+STRINGDRIVER msg_string = {
+ msg_string_init, /* initialize string structure */
+ msg_string_next, /* get next byte in string structure */
+ msg_string_setpos /* set position in string structure */
+};
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ unsigned long i,uid;
+ long f;
+ unsigned char *s,*t,*u,*v,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ logouthook_t lgoh;
+ int ret = 0;
+ time_t autologouttime = 0;
+ char *pgmname;
+ /* if case we get borked immediately */
+ if (setjmp (jmpenv)) _exit (1);
+ pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ (char *) s+1 : argv[0]) : "imapd";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "imap");
+#include "linkage.c"
+ rfc822_date (tmp); /* get date/time at startup */
+ /* initialize server */
+ server_init (pgmname,"imap","imaps",clkint,kodint,hupint,trmint,staint);
+ /* forbid automatic untagged expunge */
+ mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
+ /* arm proxy copy callback */
+ mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
+ /* arm referral callback */
+ mail_parameters (NIL,SET_IMAPREFERRAL,(void *) referral);
+ /* arm COPYUID callback */
+ mail_parameters (NIL,SET_COPYUID,(void *) copyuid);
+ /* arm APPENDUID callback */
+ mail_parameters (NIL,SET_APPENDUID,(void *) appenduid);
+
+ if (stat (SHUTDOWNFILE,&sbuf)) {
+ char proxy[MAILTMPLEN];
+ FILE *nntp = fopen (NNTPFILE,"r");
+ if (nntp) { /* desire NNTP proxy? */
+ if (fgets (proxy,MAILTMPLEN,nntp)) {
+ /* remove newline and set NNTP proxy */
+ if (s = strchr (proxy,'\n')) *s = '\0';
+ nntpproxy = cpystr (proxy);
+ /* disable the news driver */
+ mail_parameters (NIL,DISABLE_DRIVER,"news");
+ }
+ fclose (nntp); /* done reading proxy name */
+ }
+ s = myusername_full (&i); /* get user name and flags */
+ switch (i) {
+ case MU_NOTLOGGEDIN:
+ PSOUT ("* OK ["); /* not logged in, ordinary startup */
+ pcapability (-1);
+ break;
+ case MU_ANONYMOUS:
+ anonymous = T; /* anonymous user, fall into default */
+ s = "ANONYMOUS";
+ case MU_LOGGEDIN:
+ PSOUT ("* PREAUTH ["); /* already logged in, pre-authorized */
+ pcapability (1);
+ user = cpystr (s); /* copy user name */
+ pass = cpystr ("*"); /* set fake password */
+ state = SELECT; /* enter select state */
+ break;
+ default:
+ fatal ("Unknown state from myusername_full()");
+ }
+ PSOUT ("] ");
+ if (user) { /* preauthenticated as someone? */
+ PSOUT ("Pre-authenticated user ");
+ PSOUT (user);
+ PBOUT (' ');
+ }
+ }
+ else { /* login disabled */
+ PSOUT ("* BYE Service not available ");
+ state = LOGOUT;
+ }
+ PSOUT (tcp_serverhost ());
+ PSOUT (" IMAP4rev1 ");
+ PSOUT (CCLIENTVERSION);
+ PBOUT ('.');
+ PSOUT (version);
+ PSOUT (" at ");
+ PSOUT (tmp);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ switch (state) { /* do this after the banner */
+ case LOGIN:
+ autologouttime = time (0) + LOGINTIMEOUT;
+ break;
+ case SELECT:
+ syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
+ user,tcp_clienthost ());
+ break;
+ }
+
+ if (setjmp (jmpenv)) { /* die if a signal handler say so */
+ /* in case we get borked now */
+ if (setjmp (jmpenv)) _exit (1);
+ /* need to close stream gracefully? */
+ if (stream && !stream->lock && (stream->dtb->flags & DR_XPOINT))
+ stream = mail_close (stream);
+ ret = 1; /* set exit status */
+ }
+ else while (state != LOGOUT) {/* command processing loop */
+ slurp (cmdbuf,CMDLEN,TIMEOUT);
+ /* no more last error or literal */
+ if (lstwrn) fs_give ((void **) &lstwrn);
+ if (lsterr) fs_give ((void **) &lsterr);
+ if (lstref) fs_give ((void **) &lstref);
+ while (litsp) fs_give ((void **) &litstk[--litsp]);
+ /* find end of line */
+ if (!strchr (cmdbuf,'\012')) {
+ if (t = strchr (cmdbuf,' ')) *t = '\0';
+ if ((t - cmdbuf) > 100) t = NIL;
+ flush (); /* flush excess */
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Line too long before authentication host=%.80s",
+ tcp_clienthost ());
+ sprintf (tmp,response,t ? (char *) cmdbuf : "*");
+ PSOUT (tmp);
+ }
+ else if (!(tag = strtok (cmdbuf," \015\012"))) {
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Null command before authentication host=%.80s",
+ tcp_clienthost ());
+ PSOUT ("* BAD Null command\015\012");
+ }
+ else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag\015\012");
+ else if (!(s = strtok (NIL," \015\012"))) {
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Missing command before authentication host=%.80s",
+ tcp_clienthost ());
+ PSOUT (tag);
+ PSOUT (" BAD Missing command\015\012");
+ }
+ else { /* parse command */
+ response = win; /* set default response */
+ finding = NIL; /* no longer FINDing */
+ ucase (s); /* canonicalize command case */
+ /* UID command? */
+ if (!strcmp (s,"UID") && strtok (NIL," \015\012")) {
+ uid = T; /* a UID command */
+ s[3] = ' '; /* restore the space delimiter */
+ ucase (s); /* make sure command all uppercase */
+ }
+ else uid = NIL; /* not a UID command */
+ /* flush previous saved command */
+ if (cmd) fs_give ((void **) &cmd);
+ cmd = cpystr (s); /* save current command */
+ /* snarf argument, see if possible litplus */
+ if ((arg = strtok (NIL,"\015\012")) && ((i = strlen (arg)) > 3) &&
+ (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (arg[i]); i--);
+ if (arg[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (arg + i + 1,NIL,10);
+ }
+ }
+
+ /* these commands always valid */
+ if (!strcmp (cmd,"NOOP")) {
+ if (arg) response = badarg;
+ else if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ else if (!strcmp (cmd,"LOGOUT")) {
+ if (arg) response = badarg;
+ else { /* time to say farewell */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ if (lastsel) fs_give ((void **) &lastsel);
+ if (state == OPEN) stream = mail_close (stream);
+ state = LOGOUT;
+ PSOUT ("* BYE ");
+ PSOUT (mylocalhost ());
+ PSOUT (" IMAP4rev1 server terminating connection\015\012");
+ }
+ }
+ else if (!strcmp (cmd,"CAPABILITY")) {
+ if (arg) response = badarg;
+ else {
+ PSOUT ("* ");
+ pcapability (0); /* print capabilities */
+ CRLF;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+#ifdef NETSCAPE_BRAIN_DAMAGE
+ else if (!strcmp (cmd,"NETSCAPE")) {
+ PSOUT ("* OK [NETSCAPE]\015\012* VERSION 1.0 UNIX\015\012* ACCOUNT-URL \"");
+ PSOUT (NETSCAPE_BRAIN_DAMAGE);
+ PBOUT ('"');
+ CRLF;
+ }
+#endif
+
+ else switch (state) { /* dispatch depending upon state */
+ case LOGIN: /* waiting to get logged in */
+ /* new style authentication */
+ if (!strcmp (cmd,"AUTHENTICATE")) {
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ initial = NIL; /* no initial argument */
+ cancelled = NIL; /* not cancelled */
+ /* mandatory first argument */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg && !(initial = snarf_base64 (&arg)))
+ response = misarg; /* optional second argument */
+ else if (arg) response = badarg;
+ else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
+ if (!(s = imap_responder ("",0,NIL)))
+ response ="%.80s BAD AUTHENTICATE ANONYMOUS cancelled\015\012";
+ else if (anonymous_login (argc,argv)) {
+ anonymous = T; /* note we are anonymous */
+ user = cpystr ("ANONYMOUS");
+ pass = cpystr ("*");
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin;/* return logged-in capabilities */
+ syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
+ tcp_clienthost ());
+ fs_give ((void **) &s);
+ }
+ else response ="%.80s NO AUTHENTICATE ANONYMOUS failed\015\012";
+ }
+ else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
+ pass = cpystr ("*");
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin; /* return logged-in capabilities */
+ syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s mech=%.80s",
+ user,tcp_clienthost (),s);
+ }
+
+ else {
+ AUTHENTICATOR *auth = mail_lookup_auth (1);
+ char *msg = (char *) fs_get (strlen (cmd) + strlen (s) + 2);
+ sprintf (msg,"%s %s",cmd,s);
+ fs_give ((void **) &cmd);
+ cmd = msg;
+ for (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
+ auth && compare_cstring (s,auth->name); auth = auth->next);
+ /* Failed authentication when hidden looks like invalid command.
+ * This is intentional but confused me when I was debugging.
+ */
+ if (auth && auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ response = lose;
+ if (cancelled) {
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("cancelled by user");
+ }
+ if (!lsterr) /* catch-all */
+ lsterr = cpystr ("Invalid authentication credentials");
+ syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
+ tcp_clienthost ());
+ }
+ else {
+ response = badcmd;
+ syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s,
+ tcp_clienthost ());
+ }
+ }
+ }
+
+ /* plaintext login with password */
+ else if (!strcmp (cmd,"LOGIN")) {
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ /* two arguments */
+ if (!((user = cpystr (snarf (&arg))) &&
+ (pass = cpystr (snarf (&arg))))) response = misarg;
+ else if (arg) response = badarg;
+ /* see if we allow anonymous */
+ else if (!compare_cstring (user,"ANONYMOUS") &&
+ !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
+ anonymous = T; /* note we are anonymous */
+ ucase (user); /* make all uppercase for consistency */
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin; /* return logged-in capabilities */
+ syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
+ tcp_clienthost ());
+ }
+ else { /* delimit user from possible admin */
+ if (s = strchr (user,'*')) *s++ ='\0';
+ /* see if username and password are OK */
+ if (server_login (user,pass,s,argc,argv)) {
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin;/* return logged-in capabilities */
+ syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
+ tcp_clienthost ());
+ }
+ else {
+ response = lose;
+ if (!lsterr) lsterr = cpystr ("Invalid login credentials");
+ }
+ }
+ }
+ /* start TLS security */
+ else if (!strcmp (cmd,"STARTTLS")) {
+ if (arg) response = badarg;
+ else if (lsterr = ssl_start_tls (pgmname)) response = lose;
+ }
+ else response = badcmd;
+ break;
+
+ case OPEN: /* valid only when mailbox open */
+ /* fetch mailbox attributes */
+ if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
+ if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
+ response = misarg;
+ else if (uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s)) fetch (t,uid);
+ else response = badseq;
+ }
+ /* store mailbox attributes */
+ else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
+ /* must have three arguments */
+ if (!(arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) &&
+ (t = strtok (NIL,"\015\012")))) response = misarg;
+ else if (!(uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s))) response = badseq;
+ else {
+ f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
+ if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
+ strcpy (tmp,"\\Answered \\Flagged \\Deleted \\Draft \\Seen");
+ for (i = 0, u = tmp;
+ (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
+ if (strlen (v) <
+ ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
+ *u++ = ' '; /* write next flag */
+ strcpy (u,v);
+ }
+ mail_flag (stream,s,tmp,f & ~ST_SET);
+ }
+ else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
+ f &= ~ST_SET; /* clear flags */
+ else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
+ response = badatt;
+ break;
+ }
+ /* find last keyword */
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
+ mail_flag (stream,s,t,f);
+ /* any new keywords appeared? */
+ if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
+ /* return flags if silence not wanted */
+ if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))
+ for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
+ mail_elt (stream,i)->spare2 = (f & ST_SILENT) ? NIL : T;
+ }
+ }
+
+ /* check for new mail */
+ else if (!strcmp (cmd,"CHECK")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else if (!anonymous) {
+ mail_check (stream);
+ /* remember last check time */
+ lastcheck = time (0);
+ }
+ }
+ /* expunge deleted messages */
+ else if (!(anonymous || (strcmp (cmd,"EXPUNGE") &&
+ strcmp (cmd,"UID EXPUNGE")))) {
+ if (uid && !arg) response = misarg;
+ else if (!uid && arg) response = badarg;
+ else { /* expunge deleted or specified UIDs */
+ mail_expunge_full (stream,arg,arg ? EX_UID : NIL);
+ /* remember last checkpoint */
+ lastcheck = time (0);
+ }
+ }
+ /* close mailbox */
+ else if (!strcmp (cmd,"CLOSE") || !strcmp (cmd,"UNSELECT")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else {
+ /* no last uid */
+ uidvalidity = lastuid = 0;
+ if (lastsel) fs_give ((void **) &lastsel);
+ if (lastid) fs_give ((void **) &lastid);
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ stream = mail_close_full (stream,((*cmd == 'C') && !anonymous) ?
+ CL_EXPUNGE : NIL);
+ state = SELECT; /* no longer opened */
+ lastcheck = 0; /* no last checkpoint */
+ }
+ }
+ else if (!anonymous && /* copy message(s) */
+ (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
+ trycreate = NIL; /* no trycreate status */
+ if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"\015\012"))
+ && (t = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ else if (!nmsgs) {
+ response = lose;
+ if (!lsterr) lsterr = cpystr ("Mailbox is empty");
+ }
+ else if (!(uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s))) response = badseq;
+ /* try copy */
+ else if (!mail_copy_full (stream,s,t,uid ? CP_UID : NIL)) {
+ response = trycreate ? losetry : lose;
+ if (!lsterr) lsterr = cpystr ("No such destination mailbox");
+ }
+ }
+
+ /* sort mailbox */
+ else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
+ /* must have four arguments */
+ if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
+ (t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
+ else { /* read criteria */
+ SEARCHPGM *spg = NIL;
+ char *cs = NIL;
+ SORTPGM *pgm = NIL,*pg = NIL;
+ unsigned long *slst,*sl;
+ *t = NIL; /* tie off criteria list */
+ if (!(s = strtok (ucase (s)," "))) response = badatt;
+ else {
+ do { /* parse sort attributes */
+ if (pg) pg = pg->next = mail_newsortpgm ();
+ else pgm = pg = mail_newsortpgm ();
+ if (!strcmp (s,"REVERSE")) {
+ pg->reverse = T;
+ if (!(s = strtok (NIL," "))) {
+ s = ""; /* end of attributes */
+ break;
+ }
+ }
+ if (!strcmp (s,"DATE")) pg->function = SORTDATE;
+ else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
+ else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
+ else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
+ else if (!strcmp (s,"TO")) pg->function = SORTTO;
+ else if (!strcmp (s,"CC")) pg->function = SORTCC;
+ else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
+ else break;
+ } while (s = strtok (NIL," "));
+ /* bad SORT attribute */
+ if (s) response = badatt;
+ /* get charset and search criteria */
+ else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg &&
+ *arg)) response = misarg;
+ /* parse search criteria */
+ else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0)) response = badatt;
+ else if (arg && *arg) response = badarg;
+ else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID:NIL)) {
+ PSOUT ("* SORT");
+ for (sl = slst; *sl; sl++) {
+ PBOUT (' ');
+ pnum (*sl);
+ }
+ CRLF;
+ fs_give ((void **) &slst);
+ }
+ }
+ if (pgm) mail_free_sortpgm (&pgm);
+ if (spg) mail_free_searchpgm (&spg);
+ if (cs) fs_give ((void **) &cs);
+ }
+ }
+
+ /* thread mailbox */
+ else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
+ THREADNODE *thr;
+ SEARCHPGM *spg = NIL;
+ char *cs = NIL;
+ /* must have four arguments */
+ if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) &&
+ (cs = cpystr (cs)) && (arg = strtok (NIL,"\015\012"))))
+ response = misarg;
+ else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0)) response = badatt;
+ else if (arg && *arg) response = badarg;
+ else {
+ if (thr = mail_thread (stream,s,cs,spg,uid ? SE_UID : NIL)) {
+ PSOUT ("* THREAD ");
+ pthread (thr);
+ mail_free_threadnode (&thr);
+ }
+ else PSOUT ("* THREAD");
+ CRLF;
+ }
+ if (spg) mail_free_searchpgm (&spg);
+ if (cs) fs_give ((void **) &cs);
+ }
+
+ /* search mailbox */
+ else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
+ int retval = NIL;
+ char *charset = NIL;
+ SEARCHPGM *pgm;
+ response = misarg; /* assume failure */
+ if (!arg) break; /* one or more arguments required */
+ if (((arg[0] == 'R') || (arg[0] == 'r')) &&
+ ((arg[1] == 'E') || (arg[1] == 'e')) &&
+ ((arg[2] == 'T') || (arg[2] == 't')) &&
+ ((arg[3] == 'U') || (arg[3] == 'u')) &&
+ ((arg[4] == 'R') || (arg[4] == 'r')) &&
+ ((arg[5] == 'N') || (arg[5] == 'n')) &&
+ (arg[6] == ' ') && (arg[7] == '(')) {
+ retval = 0x4000; /* return is specified */
+ for (arg += 8; *arg && (*arg != ')'); ) {
+ if (((arg[0] == 'M') || (arg[0] == 'm')) &&
+ ((arg[1] == 'I') || (arg[1] == 'i')) &&
+ ((arg[2] == 'N') || (arg[2] == 'n')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x1;
+ arg += 3;
+ }
+ else if (((arg[0] == 'M') || (arg[0] == 'm')) &&
+ ((arg[1] == 'A') || (arg[1] == 'a')) &&
+ ((arg[2] == 'X') || (arg[2] == 'x')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x2;
+ arg += 3;
+ }
+ else if (((arg[0] == 'A') || (arg[0] == 'a')) &&
+ ((arg[1] == 'L') || (arg[1] == 'l')) &&
+ ((arg[2] == 'L') || (arg[2] == 'l')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x4;
+ arg += 3;
+ }
+ else if (((arg[0] == 'C') || (arg[0] == 'c')) &&
+ ((arg[1] == 'O') || (arg[1] == 'o')) &&
+ ((arg[2] == 'U') || (arg[2] == 'u')) &&
+ ((arg[3] == 'N') || (arg[3] == 'n')) &&
+ ((arg[4] == 'T') || (arg[4] == 't')) &&
+ ((arg[5] == ' ') || (arg[5] == ')'))) {
+ retval |= 0x10;
+ arg += 5;
+ }
+ else break; /* unknown return value */
+ /* more return values to come */
+ if ((*arg == ' ') && (arg[1] != ')')) ++arg;
+ }
+ /* RETURN list must be properly terminated */
+ if ((*arg++ != ')') || (*arg++ != ' ')) break;
+ /* default return value is ALL */
+ if (!(retval &= 0x3fff)) retval = 0x4;
+ }
+
+ /* character set specified? */
+ if (((arg[0] == 'C') || (arg[0] == 'c')) &&
+ ((arg[1] == 'H') || (arg[1] == 'h')) &&
+ ((arg[2] == 'A') || (arg[2] == 'a')) &&
+ ((arg[3] == 'R') || (arg[3] == 'r')) &&
+ ((arg[4] == 'S') || (arg[4] == 's')) &&
+ ((arg[5] == 'E') || (arg[5] == 'e')) &&
+ ((arg[6] == 'T') || (arg[6] == 't')) &&
+ (arg[7] == ' ')) {
+ arg += 8; /* yes, skip over CHARSET token */
+ if (s = snarf (&arg)) charset = cpystr (s);
+ else break; /* missing character set */
+ }
+ /* must have arguments here */
+ if (!(arg && *arg)) break;
+ if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0) && !*arg) {
+ response = win; /* looks good, try the search */
+ mail_search_full (stream,charset,pgm,SE_FREE);
+ /* output search results if success */
+ if (response == win) {
+ if (retval) { /* ESEARCH desired */
+ PSOUT ("* ESEARCH (TAG ");
+ pstring (tag);
+ PBOUT (')');
+ if (uid) PSOUT (" UID");
+ /* wants MIN */
+ if (retval & 0x1) {
+ for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
+ ++i);
+ if (i <= nmsgs) {
+ PSOUT (" MIN ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ /* wants MAX */
+ if (retval & 0x2) {
+ for (i = nmsgs; i && !mail_elt (stream,i)->searched; --i);
+ if (i) {
+ PSOUT (" MAX ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+
+ /* wants ALL */
+ if (retval & 0x4) {
+ unsigned long j;
+ /* find first match */
+ for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
+ ++i);
+ if (i <= nmsgs) {
+ PSOUT (" ALL ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ j = i; /* last message output */
+ }
+ while (++i <= nmsgs) {
+ if (mail_elt (stream,i)->searched) {
+ while ((++i <= nmsgs) && mail_elt (stream,i)->searched);
+ /* previous message is end of range */
+ if (j != --i) {
+ PBOUT (':');
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ /* search for next match */
+ while ((++i <= nmsgs) && !mail_elt (stream,i)->searched);
+ if (i <= nmsgs) {
+ PBOUT (',');
+ pnum (uid ? mail_uid (stream,i) : i);
+ j = i; /* last message output */
+ }
+ }
+ }
+ /* wants COUNT */
+ if (retval & 0x10) {
+ unsigned long j;
+ for (i = 1, j = 0; i <= nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) ++j;
+ PSOUT (" COUNT ");
+ pnum (j);
+ }
+ }
+ else { /* standard search */
+ PSOUT ("* SEARCH");
+ for (i = 1; i <= nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ PBOUT (' ');
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ CRLF;
+ }
+ }
+ else mail_free_searchpgm (&pgm);
+ if (charset) fs_give ((void **) &charset);
+ }
+
+ else /* fall into select case */
+ case SELECT: /* valid whenever logged in */
+ /* select new mailbox */
+ if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
+ strcmp (cmd,"BBOARD"))) {
+ /* single argument */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else if (nameok (NIL,s = bboardname (cmd,s))) {
+ DRIVER *factory = mail_valid (NIL,s,NIL);
+ f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) |
+ ((*cmd == 'S') ? NIL : OP_READONLY);
+ curdriver = NIL; /* no drivers known */
+ /* no last uid */
+ uidvalidity = lastuid = 0;
+ if (lastid) fs_give ((void **) &lastid);
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ nflags = 0; /* force update */
+ nmsgs = recent = 0xffffffff;
+ if (factory && !strcmp (factory->name,"phile") &&
+ (stream = mail_open (stream,s,f | OP_SILENT)) &&
+ (response == win)) {
+ BODY *b;
+ /* see if proxy open */
+ if ((mail_elt (stream,1)->rfc822_size < 400) &&
+ mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
+ (t = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
+ (i < MAILTMPLEN) && (t[0] == '{')) {
+ /* copy and tie off */
+ strncpy (tmp,t,i)[i] = '\0';
+ /* nuke any trailing newline */
+ if (t = strpbrk (tmp,"\r\n")) *t = '\0';
+ /* try to open proxy */
+ if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
+ (response == win) && tstream->nmsgs) {
+ s = tmp; /* got it, close the link */
+ mail_close (stream);
+ stream = tstream;
+ tstream = NIL;
+ }
+ }
+ /* now give the exists event */
+ stream->silent = NIL;
+ mm_exists (stream,stream->nmsgs);
+ }
+ else if (!factory && isnewsproxy (s)) {
+ sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
+ stream = mail_open (stream,tmp,f);
+ }
+ /* open stream normally then */
+ else stream = mail_open (stream,s,f);
+
+ if (stream && (response == win)) {
+ state = OPEN; /* note state open */
+ if (lastsel) fs_give ((void **) &lastsel);
+ /* canonicalize INBOX */
+ if (!compare_cstring (s,"#MHINBOX"))
+ lastsel = cpystr ("#MHINBOX");
+ else lastsel = cpystr (compare_cstring (s,"INBOX") ?
+ (char *) s : "INBOX");
+ /* note readonly/readwrite */
+ response = stream->rdonly ? rowin : rwwin;
+ if (anonymous)
+ syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
+ stream->mailbox,tcp_clienthost ());
+ lastcheck = 0; /* no last check */
+ }
+ else { /* failed, nuke old selection */
+ if (stream) stream = mail_close (stream);
+ state = SELECT; /* no mailbox open now */
+ if (lastsel) fs_give ((void **) &lastsel);
+ response = lose; /* open failed */
+ }
+ }
+ }
+
+ /* APPEND message to mailbox */
+ else if (!(anonymous || strcmp (cmd,"APPEND"))) {
+ /* parse mailbox name */
+ if ((s = snarf (&arg)) && arg) {
+ STRING st; /* message stringstruct */
+ APPENDDATA ad;
+ ad.arg = arg; /* command arguments */
+ /* no message yet */
+ ad.flags = ad.date = ad.msg = NIL;
+ ad.message = &st; /* pointer to stringstruct to use */
+ trycreate = NIL; /* no trycreate status */
+ if (!mail_append_multiple (NIL,s,append_msg,(void *) &ad)) {
+ if (response == win) response = trycreate ? losetry : lose;
+ /* this can happen with #driver. hack */
+ if (!lsterr) lsterr = cpystr ("No such destination mailbox");
+ }
+ /* clean up any message text left behind */
+ if (ad.flags) fs_give ((void **) &ad.flags);
+ if (ad.date) fs_give ((void **) &ad.date);
+ if (ad.msg) fs_give ((void **) &ad.msg);
+ }
+ else response = misarg;
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* list mailboxes */
+ else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
+ /* get reference and mailbox argument */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
+ response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,LONGT)) {
+ proxylist = T;
+ mail_list (NIL,"",tmp);
+ proxylist = NIL;
+ }
+ else mail_list (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* scan mailboxes */
+ else if (!strcmp (cmd,"SCAN")) {
+ /* get arguments */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
+ (u = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,NIL))
+ mm_log ("SCAN not permitted for news",ERROR);
+ else mail_scan (NIL,s,t,u);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* list subscribed mailboxes */
+ else if (!strcmp (cmd,"LSUB") || !strcmp (cmd,"RLSUB")) {
+ /* get reference and mailbox argument */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
+ response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,NIL)) newsrc_lsub (NIL,tmp);
+ else mail_lsub (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* find mailboxes */
+ else if (!strcmp (cmd,"FIND")) {
+ /* get subcommand and true argument */
+ if (!(arg && (s = strtok (arg," \015\012")) && (s == cmd + 5) &&
+ (cmd[4] = ' ') && ucase (s) &&
+ (arg = strtok (NIL,"\015\012")) && (s = snarf_list (&arg))))
+ response = misarg; /* missing required argument */
+ else if (arg) response = badarg;
+ /* punt on single-char wildcards */
+ else if (strpbrk (s,"%?")) response =
+ "%.80s NO IMAP2 ? and %% wildcards not supported: %.80s\015\012";
+ else if (nameok (NIL,s)) {
+ finding = T; /* note that we are FINDing */
+ /* dispatch based on type */
+ if (!strcmp (cmd,"FIND MAILBOXES") && !anonymous)
+ mail_lsub (NIL,NIL,s);
+ else if (!strcmp (cmd,"FIND ALL.MAILBOXES")) {
+ /* convert * to % for compatible behavior */
+ for (t = s; *t; t++) if (*t == '*') *t = '%';
+ mail_list (NIL,NIL,s);
+ }
+ else response = badcmd;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* status of mailbox */
+ else if (!strcmp (cmd,"STATUS")) {
+ if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
+ (t = strchr (arg,')')) && (t - arg) && !t[1]))
+ response = misarg;
+ else {
+ f = NIL; /* initially no flags */
+ *t = '\0'; /* tie off flag string */
+ /* read flags */
+ t = strtok (ucase (arg)," ");
+ do { /* parse each one; unknown generate warning */
+ if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
+ else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
+ else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
+ else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
+ else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
+ else {
+ PSOUT ("* NO Unknown status flag ");
+ PSOUT (t);
+ CRLF;
+ }
+ } while (t = strtok (NIL," "));
+ ping_mailbox (uid); /* in case the fool did STATUS on open mbx */
+ PFLUSH (); /* make sure stdout is dumped in case slave */
+ if (!compare_cstring (s,"INBOX")) s = "INBOX";
+ else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
+ if (state == LOGOUT) response = lose;
+ /* get mailbox status */
+ else if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox)))) {
+ unsigned long unseen;
+ /* snarl at cretins which do this */
+ PSOUT ("* NO CLIENT BUG DETECTED: STATUS on selected mailbox: ");
+ PSOUT (s);
+ CRLF;
+ tmp[0] = ' '; tmp[1] = '\0';
+ if (f & SA_MESSAGES)
+ sprintf (tmp + strlen (tmp)," MESSAGES %lu",stream->nmsgs);
+ if (f & SA_RECENT)
+ sprintf (tmp + strlen (tmp)," RECENT %lu",stream->recent);
+ if (f & SA_UNSEEN) {
+ for (i = 1,unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) unseen++;
+ sprintf (tmp + strlen (tmp)," UNSEEN %lu",unseen);
+ }
+ if (f & SA_UIDNEXT)
+ sprintf (tmp + strlen (tmp)," UIDNEXT %lu",stream->uid_last+1);
+ if (f & SA_UIDVALIDITY)
+ sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",
+ stream->uid_validity);
+ tmp[1] = '(';
+ strcat (tmp,")\015\012");
+ PSOUT ("* STATUS ");
+ pastring (s);
+ PSOUT (tmp);
+ }
+ else if (isnewsproxy (s)) {
+ sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
+ if (!mail_status (NIL,tmp,f)) response = lose;
+ }
+ else if (!mail_status (NIL,s,f)) response = lose;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* subscribe to mailbox */
+ else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
+ /* get <mailbox> or MAILBOX <mailbox> */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) { /* IMAP2bis form */
+ if (compare_cstring (s,"MAILBOX")) response = badarg;
+ else if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else mail_subscribe (NIL,s);
+ }
+ else if (isnewsproxy (s)) newsrc_update (NIL,s+6,':');
+ else mail_subscribe (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* unsubscribe to mailbox */
+ else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
+ /* get <mailbox> or MAILBOX <mailbox> */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) { /* IMAP2bis form */
+ if (compare_cstring (s,"MAILBOX")) response = badarg;
+ else if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else if (isnewsproxy (s)) newsrc_update (NIL,s+6,'!');
+ else mail_unsubscribe (NIL,s);
+ }
+ else mail_unsubscribe (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ else if (!strcmp (cmd,"NAMESPACE")) {
+ if (arg) response = badarg;
+ else {
+ NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
+ NIL);
+ NAMESPACE *n;
+ PARAMETER *p;
+ PSOUT ("* NAMESPACE");
+ if (ns) for (i = 0; i < 3; i++) {
+ if (n = ns[i]) {
+ PSOUT (" (");
+ do {
+ PBOUT ('(');
+ pstring (n->name);
+ switch (n->delimiter) {
+ case '\\': /* quoted delimiter */
+ case '"':
+ PSOUT (" \"\\\\\"");
+ break;
+ case '\0': /* no delimiter */
+ PSOUT (" NIL");
+ break;
+ default: /* unquoted delimiter */
+ PSOUT (" \"");
+ PBOUT (n->delimiter);
+ PBOUT ('"');
+ break;
+ }
+ /* NAMESPACE extensions are hairy */
+ if (p = n->param) do {
+ PBOUT (' ');
+ pstring (p->attribute);
+ PSOUT (" (");
+ do pstring (p->value);
+ while (p->next && !p->next->attribute && (p = p->next));
+ PBOUT (')');
+ } while (p = p->next);
+ PBOUT (')');
+ } while (n = n->next);
+ PBOUT (')');
+ }
+ else PSOUT (" NIL");
+ }
+ else PSOUT (" NIL NIL NIL");
+ CRLF;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* create mailbox */
+ else if (!(anonymous || strcmp (cmd,"CREATE"))) {
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else mail_create (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* delete mailbox */
+ else if (!(anonymous || strcmp (cmd,"DELETE"))) {
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else { /* make sure not selected */
+ if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox))))
+ mm_log ("Can not DELETE the selected mailbox",ERROR);
+ else mail_delete (NIL,s);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* rename mailbox */
+ else if (!(anonymous || strcmp (cmd,"RENAME"))) {
+ if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ else { /* make sure not selected */
+ if (!compare_cstring (s,"INBOX")) s = "INBOX";
+ else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
+ if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox))))
+ mm_log ("Can not RENAME the selected mailbox",ERROR);
+ else mail_rename (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* idle mode */
+ else if (!strcmp (cmd,"IDLE")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else { /* tell client ready for argument */
+ unsigned long donefake = 0;
+ PSOUT ("+ Waiting for DONE\015\012");
+ PFLUSH (); /* dump output buffer */
+ /* inactivity countdown */
+ i = ((TIMEOUT) / (IDLETIMER)) + 1;
+ do { /* main idle loop */
+ if (!donefake) { /* don't ping mailbox if faking */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,
+ (void *) stream);
+ ping_mailbox (uid);
+ /* maybe do a checkpoint if not anonymous */
+ if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) {
+ mail_check (stream);
+ /* cancel likely altwin from mail_check() */
+ if (lsterr) fs_give ((void **) &lsterr);
+ if (lstwrn) fs_give ((void **) &lstwrn);
+ /* remember last checkpoint */
+ lastcheck = time (0);
+ }
+ }
+ if (lstwrn) { /* have a warning? */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+ if (!(i % 2)) { /* prevent NAT timeouts */
+ sprintf (tmp,"* OK Timeout in %lu minutes\015\012",
+ (i * IDLETIMER) / 60);
+ PSOUT (tmp);
+ }
+ /* two minutes before the end... */
+ if ((state == OPEN) && (i <= 2)) {
+ sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
+ donefake = nmsgs + 1,recent + 1);
+ PSOUT (tmp); /* prod client to wake up */
+ }
+ PFLUSH (); /* dump output buffer */
+ } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
+
+ /* time to exit idle loop */
+ if (state != LOGOUT) {
+ if (i) { /* still have time left? */
+ /* yes, read expected DONE */
+ slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
+ if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
+ ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
+ ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
+ ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
+ (((tmp[4] != '\015') || (tmp[5] != '\012')) &&
+ (tmp[4] != '\012')))
+ response = "%.80s BAD Bogus IDLE continuation\015\012";
+ if (donefake) { /* if faking at the end */
+ /* send EXPUNGE (should be just 1) */
+ while (donefake > nmsgs) {
+ sprintf (tmp,"* %lu EXPUNGE\015\012",donefake--);
+ PSOUT (tmp);
+ }
+ sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
+ nmsgs,recent);
+ PSOUT (tmp);
+ }
+ }
+ else clkint (); /* otherwise do autologout action */
+ }
+ }
+ }
+ else response = badcmd;
+ break;
+ default:
+ response = "%.80s BAD Unknown state for %.80s command\015\012";
+ break;
+ }
+
+ while (litplus.ok) { /* any unread LITERAL+? */
+ litplus.ok = NIL; /* yes, cancel it now */
+ clearerr (stdin); /* clear stdin errors */
+ status = "discarding unread literal";
+ /* read literal and discard it */
+ while (i = (litplus.size > MAILTMPLEN) ? MAILTMPLEN : litplus.size) {
+ if (state == LOGOUT) litplus.size = 0;
+ else {
+ settimeout (INPUTTIMEOUT);
+ if (PSINR (tmp,i)) litplus.size -= i;
+ else {
+ ioerror (stdin,status);
+ litplus.size = 0; /* in case it continues */
+ }
+ }
+ }
+ settimeout (0); /* stop timeout */
+ /* get new command tail */
+ slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
+ /* locate end of line */
+ if (t = strchr (tmp,'\012')) {
+ /* back over CR */
+ if ((t > tmp) && (t[-1] == '\015')) --t;
+ *t = NIL; /* tie off CRLF */
+ /* possible LITERAL+? */
+ if (((i = strlen (tmp)) > 3) && (tmp[i - 1] == '}') &&
+ (tmp[i - 2] == '+') && isdigit (tmp[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (tmp[i]); i--);
+ if (tmp[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (tmp + i + 1,NIL,10);
+ }
+ }
+ }
+ else flush (); /* overlong line after LITERAL+, punt */
+ }
+ ping_mailbox (uid); /* update mailbox status before response */
+ if (lstwrn && lsterr) { /* output most recent warning */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+
+ if (response == logwin) { /* authentication win message */
+ sprintf (tmp,response,lstref ? "*" : tag);
+ PSOUT (tmp); /* start response */
+ pcapability (1); /* print logged-in capabilities */
+ PSOUT ("] User ");
+ PSOUT (user);
+ PSOUT (" authenticated\015\012");
+ if (lstref) {
+ sprintf (tmp,response,tag);
+ PSOUT (tmp); /* start response */
+ PSOUT ("[REFERRAL ");
+ PSOUT (lstref);
+ PSOUT ("] ");
+ PSOUT (lasterror ());
+ CRLF;
+ }
+ }
+ else if ((response == win) || (response == lose)) {
+ sprintf (tmp,response,tag);
+ PSOUT (tmp);
+ if (cauidvalidity) { /* COPYUID/APPENDUID response? */
+ sprintf (tmp,"[%.80sUID %lu ",(char *)
+ ((s = strchr (cmd,' ')) ? s+1 : cmd),cauidvalidity);
+ PSOUT (tmp);
+ cauidvalidity = 0; /* cancel response for future */
+ if (csset) {
+ pset (&csset);
+ PBOUT (' ');
+ }
+ pset (&caset);
+ PSOUT ("] ");
+ }
+ else if (lstref) { /* have a referral? */
+ PSOUT ("[REFERRAL ");
+ PSOUT (lstref);
+ PSOUT ("] ");
+ }
+ if (lsterr || lstwrn) PSOUT (lasterror ());
+ else {
+ PSOUT (cmd);
+ PSOUT ((response == win) ? " completed" : "failed");
+ }
+ CRLF;
+ }
+ else { /* normal response */
+ if ((response == rowin) || (response == rwwin)) {
+ if (lstwrn) { /* output most recent warning */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+ }
+ sprintf (tmp,response,tag,cmd,lasterror ());
+ PSOUT (tmp); /* output response */
+ }
+ }
+ PFLUSH (); /* make sure output blatted */
+
+ if (autologouttime) { /* have an autologout in effect? */
+ /* cancel if no longer waiting for login */
+ if (state != LOGIN) autologouttime = 0;
+ /* took too long to login */
+ else if (autologouttime < time (0)) {
+ logout = goodbye = "Autologout";
+ stream = NIL;
+ state = LOGOUT; /* sayonara */
+ }
+ }
+ }
+ if (goodbye && !quell_events){/* have a goodbye message? */
+ PSOUT ("* BYE "); /* utter it */
+ PSOUT (goodbye);
+ CRLF;
+ PFLUSH (); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL))
+ (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (ret); /* all done */
+ return ret; /* stupid compilers */
+}
+
+/* Ping mailbox during each cycle. Also check alerts
+ * Accepts: last command was UID flag
+ */
+
+void ping_mailbox (unsigned long uid)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ if (state == OPEN) {
+ if (!mail_ping (stream)) { /* make sure stream still alive */
+ PSOUT ("* BYE ");
+ PSOUT (mylocalhost ());
+ PSOUT (" Fatal mailbox error: ");
+ PSOUT (lasterror ());
+ CRLF;
+ stream = NIL; /* don't try to clean up stream */
+ state = LOGOUT; /* go away */
+ syslog (LOG_INFO,
+ "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ lasterror ());
+ return;
+ }
+ /* change in number of messages? */
+ if (existsquelled || (nmsgs != stream->nmsgs)) {
+ PSOUT ("* ");
+ pnum (nmsgs = stream->nmsgs);
+ PSOUT (" EXISTS\015\012");
+ }
+ /* change in recent messages? */
+ if (existsquelled || (recent != stream->recent)) {
+ PSOUT ("* ");
+ pnum (recent = stream->recent);
+ PSOUT (" RECENT\015\012");
+ }
+ existsquelled = NIL; /* don't do this until asked again */
+ if (stream->uid_validity && (stream->uid_validity != uidvalidity)) {
+ PSOUT ("* OK [UIDVALIDITY ");
+ pnum (stream->uid_validity);
+ PSOUT ("] UID validity status\015\012* OK [UIDNEXT ");
+ pnum (stream->uid_last + 1);
+ PSOUT ("] Predicted next UID\015\012");
+ if (stream->uid_nosticky) {
+ PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
+ PSOUT (stream->mailbox);
+ CRLF;
+ }
+ uidvalidity = stream->uid_validity;
+ }
+
+ /* don't bother if driver changed */
+ if (curdriver == stream->dtb) {
+ /* first report any new flags */
+ if ((nflags < NUSERFLAGS) && stream->user_flags[nflags])
+ new_flags (stream);
+ for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
+ PSOUT ("* ");
+ pnum (i);
+ PSOUT (" FETCH (");
+ fetch_flags (i,NIL); /* output changed flags */
+ if (uid) { /* need to include UIDs in response? */
+ PBOUT (' ');
+ fetch_uid (i,NIL);
+ }
+ PSOUT (")\015\012");
+ }
+ }
+ else { /* driver changed */
+ new_flags (stream); /* send mailbox flags */
+ if (curdriver) { /* note readonly/write if possible change */
+ PSOUT ("* OK [READ-");
+ PSOUT (stream->rdonly ? "ONLY" : "WRITE");
+ PSOUT ("] Mailbox status\015\012");
+ }
+ curdriver = stream->dtb;
+ if (nmsgs) { /* get flags for all messages */
+ sprintf (tmp,"1:%lu",nmsgs);
+ mail_fetch_flags (stream,tmp,NIL);
+ /* don't do this if newsrc already did */
+ if (!(curdriver->flags & DR_NEWS)) {
+ /* find first unseen message */
+ for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
+ if (i <= nmsgs) {
+ PSOUT ("* OK [UNSEEN ");
+ pnum (i);
+ PSOUT ("] first unseen message in ");
+ PSOUT (stream->mailbox);
+ CRLF;
+ }
+ }
+ }
+ }
+ }
+ if (shutdowntime && (time (0) > shutdowntime + SHUTDOWNTIMER)) {
+ PSOUT ("* BYE Server shutting down\015\012");
+ state = LOGOUT;
+ }
+ /* don't do these stat()s every cycle */
+ else if (time (0) > alerttime + ALERTTIMER) {
+ struct stat sbuf;
+ /* have a shutdown file? */
+ if (!stat (SHUTDOWNFILE,&sbuf)) {
+ PSOUT ("* OK [ALERT] Server shutting down shortly\015\012");
+ shutdowntime = time (0);
+ }
+ alerttime = time (0); /* output any new alerts */
+ sysalerttime = palert (ALERTFILE,sysalerttime);
+ if (state != LOGIN) /* do user alert if logged in */
+ useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
+ }
+}
+
+/* Print an alert file
+ * Accepts: path of alert file
+ * time of last printed alert file
+ * Returns: updated time of last printed alert file
+ */
+
+time_t palert (char *file,time_t oldtime)
+{
+ FILE *alf;
+ struct stat sbuf;
+ int c,lc = '\012';
+ /* have a new alert file? */
+ if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
+ !(alf = fopen (file,"r"))) return oldtime;
+ /* yes, display it */
+ while ((c = getc (alf)) != EOF) {
+ if (lc == '\012') PSOUT ("* OK [ALERT] ");
+ switch (c) { /* output character */
+ case '\012': /* newline means do CRLF */
+ CRLF;
+ case '\015': /* flush CRs */
+ case '\0': /* flush nulls */
+ break;
+ default:
+ PBOUT (c); /* output all other characters */
+ break;
+ }
+ lc = c; /* note previous character */
+ }
+ fclose (alf);
+ if (lc != '\012') CRLF; /* final terminating CRLF */
+ return sbuf.st_mtime; /* return updated last alert time */
+}
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void msg_string_init (STRING *s,void *data,unsigned long size)
+{
+ MSGDATA *md = (MSGDATA *) data;
+ s->data = data; /* note stream/msgno and header length */
+#if 0
+ s->size = size; /* message size */
+ s->curpos = s->chunk = /* load header */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
+ FT_PREFETCHTEXT | FT_PEEK);
+#else /* This kludge is necessary because of broken mail stores */
+ mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
+ s->curpos = s->chunk = /* load header */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PEEK);
+ s->size += s->data1; /* header + body size */
+#endif
+ s->cursize = s->chunksize = s->data1;
+ s->offset = 0; /* offset is start of message */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char msg_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void msg_string_setpos (STRING *s,unsigned long i)
+{
+ MSGDATA *md = (MSGDATA *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+/* Send flags for stream
+ * Accepts: MAIL stream
+ * scratch buffer
+ */
+
+void new_flags (MAILSTREAM *stream)
+{
+ int i,c;
+ PSOUT ("* FLAGS (");
+ for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
+ PSOUT (stream->user_flags[i]);
+ PBOUT (' ');
+ nflags = i + 1;
+ }
+ PSOUT ("\\Answered \\Flagged \\Deleted \\Draft \\Seen)\015\012* OK [PERMANENTFLAGS (");
+ for (i = c = 0; i < NUSERFLAGS; i++)
+ if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
+ put_flag (&c,stream->user_flags[i]);
+ if (stream->kwd_create) put_flag (&c,"\\*");
+ if (stream->perm_answered) put_flag (&c,"\\Answered");
+ if (stream->perm_flagged) put_flag (&c,"\\Flagged");
+ if (stream->perm_deleted) put_flag (&c,"\\Deleted");
+ if (stream->perm_draft) put_flag (&c,"\\Draft");
+ if (stream->perm_seen) put_flag (&c,"\\Seen");
+ PSOUT (")] Permanent flags\015\012");
+}
+
+/* Set timeout
+ * Accepts: desired interval
+ */
+
+void settimeout (unsigned int i)
+{
+ /* limit if not logged in */
+ if (i) alarm ((state == LOGIN) ? LOGINTIMEOUT : i);
+ else alarm (0);
+}
+
+
+/* Clock interrupt
+ * Returns only if critical code in progress
+ */
+
+void clkint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "Autologout";
+ goodbye = "Autologout (idle for too long)";
+ if (critical) { /* must defer if in critical code(?) */
+ close (0); /* kill stdin */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+
+/* Kiss Of Death interrupt
+ * Returns only if critical code in progress
+ */
+
+void kodint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = goodbye = "Killed (lost mailbox lock)";
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+/* Hangup interrupt
+ * Returns only if critical code in progress
+ */
+
+void hupint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "Hangup";
+ goodbye = NIL; /* other end is already gone */
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+
+/* Termination interrupt
+ * Returns only if critical code in progress
+ */
+
+void trmint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = goodbye = "Killed (terminated)";
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions
+ */
+ stream = NIL;
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+/* The routines on this and the next page eschew the use of non-syscall libc
+ * routines (especially stdio) for a reason. Also, these hideous #if
+ * condtionals need to be replaced.
+ */
+
+#ifndef unix
+#define unix 0
+#endif
+
+
+/* Status request interrupt
+ * Always returns
+ */
+
+void staint (void)
+{
+#if unix
+ int fd;
+ char *s,buf[8*MAILTMPLEN];
+ unsigned long pid = getpid ();
+ /* build file name */
+ s = nout (sout (buf,"/tmp/imapd-status."),pid,10);
+ if (user) s = sout (sout (s,"."),user);
+ *s = '\0'; /* tie off file name */
+ if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) {
+ fchmod (fd,0666);
+ s = nout (sout (buf,"PID="),pid,10);
+ if (user) s = sout (sout (s,", user="),user);
+ switch (state) {
+ case LOGIN:
+ s = sout (s,", not logged in");
+ break;
+ case SELECT:
+ s = sout (s,", logged in");
+ break;
+ case OPEN:
+ s = sout (s,", mailbox open");
+ break;
+ case LOGOUT:
+ s = sout (s,", logging out");
+ break;
+ }
+ if (stream && stream->mailbox)
+ s = sout (sout (s,"\nmailbox="),stream->mailbox);
+ *s++ = '\n';
+ if (status) {
+ s = sout (s,status);
+ if (cmd) s = sout (sout (s,", last command="),cmd);
+ }
+ else s = sout (sout (s,cmd)," in progress");
+ *s++ = '\n';
+ write (fd,buf,s-buf);
+ close (fd);
+ }
+#endif
+}
+
+/* Write string
+ * Accepts: destination string pointer
+ * string
+ * Returns: updated string pointer
+ */
+
+char *sout (char *s,char *t)
+{
+ while (*t) *s++ = *t++;
+ return s;
+}
+
+
+/* Write number
+ * Accepts: destination string pointer
+ * number
+ * base
+ * Returns: updated string pointer
+ */
+
+char *nout (char *s,unsigned long n,unsigned long base)
+{
+ char stack[256];
+ char *t = stack;
+ /* push PID digits on stack */
+ do *t++ = (char) (n % base) + '0';
+ while (n /= base);
+ /* pop digits from stack */
+ while (t > stack) *s++ = *--t;
+ return s;
+}
+
+/* Slurp a command line
+ * Accepts: buffer pointer
+ * buffer size
+ * input timeout
+ */
+
+void slurp (char *s,int n,unsigned long timeout)
+{
+ memset (s,'\0',n); /* zap buffer */
+ if (state != LOGOUT) { /* get a command under timeout */
+ settimeout (timeout);
+ clearerr (stdin); /* clear stdin errors */
+ status = "reading line";
+ if (!PSIN (s,n-1)) ioerror (stdin,status);
+ settimeout (0); /* make sure timeout disabled */
+ status = NIL;
+ }
+}
+
+
+/* Read a literal
+ * Accepts: destination buffer (must be size+1 for trailing NUL)
+ * size of buffer (must be less than 4294967295)
+ */
+
+void inliteral (char *s,unsigned long n)
+{
+ unsigned long i;
+ if (litplus.ok) { /* no more LITERAL+ to worry about */
+ litplus.ok = NIL;
+ litplus.size = 0;
+ }
+ else { /* otherwise tell client ready for argument */
+ PSOUT ("+ Ready for argument\015\012");
+ PFLUSH (); /* dump output buffer */
+ }
+ clearerr (stdin); /* clear stdin errors */
+ memset (s,'\0',n+1); /* zap buffer */
+ status = "reading literal";
+ while (n) { /* get data under timeout */
+ if (state == LOGOUT) n = 0;
+ else {
+ settimeout (INPUTTIMEOUT);
+ i = min (n,8192); /* must read at least 8K within timeout */
+ if (PSINR (s,i)) {
+ s += i;
+ n -= i;
+ }
+ else {
+ ioerror (stdin,status);
+ n = 0; /* in case it continues */
+ }
+ settimeout (0); /* stop timeout */
+ }
+ }
+}
+
+/* Flush until newline seen
+ * Returns: NIL, always
+ */
+
+unsigned char *flush (void)
+{
+ int c;
+ if (state != LOGOUT) {
+ settimeout (INPUTTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ status = "flushing line";
+ while ((c = PBIN ()) != '\012') if (c == EOF) ioerror (stdin,status);
+ settimeout (0); /* make sure timeout disabled */
+ }
+ response = "%.80s BAD Command line too long\015\012";
+ status = NIL;
+ return NIL;
+}
+
+
+/* Report command stream error and die
+ * Accepts: stdin or stdout (whichever got the error)
+ * reason (what caller was doing)
+ */
+
+void ioerror (FILE *f,char *reason)
+{
+ static char msg[MAILTMPLEN];
+ char *s,*t;
+ if (logout) { /* say nothing if already dying */
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ /* write error string */
+ for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect",
+ t = logout = msg; *s; *t++ = *s++);
+ for (s = ", while "; *s; *t++ = *s++);
+ for (s = reason; *s; *t++ = *s++);
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+ }
+}
+
+/* Parse an IMAP astring
+ * Accepts: pointer to argument text pointer
+ * pointer to returned size
+ * pointer to returned delimiter
+ * Returns: argument
+ */
+
+unsigned char *parse_astring (unsigned char **arg,unsigned long *size,
+ unsigned char *del)
+{
+ unsigned long i;
+ unsigned char c,*s,*t,*v;
+ if (!*arg) return NIL; /* better be an argument */
+ switch (**arg) { /* see what the argument is */
+ default: /* atom */
+ for (s = t = *arg, i = 0;
+ (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
+ (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
+ (*t != '\\'); ++t,++i);
+ if (*size = i) break; /* got atom if non-empty */
+ case ')': case '%': case '*': case '\\': case '\0': case ' ':
+ return NIL; /* empty atom is a bogon */
+ case '"': /* hunt for trailing quote */
+ for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
+ /* quote next character */
+ if (c == '\\') switch (c = *t++) {
+ case '"': case '\\': break;
+ default: return NIL; /* invalid quote-next */
+ }
+ /* else must be a CHAR */
+ if (!c || (c & 0x80)) return NIL;
+ }
+ *v = '\0'; /* tie off string */
+ *size = v - s; /* return size */
+ break;
+
+ case '{': /* literal string */
+ s = *arg + 1; /* get size */
+ if (!isdigit (*s)) return NIL;
+ if ((*size = i = strtoul (s,(char **) &t,10)) > MAXCLIENTLIT) {
+ mm_notify (NIL,"Absurdly long client literal",ERROR);
+ syslog (LOG_INFO,"Overlong (%lu) client literal user=%.80s host=%.80s",
+ i,user ? (char *) user : "???",tcp_clienthost ());
+ return NIL;
+ }
+ switch (*t) { /* validate end of literal */
+ case '+': /* non-blocking literal */
+ if (*++t != '}') return NIL;
+ case '}':
+ if (!t[1]) break; /* OK if end of line */
+ default:
+ return NIL; /* bad literal */
+ }
+ if (litsp >= LITSTKLEN) { /* make sure don't overflow stack */
+ mm_notify (NIL,"Too many literals in command",ERROR);
+ return NIL;
+ }
+ /* get a literal buffer */
+ inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i);
+ /* get new command tail */
+ slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT);
+ if (!strchr (t,'\012')) return flush ();
+ /* reset strtok mechanism, tie off if done */
+ if (!strtok (t,"\015\012")) *t = '\0';
+ /* possible LITERAL+? */
+ if (((i = strlen (t)) > 3) && (t[i - 1] == '}') &&
+ (t[i - 2] == '+') && isdigit (t[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (t[i]); i--);
+ if (t[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (t + i + 1,NIL,10);
+ }
+ }
+ break;
+ }
+ if (*del = *t) { /* have a delimiter? */
+ *t++ = '\0'; /* yes, stomp on it */
+ *arg = t; /* update argument pointer */
+ }
+ else *arg = NIL; /* no more arguments */
+ return s;
+}
+
+/* Snarf a command argument (simple jacket into parse_astring())
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf (unsigned char **arg)
+{
+ unsigned long i;
+ unsigned char c;
+ unsigned char *s = parse_astring (arg,&i,&c);
+ return ((c == ' ') || !c) ? s : NIL;
+}
+
+
+/* Snarf a BASE64 argument for SASL-IR
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf_base64 (unsigned char **arg)
+{
+ unsigned char *ret = *arg;
+ unsigned char *s = ret + 1;
+ static char base64mask[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+ if (*(ret = *arg) == '='); /* easy case if zero-length argument */
+ /* must be at least one BASE64 char */
+ else if (!base64mask[*ret]) return NIL;
+ else { /* quick and dirty */
+ while (base64mask[*s++]); /* scan until end of BASE64 */
+ if (*s == '=') ++s; /* allow up to two padding chars */
+ if (*s == '=') ++s;
+ }
+ switch (*s) { /* anything following the argument? */
+ case ' ': /* another argument */
+ *s++ = '\0'; /* tie off previous argument */
+ *arg = s; /* and update argument pointer */
+ break;
+ case '\0': /* end of command */
+ *arg = NIL;
+ break;
+ default: /* syntax error */
+ return NIL;
+ }
+ return ret; /* return BASE64 string */
+}
+
+/* Snarf a list command argument (simple jacket into parse_astring())
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf_list (unsigned char **arg)
+{
+ unsigned long i;
+ unsigned char c,*s,*t;
+ if (!*arg) return NIL; /* better be an argument */
+ switch (**arg) {
+ default: /* atom and/or wildcard chars */
+ for (s = t = *arg, i = 0;
+ (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
+ (*t != '"') && (*t != '\\'); ++t,++i);
+ if (c = *t) { /* have a delimiter? */
+ *t++ = '\0'; /* stomp on it */
+ *arg = t; /* update argument pointer */
+ }
+ else *arg = NIL;
+ break;
+ case ')': case '\\': case '\0': case ' ':
+ return NIL; /* empty name is bogus */
+ case '"': /* quoted string? */
+ case '{': /* or literal? */
+ s = parse_astring (arg,&i,&c);
+ break;
+ }
+ return ((c == ' ') || !c) ? s : NIL;
+}
+
+/* Get a list of header lines
+ * Accepts: pointer to string pointer
+ * pointer to list flag
+ * Returns: string list
+ */
+
+STRINGLIST *parse_stringlist (unsigned char **s,int *list)
+{
+ char c = ' ',*t;
+ unsigned long i;
+ STRINGLIST *ret = NIL,*cur = NIL;
+ if (*s && **s == '(') { /* proper list? */
+ ++*s; /* for each item in list */
+ while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
+ /* get new block */
+ if (cur) cur = cur->next = mail_newstringlist ();
+ else cur = ret = mail_newstringlist ();
+ /* note text */
+ cur->text.data = (unsigned char *) fs_get (i + 1);
+ memcpy (cur->text.data,t,i);
+ cur->text.size = i; /* and size */
+ }
+ /* must be end of list */
+ if (c != ')') mail_free_stringlist (&ret);
+ }
+ if (t = *s) { /* need to reload strtok() state? */
+ /* end of a list? */
+ if (*list && (*t == ')') && !t[1]) *list = NIL;
+ else {
+ *--t = ' '; /* patch a space back in */
+ *--t = 'x'; /* and a hokey character before that */
+ t = strtok (t," "); /* reset to *s */
+ }
+ }
+ return ret;
+}
+
+/* Get value of UID * for criteria parsing
+ * Accepts: stream
+ * Returns: maximum UID
+ */
+
+unsigned long uidmax (MAILSTREAM *stream)
+{
+ return stream->nmsgs ? mail_uid (stream,stream->nmsgs) : 0xffffffff;
+}
+
+
+/* Parse search criteria
+ * Accepts: search program to write criteria into
+ * pointer to argument text pointer
+ * maximum message number
+ * maximum UID
+ * logical nesting depth
+ * Returns: T if success, NIL if error
+ */
+
+long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth)
+{
+ if (arg && *arg) { /* must be an argument */
+ /* parse criteria */
+ do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
+ /* as long as a space delimiter */
+ while (**arg == ' ' && (*arg)++);
+ /* failed if not end of criteria */
+ if (**arg && **arg != ')') return NIL;
+ }
+ return T; /* success */
+}
+
+/* Parse a search criterion
+ * Accepts: search program to write criterion into
+ * pointer to argument text pointer
+ * maximum message number
+ * maximum UID
+ * logical nesting depth
+ * Returns: T if success, NIL if error
+ */
+
+long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth)
+{
+ unsigned long i;
+ unsigned char c = NIL,*s,*t,*v,*tail,*del;
+ SEARCHSET **set;
+ SEARCHPGMLIST **not;
+ SEARCHOR **or;
+ SEARCHHEADER **hdr;
+ long ret = NIL;
+ /* better be an argument */
+ if ((depth > 500) || !(arg && *arg));
+ else if (**arg == '(') { /* list of criteria? */
+ (*arg)++; /* yes, parse the criteria */
+ if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
+ (*arg)++; /* skip closing paren */
+ ret = T; /* successful parse of list */
+ }
+ }
+ else { /* find end of criterion */
+ if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
+ c = *(del = tail); /* remember the delimiter */
+ *del = '\0'; /* tie off criterion */
+ switch (*ucase (s)) { /* dispatch based on character */
+ case '*': /* sequence */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (*(set = &pgm->msgno)){/* already a sequence? */
+ /* silly, but not as silly as the client! */
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
+ }
+ ret = crit_set (set,&s,maxmsg) && (tail == s);
+ break;
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (s+1,"LL")) ret = T;
+ else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
+ break;
+
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->bcc,&tail);
+ else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->before,&tail);
+ else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->body,&tail);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (s+1,"C") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->cc,&tail);
+ break;
+ case 'D': /* possible DELETED */
+ if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
+ if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
+ else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->from,&tail);
+ break;
+ case 'H': /* possible HEADER */
+ if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
+ (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
+ (t = parse_astring (&v,&i,&c))) {
+ for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
+ *hdr = mail_newsearchheader (s,t);
+ /* update tail, restore delimiter */
+ *(tail = v ? v - 1 : t + i) = c;
+ ret = T; /* success */
+ }
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->keyword,&tail);
+ break;
+ case 'L':
+ if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->larger,&tail);
+ break;
+ case 'N': /* possible NEW, NOT */
+ if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
+ else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
+ }
+ break;
+
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (s+1,"LD")) ret = pgm->old = T;
+ else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->on,&tail);
+ else if (!strcmp (s+1,"R") && c == ' ') {
+ for (or = &pgm->or; *or; or = &(*or)->next);
+ *or = mail_newsearchor ();
+ ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
+ depth+1) &&
+ (*tail == ' ') && *++tail &&
+ parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
+ }
+ else if (!strcmp (s+1,"LDER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->older,&tail);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SINCE, SUBJECT */
+ if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
+ else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->sentbefore,&tail);
+ else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->senton,&tail);
+ else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->sentsince,&tail);
+ else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->since,&tail);
+ else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->smaller,&tail);
+ else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->subject,&tail);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->text,&tail);
+ else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->to,&tail);
+ break;
+
+ case 'U': /* possible UID, UN* */
+ if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
+ if (*(set = &pgm->uid)){/* already a sequence? */
+ /* silly, but not as silly as the client! */
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
+ }
+ ret = crit_set (set,&tail,maxuid);
+ }
+ else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
+ else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
+ else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
+ else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
+ else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->unkeyword,&tail);
+ else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
+ break;
+ case 'Y': /* possible YOUNGER */
+ if (!strcmp (s+1,"OUNGER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->younger,&tail);
+ break;
+ default: /* oh dear */
+ break;
+ }
+ if (ret) { /* only bother if success */
+ *del = c; /* restore delimiter */
+ *arg = tail; /* update argument pointer */
+ }
+ }
+ return ret; /* return more to come */
+}
+
+/* Parse a search date criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_date (unsigned short *date,unsigned char **arg)
+{
+ if (*date) return NIL; /* can't double this value */
+ /* handle quoted form */
+ if (**arg != '"') return crit_date_work (date,arg);
+ (*arg)++; /* skip past opening quote */
+ if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
+ (*arg)++; /* skip closing quote */
+ return T;
+}
+
+/* Worker routine to parse a search date criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_date_work (unsigned short *date,unsigned char **arg)
+{
+ int d,m,y;
+ /* day */
+ if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
+ if (d == ' ') d = 0; /* leading space */
+ else d -= '0'; /* first digit */
+ if (isdigit (**arg)) { /* if a second digit */
+ d *= 10; /* slide over first digit */
+ d += *(*arg)++ - '0'; /* second digit */
+ }
+ if ((**arg == '-') && (y = *++(*arg))) {
+ m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
+ if ((y = *++(*arg))) {
+ m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
+ if ((y = *++(*arg))) {
+ m += (y >= 'a' ? y - 'a' : y - 'A');
+ switch (m) { /* determine the month */
+ case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
+ case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
+ case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
+ case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
+ case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
+ case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
+ case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
+ case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
+ default: return NIL;
+ }
+ if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
+ y = 0; /* init year */
+ do {
+ y *= 10; /* add this number */
+ y += *(*arg)++ - '0';
+ }
+ while (isdigit (**arg));
+ /* minimal validity check of date */
+ if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
+ /* time began on UNIX in 1970 */
+ if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
+ /* return value */
+ *date = mail_shortdate (y - BASEYEAR,m,d);
+ return T; /* success */
+ }
+ }
+ }
+ }
+ }
+ return NIL; /* else error */
+}
+
+/* Parse a search set criterion
+ * Accepts: set to write into
+ * pointer to argument text pointer
+ * maximum value permitted
+ * Returns: T if success, NIL if error
+ */
+
+long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima)
+{
+ unsigned long i = 0;
+ if (*set) return NIL; /* can't double this value */
+ *set = mail_newsearchset (); /* instantiate a new search set */
+ if (**arg == '*') { /* maxnum? */
+ (*arg)++; /* skip past that number */
+ (*set)->first = maxima;
+ }
+ else if (crit_number (&i,arg) && i) (*set)->first = i;
+ else return NIL; /* bogon */
+ switch (**arg) { /* decide based on delimiter */
+ case ':': /* sequence range */
+ i = 0; /* reset for crit_number() */
+ if (*++(*arg) == '*') { /* maxnum? */
+ (*arg)++; /* skip past that number */
+ (*set)->last = maxima;
+ }
+ else if (crit_number (&i,arg) && i) {
+ if (i < (*set)->first) { /* backwards range */
+ (*set)->last = (*set)->first;
+ (*set)->first = i;
+ }
+ else (*set)->last = i; /* set last number */
+ }
+ else return NIL; /* bogon */
+ if (**arg != ',') break; /* drop into comma case if comma seen */
+ case ',':
+ (*arg)++; /* skip past delimiter */
+ return crit_set (&(*set)->next,arg,maxima);
+ default:
+ break;
+ }
+ return T; /* return success */
+}
+
+/* Parse a search number criterion
+ * Accepts: number to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_number (unsigned long *number,unsigned char **arg)
+{
+ /* can't double this value */
+ if (*number || !isdigit (**arg)) return NIL;
+ *number = 0;
+ while (isdigit (**arg)) { /* found a digit? */
+ *number *= 10; /* add a decade */
+ *number += *(*arg)++ - '0'; /* add number */
+ }
+ return T;
+}
+
+
+/* Parse a search string criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_string (STRINGLIST **string,unsigned char **arg)
+{
+ unsigned long i;
+ char c;
+ char *s = parse_astring (arg,&i,&c);
+ if (!s) return NIL;
+ /* find tail of list */
+ while (*string) string = &(*string)->next;
+ *string = mail_newstringlist ();
+ (*string)->text.data = (unsigned char *) fs_get (i + 1);
+ memcpy ((*string)->text.data,s,i);
+ (*string)->text.data[i] = '\0';
+ (*string)->text.size = i;
+ /* if end of arguments, wrap it up here */
+ if (!*arg) *arg = (char *) (*string)->text.data + i;
+ else (*--(*arg) = c); /* back up pointer, restore delimiter */
+ return T;
+}
+
+/* Fetch message data
+ * Accepts: string of data items to be fetched (must be writeable)
+ * UID fetch flag
+ */
+
+#define MAXFETCH 100
+
+void fetch (char *t,unsigned long uid)
+{
+ fetchfn_t f[MAXFETCH +2];
+ void *fa[MAXFETCH + 2];
+ int k;
+ memset ((void *) f,NIL,sizeof (f));
+ memset ((void *) fa,NIL,sizeof (fa));
+ fetch_work (t,uid,f,fa); /* do the work */
+ /* clean up arguments */
+ for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
+}
+
+
+/* Fetch message data worker routine
+ * Accepts: string of data items to be fetched (must be writeable)
+ * UID fetch flag
+ * function dispatch vector
+ * function argument vector
+ */
+
+void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
+{
+ unsigned char *s,*v;
+ unsigned long i;
+ unsigned long k = 0;
+ BODY *b;
+ int list = NIL;
+ int parse_envs = NIL;
+ int parse_bodies = NIL;
+ if (uid) { /* need to fetch UIDs? */
+ fa[k] = NIL; /* no argument */
+ f[k++] = fetch_uid; /* push a UID fetch on the stack */
+ }
+
+ /* process macros */
+ if (!strcmp (ucase (t),"ALL"))
+ strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
+ else if (!strcmp (t,"FULL"))
+ strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
+ else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
+ if (list = (*t == '(')) t++; /* skip open paren */
+ if (s = strtok (t," ")) do { /* parse attribute list */
+ if (list && (i = strlen (s)) && (s[i-1] == ')')) {
+ list = NIL; /* done with list */
+ s[i-1] = '\0'; /* tie off last item */
+ }
+ fa[k] = NIL; /* default to no argument */
+ if (!strcmp (s,"UID")) { /* no-op if implicit */
+ if (!uid) f[k++] = fetch_uid;
+ }
+ else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
+ else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
+ else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
+ else if (!strcmp (s,"ENVELOPE")) {
+ parse_envs = T; /* we will need to parse envelopes */
+ f[k++] = fetch_envelope;
+ }
+ else if (!strcmp (s,"BODY")) {
+ parse_envs = parse_bodies = T;
+ f[k++] = fetch_body;
+ }
+ else if (!strcmp (s,"BODYSTRUCTURE")) {
+ parse_envs = parse_bodies = T;
+ f[k++] = fetch_bodystructure;
+ }
+ else if (!strcmp (s,"RFC822")) {
+ fa[k] = s[6] ? (void *) FT_PEEK : NIL;
+ f[k++] = fetch_rfc822;
+ }
+ else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
+ else if (!strcmp (s,"RFC822.TEXT")) {
+ fa[k] = s[11] ? (void *) FT_PEEK : NIL;
+ f[k++] = fetch_rfc822_text;
+ }
+
+ else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10) ||
+ !strncmp (s,"BINARY[",7) || !strncmp (s,"BINARY.PEEK[",12) ||
+ !strncmp (s,"BINARY.SIZE[",12)) {
+ TEXTARGS *ta = (TEXTARGS *)
+ memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
+ if (s[1] == 'I') { /* body or binary? */
+ ta->binary = FTB_BINARY;/* binary */
+ f[k] = fetch_body_part_binary;
+ if (s[6] == '.') { /* wanted peek or size? */
+ if (s[7] == 'P') ta->flags = FT_PEEK;
+ else ta->binary |= FTB_SIZE;
+ s += 12; /* skip to section specifier */
+ }
+ else s += 7; /* skip to section specifier */
+ if (!isdigit (*s)) { /* make sure top-level digit */
+ fs_give ((void **) &ta);
+ response = badbin;
+ return;
+ }
+ }
+ else { /* body */
+ f[k] = fetch_body_part_contents;
+ if (s[4] == '.') { /* wanted peek? */
+ ta->flags = FT_PEEK;
+ s += 10; /* skip to section specifier */
+ }
+ else s += 5; /* skip to section specifier */
+ }
+ if (*(v = s) != ']') { /* non-empty section specifier? */
+ if (isdigit (*v)) { /* have section specifier? */
+ /* need envelopes and bodies */
+ parse_envs = parse_bodies = T;
+ while (isdigit (*v)) /* scan to end of section specifier */
+ if ((*++v == '.') && isdigit (v[1])) v++;
+ /* any IMAP4rev1 stuff following? */
+ if ((*v == '.') && isalpha (v[1])) {
+ if (ta->binary) { /* not if binary you don't */
+ fs_give ((void **) &ta);
+ response = badbin;
+ return;
+ }
+ *v++ = '\0'; /* yes, tie off section specifier */
+ if (!strncmp (v,"MIME",4)) {
+ v += 4; /* found <section>.MIME */
+ f[k] = fetch_body_part_mime;
+ }
+ }
+ else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Syntax error in section specifier\015\012";
+ return;
+ }
+ }
+
+ if (*v != ']') { /* IMAP4rev1 stuff here? */
+ if (!strncmp (v,"HEADER",6)) {
+ *v = '\0'; /* tie off in case top level */
+ v += 6; /* found [<section>.]HEADER */
+ f[k] = fetch_body_part_header;
+ /* partial headers wanted? */
+ if (!strncmp (v,".FIELDS",7)) {
+ v += 7; /* yes */
+ if (!strncmp (v,".NOT",4)) {
+ v += 4; /* want to exclude named headers */
+ ta->flags |= FT_NOT;
+ }
+ if (*v || !(v = strtok (NIL,"\015\012")) ||
+ !(ta->lines = parse_stringlist (&v,&list))) {
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Syntax error in header fields\015\012";
+ return;
+ }
+ }
+ }
+ else if (!strncmp (v,"TEXT",4)) {
+ *v = '\0'; /* tie off in case top level */
+ v += 4; /* found [<section>.]TEXT */
+ f[k] = fetch_body_part_text;
+ }
+ else {
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Unknown section text specifier\015\012";
+ return;
+ }
+ }
+ }
+ /* tie off section */
+ if (*v == ']') *v++ = '\0';
+ else { /* bogon */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Section specifier not terminated\015\012";
+ return;
+ }
+
+ if ((*v == '<') && /* partial specifier? */
+ ((ta->binary & FTB_SIZE) ||
+ !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) ||
+ v) &&
+ (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) &&
+ (*v++ == '>')))) {
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);
+ response ="%.80s BAD Syntax error in partial text specifier\015\012";
+ return;
+ }
+ switch (*v) { /* what's there now? */
+ case ' ': /* more follows */
+ *--v = ' '; /* patch a space back in */
+ *--v = 'x'; /* and a hokey character before that */
+ strtok (v," "); /* reset strtok mechanism */
+ break;
+ case '\0': /* none */
+ break;
+ case ')': /* end of list */
+ if (list && !v[1]) { /* make sure of that */
+ list = NIL;
+ strtok (v," "); /* reset strtok mechanism */
+ break; /* all done */
+ }
+ /* otherwise it's a bogon, drop in */
+ default: /* bogon */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);
+ response = "%.80s BAD Syntax error after section specifier\015\012";
+ return;
+ }
+ /* make copy of section specifier */
+ if (s && *s) ta->section = cpystr (s);
+ fa[k++] = (void *) ta; /* set argument */
+ }
+ else { /* unknown attribute */
+ response = badatt;
+ return;
+ }
+ } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list);
+ else {
+ response = misarg; /* missing attribute list */
+ return;
+ }
+
+ if (s) { /* too many attributes? */
+ response = "%.80s BAD Excessively complex FETCH attribute list\015\012";
+ return;
+ }
+ if (list) { /* too many attributes? */
+ response = "%.80s BAD Unterminated FETCH attribute list\015\012";
+ return;
+ }
+ f[k] = NIL; /* tie off attribute list */
+ /* c-client clobbers sequence, use spare */
+ for (i = 1; i <= nmsgs; i++)
+ mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
+ /* for each requested message */
+ for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) {
+ /* kill if dying */
+ if (state == LOGOUT) longjmp (jmpenv,1);
+ if (mail_elt (stream,i)->spare) {
+ /* parse envelope, set body, do warnings */
+ if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
+ quell_events = T; /* can't do any events now */
+ PSOUT ("* "); /* leader */
+ pnum (i);
+ PSOUT (" FETCH (");
+ (*f[0]) (i,fa[0]); /* do first attribute */
+ /* for each subsequent attribute */
+ for (k = 1; f[k] && (response != loseunknowncte); k++) {
+ PBOUT (' '); /* delimit with space */
+ (*f[k]) (i,fa[k]); /* do that attribute */
+ }
+ PSOUT (")\015\012"); /* trailer */
+ quell_events = NIL; /* events alright now */
+ }
+ }
+}
+
+/* Fetch message body structure (extensible)
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_bodystructure (unsigned long i,void *args)
+{
+ BODY *body;
+ mail_fetchstructure (stream,i,&body);
+ PSOUT ("BODYSTRUCTURE ");
+ pbodystructure (body); /* output body */
+}
+
+
+/* Fetch message body structure (non-extensible)
+ * Accepts: message number
+ * extra argument
+ */
+
+
+void fetch_body (unsigned long i,void *args)
+{
+ BODY *body;
+ mail_fetchstructure (stream,i,&body);
+ PSOUT ("BODY "); /* output attribute */
+ pbody (body); /* output body */
+}
+
+/* Fetch body part MIME header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_mime (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ unsigned long uid = mail_uid (stream,i);
+ char *tmp = (char *) fs_get (100 + strlen (ta->section));
+ sprintf (tmp,"BODY[%s.MIME]",ta->section);
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ else { /* get data */
+ st.data = (unsigned char *)
+ mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
+ if (ta->first || ta->last) remember (uid,tmp,&st);
+ }
+ pbodypartstring (i,tmp,&st,NIL,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+
+/* Fetch body part contents
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_contents (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ /* get data */
+ else if ((st.data = (unsigned char *)
+ mail_fetch_body (stream,i,ta->section,&st.size,
+ ta->flags | FT_RETURNSTRINGSTRUCT)) &&
+ (ta->first || ta->last)) remember (uid,tmp,&st);
+ pbodypartstring (i,tmp,&st,&stream->private.string,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch body part binary
+ * Accepts: message number
+ * extra argument
+ * Someday fix this to use stringstruct instead of memory
+ */
+
+void fetch_body_part_binary (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st,cst;
+ BODY *body = mail_body (stream,i,ta->section);
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ else { /* get data */
+ st.data = (unsigned char *)
+ mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
+ if (ta->first || ta->last) remember (uid,tmp,&st);
+ }
+ /* what encoding was used? */
+ if (body) switch (body->encoding) {
+ case ENCBASE64:
+ if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break;
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Undecodable BASE64 contents");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ case ENCQUOTEDPRINTABLE:
+ if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break;
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ case ENC7BIT: /* no need to convert any of these */
+ case ENC8BIT:
+ case ENCBINARY:
+ cst.data = NIL; /* no converted data to free */
+ break;
+ default: /* unknown encoding, oops */
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Unknown Content-Transfer-Encoding");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ }
+ else {
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Invalid body part");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ }
+
+ /* use decoded version if exists */
+ if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT));
+ if (ta->binary & FTB_SIZE) {/* just want size? */
+ sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "",
+ st.size);
+ PSOUT (tmp);
+ }
+ else { /* no, blat binary data */
+ int f = mail_elt (stream,i)->seen;
+ if (st.data) { /* only if have useful data */
+ /* partial specifier */
+ if (ta->first || ta->last)
+ sprintf (tmp,"BINARY[%s]<%lu> ",
+ ta->section ? ta->section : "",ta->first);
+ else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : "");
+ /* in case first byte beyond end of text */
+ if (st.size <= ta->first) st.size = ta->first = 0;
+ else { /* offset and truncate */
+ st.data += ta->first; /* move to desired position */
+ st.size -= ta->first; /* reduced size */
+ if (ta->last && (st.size > ta->last)) st.size = ta->last;
+ }
+ if (st.size) sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size);
+ else strcat (tmp,"\"\"");
+ PSOUT (tmp); /* write binary output */
+ if (st.size && (PSOUTR (&st) == EOF)) ioerror(stdout,"writing binary");
+ }
+ else {
+ sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : "");
+ PSOUT (tmp);
+ }
+ changed_flags (i,f); /* write changed flags */
+ }
+ /* free converted data */
+ if (cst.data) fs_give ((void **) &cst.data);
+ fs_give ((void **) &tmp); /* and temporary string */
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch MESSAGE/RFC822 body part header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_header (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
+ STRINGLIST *s;
+ for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (len);
+ PSOUT ("BODY[");
+ /* output attribute */
+ if (ta->section && *ta->section) {
+ PSOUT (ta->section);
+ PBOUT ('.');
+ }
+ PSOUT ("HEADER");
+ if (ta->lines) {
+ PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
+ pastringlist (ta->lines);
+ }
+ strcpy (tmp,"]"); /* close section specifier */
+ st.data = (unsigned char *) /* get data (no hope in using remember here) */
+ mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
+ pbodypartstring (i,tmp,&st,NIL,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch MESSAGE/RFC822 body part text
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_text (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ /* output attribute */
+ if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
+ else strcpy (tmp,"BODY[TEXT]");
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ /* get data */
+ else if ((st.data = (unsigned char *)
+ mail_fetch_text (stream,i,ta->section,&st.size,
+ ta->flags | FT_RETURNSTRINGSTRUCT)) &&
+ (ta->first || ta->last)) remember (uid,tmp,&st);
+ pbodypartstring (i,tmp,&st,&stream->private.string,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+
+/* Remember body part text for subsequent partial fetching
+ * Accepts: message UID
+ * body part id
+ * text
+ * string
+ */
+
+void remember (unsigned long uid,char *id,SIZEDTEXT *st)
+{
+ lastuid = uid; /* remember UID */
+ if (lastid) fs_give ((void **) &lastid);
+ lastid = cpystr (id); /* remember body part id */
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ /* remember text */
+ lastst.data = (unsigned char *)
+ memcpy (fs_get (st->size + 1),st->data,st->size);
+ lastst.size = st->size;
+}
+
+
+/* Fetch envelope
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_envelope (unsigned long i,void *args)
+{
+ ENVELOPE *env = mail_fetchenvelope (stream,i);
+ PSOUT ("ENVELOPE "); /* output attribute */
+ penv (env); /* output envelope */
+}
+
+/* Fetch flags
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_flags (unsigned long i,void *args)
+{
+ unsigned long u;
+ char *t,tmp[MAILTMPLEN];
+ int c = NIL;
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->valid) { /* have valid flags yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_flags (stream,tmp,NIL);
+ }
+ PSOUT ("FLAGS ("); /* output attribute */
+ /* output system flags */
+ if (elt->recent) put_flag (&c,"\\Recent");
+ if (elt->seen) put_flag (&c,"\\Seen");
+ if (elt->deleted) put_flag (&c,"\\Deleted");
+ if (elt->flagged) put_flag (&c,"\\Flagged");
+ if (elt->answered) put_flag (&c,"\\Answered");
+ if (elt->draft) put_flag (&c,"\\Draft");
+ if (u = elt->user_flags) do /* any user flags? */
+ if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
+ while (u); /* until no more user flags */
+ PBOUT (')'); /* end of flags */
+ elt->spare2 = NIL; /* we've sent the update */
+}
+
+
+/* Output a flag
+ * Accepts: pointer to current delimiter character
+ * flag to output
+ * Changes delimiter character to space
+ */
+
+void put_flag (int *c,char *s)
+{
+ if (*c) PBOUT (*c); /* put delimiter */
+ PSOUT (s); /* dump flag */
+ *c = ' '; /* change delimiter if necessary */
+}
+
+
+/* Output flags if was unseen
+ * Accepts: message number
+ * prior value of Seen flag
+ */
+
+void changed_flags (unsigned long i,int f)
+{
+ /* was unseen, now seen? */
+ if (!f && mail_elt (stream,i)->seen) {
+ PBOUT (' '); /* yes, delimit with space */
+ fetch_flags (i,NIL); /* output flags */
+ }
+}
+
+/* Fetch message internal date
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_internaldate (unsigned long i,void *args)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->day) { /* have internal date yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ PSOUT ("INTERNALDATE \"");
+ PSOUT (mail_date (tmp,elt));
+ PBOUT ('"');
+}
+
+
+/* Fetch unique identifier
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_uid (unsigned long i,void *args)
+{
+ PSOUT ("UID ");
+ pnum (mail_uid (stream,i));
+}
+
+/* Fetch complete RFC-822 format message
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822 (unsigned long i,void *args)
+{
+ if (i) { /* do work? */
+ int f = mail_elt (stream,i)->seen;
+#if 0
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_message (stream,i,&st.size,(long) args);
+ pbodypartstring (i,"RFC822",&st,NIL,NIL);
+#else
+ /* Yes, this version is bletcherous, but mail_fetch_message() requires
+ too much memory */
+ SIZEDTEXT txt,hdr;
+ char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
+ hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
+ txt.data = (unsigned char *)
+ mail_fetch_text (stream,i,NIL,&txt.size,
+ ((long) args) | FT_RETURNSTRINGSTRUCT);
+ PSOUT ("RFC822 {");
+ pnum (hdr.size + txt.size);
+ PSOUT ("}\015\012");
+ ptext (&hdr,NIL);
+ ptext (&txt,&stream->private.string);
+ fs_give ((void **) &hdr.data);
+#endif
+ changed_flags (i,f); /* output changed flags */
+ }
+}
+
+
+/* Fetch RFC-822 header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_header (unsigned long i,void *args)
+{
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
+ pbodypartstring (i,"RFC822.HEADER",&st,NIL,NIL);
+}
+
+
+/* Fetch RFC-822 message length
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_size (unsigned long i,void *args)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->rfc822_size) { /* have message size yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ PSOUT ("RFC822.SIZE ");
+ pnum (elt->rfc822_size);
+}
+
+/* Fetch RFC-822 text only
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_text (unsigned long i,void *args)
+{
+ if (i) { /* do work? */
+ int f = mail_elt (stream,i)->seen;
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_text (stream,i,NIL,&st.size,
+ ((long) args) | FT_RETURNSTRINGSTRUCT);
+ pbodypartstring (i,"RFC822.TEXT",&st,&stream->private.string,NIL);
+ }
+}
+
+/* Print envelope
+ * Accepts: body
+ */
+
+void penv (ENVELOPE *env)
+{
+ PBOUT ('('); /* delimiter */
+ if (env) { /* only if there is an envelope */
+ pnstring (env->date); /* output envelope fields */
+ PBOUT (' ');
+ pnstring (env->subject);
+ PBOUT (' ');
+ paddr (env->from);
+ PBOUT (' ');
+ paddr (env->sender);
+ PBOUT (' ');
+ paddr (env->reply_to);
+ PBOUT (' ');
+ paddr (env->to);
+ PBOUT (' ');
+ paddr (env->cc);
+ PBOUT (' ');
+ paddr (env->bcc);
+ PBOUT (' ');
+ pnstring (env->in_reply_to);
+ PBOUT (' ');
+ pnstring (env->message_id);
+ }
+ /* no envelope */
+ else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
+ PBOUT (')'); /* end of envelope */
+}
+
+/* Print body structure (extensible)
+ * Accepts: body
+ */
+
+void pbodystructure (BODY *body)
+{
+ PBOUT ('('); /* delimiter */
+ if (body) { /* only if there is a body */
+ PART *part;
+ /* multipart type? */
+ if (body->type == TYPEMULTIPART) {
+ /* print each part */
+ if (part = body->nested.part)
+ for (; part; part = part->next) pbodystructure (&(part->body));
+ else pbodystructure (NIL);
+ PBOUT (' '); /* space delimiter */
+ pstring (body->subtype); /* subtype */
+ PBOUT (' ');
+ pparam (body->parameter); /* multipart body extension data */
+ PBOUT (' ');
+ if (body->disposition.type) {
+ PBOUT ('(');
+ pstring (body->disposition.type);
+ PBOUT (' ');
+ pparam (body->disposition.parameter);
+ PBOUT (')');
+ }
+ else PSOUT ("NIL");
+ PBOUT (' ');
+ pnstringorlist (body->language);
+ PBOUT (' ');
+ pnstring (body->location);
+ }
+
+ else { /* non-multipart body type */
+ pstring ((char *) body_types[body->type]);
+ PBOUT (' ');
+ pstring (body->subtype);
+ PBOUT (' ');
+ pparam (body->parameter);
+ PBOUT (' ');
+ pnstring (body->id);
+ PBOUT (' ');
+ pnstring (body->description);
+ PBOUT (' ');
+ pstring ((char *) body_encodings[body->encoding]);
+ PBOUT (' ');
+ pnum (body->size.bytes);
+ switch (body->type) { /* extra stuff depends upon body type */
+ case TYPEMESSAGE:
+ /* can't do this if not RFC822 */
+ if (strcmp (body->subtype,"RFC822")) break;
+ PBOUT (' ');
+ penv (body->nested.msg->env);
+ PBOUT (' ');
+ pbodystructure (body->nested.msg->body);
+ case TYPETEXT:
+ PBOUT (' ');
+ pnum (body->size.lines);
+ break;
+ default:
+ break;
+ }
+ PBOUT (' ');
+ pnstring (body->md5);
+ PBOUT (' ');
+ if (body->disposition.type) {
+ PBOUT ('(');
+ pstring (body->disposition.type);
+ PBOUT (' ');
+ pparam (body->disposition.parameter);
+ PBOUT (')');
+ }
+ else PSOUT ("NIL");
+ PBOUT (' ');
+ pnstringorlist (body->language);
+ PBOUT (' ');
+ pnstring (body->location);
+ }
+ }
+ /* no body */
+ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL");
+ PBOUT (')'); /* end of body */
+}
+
+/* Print body (non-extensible)
+ * Accepts: body
+ */
+
+void pbody (BODY *body)
+{
+ PBOUT ('('); /* delimiter */
+ if (body) { /* only if there is a body */
+ PART *part;
+ /* multipart type? */
+ if (body->type == TYPEMULTIPART) {
+ /* print each part */
+ if (part = body->nested.part)
+ for (; part; part = part->next) pbody (&(part->body));
+ else pbody (NIL);
+ PBOUT (' '); /* space delimiter */
+ pstring (body->subtype); /* and finally the subtype */
+ }
+ else { /* non-multipart body type */
+ pstring ((char *) body_types[body->type]);
+ PBOUT (' ');
+ pstring (body->subtype);
+ PBOUT (' ');
+ pparam (body->parameter);
+ PBOUT (' ');
+ pnstring (body->id);
+ PBOUT (' ');
+ pnstring (body->description);
+ PBOUT (' ');
+ pstring ((char *) body_encodings[body->encoding]);
+ PBOUT (' ');
+ pnum (body->size.bytes);
+ switch (body->type) { /* extra stuff depends upon body type */
+ case TYPEMESSAGE:
+ /* can't do this if not RFC822 */
+ if (strcmp (body->subtype,"RFC822")) break;
+ PBOUT (' ');
+ penv (body->nested.msg ? body->nested.msg->env : NIL);
+ PBOUT (' ');
+ pbody (body->nested.msg ? body->nested.msg->body : NIL);
+ case TYPETEXT:
+ PBOUT (' ');
+ pnum (body->size.lines);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /* no body */
+ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0");
+ PBOUT (')'); /* end of body */
+}
+
+/* Print parameter list
+ * Accepts: paramter
+ */
+
+void pparam (PARAMETER *param)
+{
+ if (param) { /* one specified? */
+ PBOUT ('(');
+ do {
+ pstring (param->attribute);
+ PBOUT (' ');
+ pstring (param->value);
+ if (param = param->next) PBOUT (' ');
+ } while (param);
+ PBOUT (')'); /* end of parameters */
+ }
+ else PSOUT ("NIL");
+}
+
+
+/* Print address list
+ * Accepts: address list
+ */
+
+void paddr (ADDRESS *a)
+{
+ if (a) { /* have anything in address? */
+ PBOUT ('('); /* open the address list */
+ do { /* for each address */
+ PBOUT ('('); /* open the address */
+ pnstring (a->personal); /* personal name */
+ PBOUT (' ');
+ pnstring (a->adl); /* at-domain-list */
+ PBOUT (' ');
+ pnstring (a->mailbox); /* mailbox */
+ PBOUT (' ');
+ pnstring (a->host); /* domain name of mailbox's host */
+ PBOUT (')'); /* terminate address */
+ } while (a = a->next); /* until end of address */
+ PBOUT (')'); /* close address list */
+ }
+ else PSOUT ("NIL"); /* empty address */
+}
+
+/* Print set
+ * Accepts: set
+ */
+
+void pset (SEARCHSET **set)
+{
+ SEARCHSET *cur = *set;
+ while (cur) { /* while there's a set to do */
+ pnum (cur->first); /* output first value */
+ if (cur->last) { /* if range, output second value of range */
+ PBOUT (':');
+ pnum (cur->last);
+ }
+ if (cur = cur->next) PBOUT (',');
+ }
+ mail_free_searchset (set); /* flush set */
+}
+
+
+/* Print number
+ * Accepts: number
+ */
+
+void pnum (unsigned long i)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%lu",i);
+ PSOUT (tmp);
+}
+
+
+/* Print string
+ * Accepts: string
+ */
+
+void pstring (char *s)
+{
+ SIZEDTEXT st;
+ st.data = (unsigned char *) s;/* set up sized text */
+ st.size = strlen (s);
+ psizedstring (&st,NIL); /* print string */
+}
+
+
+/* Print nstring
+ * Accepts: string or NIL
+ */
+
+void pnstring (char *s)
+{
+ if (s) pstring (s); /* print string */
+ else PSOUT ("NIL");
+}
+
+
+/* Print atom or string
+ * Accepts: astring
+ */
+
+void pastring (char *s)
+{
+ char *t;
+ if (!*s) PSOUT ("\"\""); /* empty string */
+ else { /* see if atom */
+ for (t = s; (*t > ' ') && !(*t & 0x80) &&
+ (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') &&
+ (*t != '{') && (*t != '%') && (*t != '*'); t++);
+ if (*t) pstring (s); /* not an atom */
+ else PSOUT (s); /* else plop down as atomic */
+ }
+}
+
+/* Print sized text as quoted
+ * Accepts: sized text
+ */
+
+void psizedquoted (SIZEDTEXT *s)
+{
+ PBOUT ('"'); /* use quoted string */
+ ptext (s,NIL);
+ PBOUT ('"');
+}
+
+
+/* Print sized text as literal
+ * Accepts: sized text
+ */
+
+void psizedliteral (SIZEDTEXT *s,STRING *st)
+{
+ PBOUT ('{'); /* print literal size */
+ pnum (s->size);
+ PSOUT ("}\015\012");
+ ptext (s,st);
+}
+
+/* Print sized text as literal or quoted string
+ * Accepts: sized text
+ * alternative stringstruct of text
+ */
+
+void psizedstring (SIZEDTEXT *s,STRING *st)
+{
+ unsigned char c;
+ unsigned long i;
+
+ if (s->data) { /* if text, check if must use literal */
+ for (i = 0; ((i < s->size) && ((c = s->data[i]) & 0xe0) &&
+ !(c & 0x80) && (c != '"') && (c != '\\')); ++i);
+ /* must use literal if not all QUOTED-CHAR */
+ if (i < s->size) psizedliteral (s,st);
+ else psizedquoted (s);
+ }
+ else psizedliteral (s,st);
+}
+
+
+/* Print sized text as literal or quoted string
+ * Accepts: sized text
+ */
+
+void psizedastring (SIZEDTEXT *s)
+{
+ unsigned long i;
+ unsigned int atomp = s->size ? T : NIL;
+ for (i = 0; i < s->size; i++){/* check if must use literal */
+ if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
+ (s->data[i] == '"') || (s->data[i] == '\\')) {
+ psizedliteral (s,NIL);
+ return;
+ }
+ else switch (s->data[i]) { /* else see if any atom-specials */
+ case '(': case ')': case '{': case ' ':
+ case '%': case '*': /* list-wildcards */
+ case ']': /* resp-specials */
+ /* CTL and quoted-specials in literal check */
+ atomp = NIL; /* not an atom */
+ }
+ }
+ if (atomp) ptext (s,NIL); /* print as atom */
+ else psizedquoted (s); /* print as quoted string */
+}
+
+/* Print string list
+ * Accepts: string list
+ */
+
+void pastringlist (STRINGLIST *s)
+{
+ PBOUT ('('); /* start list */
+ do {
+ psizedastring (&s->text); /* output list member */
+ if (s->next) PBOUT (' ');
+ } while (s = s->next);
+ PBOUT (')'); /* terminate list */
+}
+
+
+/* Print nstring or list of strings
+ * Accepts: string / string list
+ */
+
+void pnstringorlist (STRINGLIST *s)
+{
+ if (!s) PSOUT ("NIL"); /* no argument given */
+ else if (s->next) { /* output list as list of strings*/
+ PBOUT ('('); /* start list */
+ do { /* output list member */
+ psizedstring (&s->text,NIL);
+ if (s->next) PBOUT (' ');
+ } while (s = s->next);
+ PBOUT (')'); /* terminate list */
+ }
+ /* and single-element list as string */
+ else psizedstring (&s->text,NIL);
+}
+
+/* Print body part string
+ * Accepts: message number
+ * body part id (note: must have space at end to append stuff)
+ * sized text of string
+ * alternative stringstruct of string
+ * text printing arguments
+ */
+
+void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
+ TEXTARGS *ta)
+{
+ int f = mail_elt (stream,msgno)->seen;
+ /* ignore stringstruct if non-initialized */
+ if (bs && !bs->curpos) bs = NIL;
+ if (ta && st->size) { /* only if have useful data */
+ /* partial specifier */
+ if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
+ /* in case first byte beyond end of text */
+ if (st->size <= ta->first) st->size = ta->first = 0;
+ else {
+ if (st->data) { /* offset and truncate */
+ st->data += ta->first; /* move to desired position */
+ st->size -= ta->first; /* reduced size */
+ }
+ else if (bs && (SIZE (bs) >= ta->first))
+ SETPOS (bs,ta->first + GETPOS (bs));
+ else st->size = 0; /* shouldn't happen */
+ if (ta->last && (st->size > ta->last)) st->size = ta->last;
+ }
+ }
+ PSOUT (id);
+ PBOUT (' ');
+ psizedstring (st,bs); /* output string */
+ changed_flags (msgno,f); /* and changed flags */
+}
+
+/* RFC 3501 technically forbids NULs in literals. Normally, the delivering
+ * MTA would take care of MIME converting the message text so that it is
+ * NUL-free. If it doesn't, then we have the choice of either violating
+ * IMAP by sending NULs, corrupting the data, or going to lots of work to do
+ * MIME conversion in the IMAP server.
+ */
+
+/* Print raw sized text
+ * Accepts: sizedtext
+ */
+
+void ptext (SIZEDTEXT *txt,STRING *st)
+{
+ unsigned char c,*s;
+ unsigned long i = txt->size;
+ if (s = txt->data) while (i && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))) --i;
+ else if (st) while (i && (PBOUT ((c = SNX (st)) ? c : 0x80) != EOF)) --i;
+ /* failed to complete? */
+ if (i) ioerror (stdout,"writing text");
+}
+
+/* Print thread
+ * Accepts: thread
+ */
+
+void pthread (THREADNODE *thr)
+{
+ THREADNODE *t;
+ while (thr) { /* for each branch */
+ PBOUT ('('); /* open branch */
+ if (thr->num) { /* first node message number */
+ pnum (thr->num);
+ if (t = thr->next) { /* any subsequent nodes? */
+ PBOUT (' ');
+ while (t) { /* for each subsequent node */
+ if (t->branch) { /* branches? */
+ pthread (t); /* yes, recurse to do branch */
+ t = NIL; /* done */
+ }
+ else { /* just output this number */
+ pnum (t->num);
+ t = t->next; /* and do next message */
+ }
+ if (t) PBOUT (' '); /* delimit if more to come */
+ }
+ }
+ }
+ else pthread (thr->next); /* nest for dummy */
+ PBOUT (')'); /* done with this branch */
+ thr = thr->branch; /* do next branch */
+ }
+}
+
+/* Print capabilities
+ * Accepts: option flag
+ */
+
+void pcapability (long flag)
+{
+ unsigned long i;
+ char *s;
+ struct stat sbuf;
+ AUTHENTICATOR *auth;
+ THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
+ /* always output protocol level */
+ PSOUT ("CAPABILITY IMAP4REV1 I18NLEVEL=1 LITERAL+");
+#ifdef NETSCAPE_BRAIN_DAMAGE
+ PSOUT (" X-NETSCAPE");
+#endif
+ if (flag >= 0) { /* want post-authentication capabilities? */
+ PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT ESEARCH WITHIN SCAN SORT");
+ while (thr) { /* threaders */
+ PSOUT (" THREAD=");
+ PSOUT (thr->name);
+ thr = thr->next;
+ }
+ if (!anonymous) PSOUT (" MULTIAPPEND");
+ }
+ if (flag <= 0) { /* want pre-authentication capabilities? */
+ PSOUT (" SASL-IR LOGIN-REFERRALS");
+ if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
+ else PSOUT (" STARTTLS");
+ /* disable plaintext */
+ if (!(i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)))
+ PSOUT (" LOGINDISABLED");
+ for (auth = mail_lookup_auth (1); auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ PSOUT (" AUTH=");
+ PSOUT (auth->name);
+ }
+ if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
+ }
+}
+
+/* Anonymous users may only use these mailboxes in these namespaces */
+
+char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
+
+
+/* Check if mailbox name is OK
+ * Accepts: reference name
+ * mailbox name
+ */
+
+long nameok (char *ref,char *name)
+{
+ int i;
+ unsigned char *s,*t;
+ if (!name) return NIL; /* failure if missing name */
+ if (!anonymous) return T; /* otherwise OK if not anonymous */
+ /* validate reference */
+ if (ref && ((*ref == '#') || (*ref == '{')))
+ for (i = 0; oktab[i]; i++) {
+ for (s = ref, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
+ if (!*t) { /* reference OK */
+ if (*name == '#') break;/* check name if override */
+ else return T; /* otherwise done */
+ }
+ }
+ /* ordinary names are OK */
+ if ((*name != '#') && (*name != '{')) return T;
+ for (i = 0; oktab[i]; i++) { /* validate mailbox */
+ for (s = name, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
+ if (!*t) return T; /* name is OK */
+ }
+ response = "%.80s NO Anonymous may not %.80s this name\015\012";
+ return NIL;
+}
+
+
+/* Convert possible BBoard name to actual name
+ * Accepts: command
+ * mailbox name
+ * Returns: maibox name
+ */
+
+char *bboardname (char *cmd,char *name)
+{
+ if (cmd[0] == 'B') { /* want bboard? */
+ char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
+ sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
+ name = s;
+ }
+ return name;
+}
+
+/* Test if name is news proxy
+ * Accepts: name
+ * Returns: T if news proxy, NIL otherwise
+ */
+
+long isnewsproxy (char *name)
+{
+ return (nntpproxy && (name[0] == '#') &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'E') || (name[2] == 'e')) &&
+ ((name[3] == 'W') || (name[3] == 'w')) &&
+ ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ?
+ LONGT : NIL;
+}
+
+
+/* News proxy generate canonical pattern
+ * Accepts: reference
+ * pattern
+ * buffer to return canonical pattern
+ * Returns: T on success with pattern in buffer, NIL on failure
+ */
+
+long newsproxypattern (char *ref,char *pat,char *pattern,long flag)
+{
+ if (!nntpproxy) return NIL;
+ if (strlen (ref) > NETMAXMBX) {
+ sprintf (pattern,"Invalid reference specification: %.80s",ref);
+ mm_log (pattern,ERROR);
+ return NIL;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ sprintf (pattern,"Invalid pattern specification: %.80s",pat);
+ mm_log (pattern,ERROR);
+ return NIL;
+ }
+ if (flag) { /* prepend proxy specifier */
+ sprintf (pattern,"{%.300s/nntp}",nntpproxy);
+ pattern += strlen (pattern);
+ }
+ if (*ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ return isnewsproxy (pattern);
+}
+
+/* IMAP4rev1 Authentication responder
+ * Accepts: challenge
+ * length of challenge
+ * pointer to response length return location if non-NIL
+ * Returns: response
+ */
+
+#define RESPBUFLEN 8*MAILTMPLEN
+
+char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
+{
+ unsigned long i,j;
+ unsigned char *t,resp[RESPBUFLEN];
+ if (initial) { /* initial response given? */
+ if (clen) return NIL; /* not permitted */
+ /* set up response */
+ i = strlen ((char *) (t = initial));
+ initial = NIL; /* no more initial response */
+ if ((*t == '=') && !t[1]) { /* SASL-IR does this for 0-length response */
+ if (rlen) *rlen = 0; /* set length zero if empty */
+ return cpystr (""); /* and return empty string as response */
+ }
+ }
+ else { /* issue challenge, get response */
+ PSOUT ("+ ");
+ for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
+ if (t[j] > ' ') PBOUT (t[j]);
+ fs_give ((void **) &t);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ /* slurp response buffer */
+ slurp ((char *) resp,RESPBUFLEN,INPUTTIMEOUT);
+ if (!(t = (unsigned char *) strchr ((char *) resp,'\012')))
+ return (char *) flush ();
+ if (t[-1] == '\015') --t; /* remove CR */
+ *t = '\0'; /* tie off buffer */
+ if (resp[0] == '*') {
+ cancelled = T;
+ return NIL;
+ }
+ i = t - resp; /* length of response */
+ t = resp; /* set up for return call */
+ }
+ return (i % 4) ? NIL : /* return if valid BASE64 */
+ (char *) rfc822_base64 (t,i,rlen ? rlen : &i);
+}
+
+/* Proxy copy across mailbox formats
+ * Accepts: mail stream
+ * sequence to copy on this stream
+ * destination mailbox
+ * option flags
+ * Returns: T if success, else NIL
+ */
+
+long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ MAILSTREAM *ts;
+ STRING st;
+ MSGDATA md;
+ SEARCHSET *set;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ md.stream = stream;
+ md.msgno = 0;
+ md.flags = md.date = NIL;
+ md.message = &st;
+ /* Currently ignores CP_MOVE and CP_DEBUG */
+ if (!((options & CP_UID) ? /* validate sequence */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
+ return NIL;
+ response = win; /* cancel previous errors */
+ if (lsterr) fs_give ((void **) &lsterr);
+ /* c-client clobbers sequence, use spare */
+ for (i = 1,j = 0,set = mail_newsearchset (); i <= nmsgs; i++)
+ if (mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) {
+ mail_append_set (set,mail_uid (stream,i));
+ if (!j) md.msgno = (j = i) - 1;
+ }
+ /* only if at least one message to copy */
+ if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) {
+ response = trycreate ? losetry : lose;
+ if (set) mail_free_searchset (&set);
+ return NIL;
+ }
+ if (caset) csset = set; /* set for return value now */
+ else if (set) mail_free_searchset (&set);
+ response = win; /* stomp any previous babble */
+ if (md.msgno) { /* get new driver name if was dummy */
+ sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
+ stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
+ ts->dtb->name : "unknown");
+ mm_log (tmp,NIL);
+ }
+ return LONGT;
+}
+
+/* Proxy append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ char *s,*t,tmp[MAILTMPLEN];
+ MSGDATA *md = (MSGDATA *) data;
+ if (md->flags) fs_give ((void **) &md->flags);
+ if (md->date) fs_give ((void **) &md->date);
+ *message = NIL; /* assume all done */
+ *flags = *date = NIL;
+ while (++md->msgno <= nmsgs)
+ if ((elt = mail_elt (md->stream,md->msgno))->spare) {
+ if (!(elt->valid && elt->day)) {
+ sprintf (tmp,"%lu",md->msgno);
+ mail_fetch_fast (md->stream,tmp,NIL);
+ }
+ memset (s = tmp,0,MAILTMPLEN);
+ /* copy flags */
+ if (elt->seen) strcat (s," \\Seen");
+ if (elt->deleted) strcat (s," \\Deleted");
+ if (elt->flagged) strcat (s," \\Flagged");
+ if (elt->answered) strcat (s," \\Answered");
+ if (elt->draft) strcat (s," \\Draft");
+ if (i = elt->user_flags) do
+ if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t &&
+ (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
+ *s++ = ' '; /* space delimiter */
+ strcpy (s,t);
+ } while (i); /* until no more user flags */
+ *message = md->message; /* set up return values */
+ *flags = md->flags = cpystr (tmp + 1);
+ *date = md->date = cpystr (mail_date (tmp,elt));
+ INIT (md->message,msg_string,(void *) md,elt->rfc822_size);
+ break; /* process this message */
+ }
+ return LONGT;
+}
+
+/* Append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ unsigned long i,j;
+ char *t;
+ APPENDDATA *ad = (APPENDDATA *) data;
+ unsigned char *arg = ad->arg;
+ /* flush text of previous message */
+ if (t = ad->flags) fs_give ((void **) &ad->flags);
+ if (t = ad->date) fs_give ((void **) &ad->date);
+ if (t = ad->msg) fs_give ((void **) &ad->msg);
+ *flags = *date = NIL; /* assume no flags or date */
+ if (t) { /* have previous message? */
+ if (!*arg) { /* if least one message, and no more coming */
+ *message = NIL; /* set stop */
+ return LONGT; /* return success */
+ }
+ else if (*arg++ != ' ') { /* must have a delimiter to next argument */
+ response = misarg; /* oops */
+ return NIL;
+ }
+ }
+ *message = ad->message; /* return pointer to message stringstruct */
+ if (*arg == '(') { /* parse optional flag list */
+ t = ++arg; /* pointer to flag list contents */
+ while (*arg && (*arg != ')')) arg++;
+ if (*arg) *arg++ = '\0';
+ if (*arg == ' ') arg++;
+ *flags = ad->flags = cpystr (t);
+ }
+ /* parse optional date */
+ if (*arg == '"') *date = ad->date = cpystr (snarf (&arg));
+ if (!arg || (*arg != '{')) /* parse message */
+ response = "%.80s BAD Missing literal in %.80s\015\012";
+ else if (!isdigit (arg[1]))
+ response = "%.80s BAD Missing message to %.80s\015\012";
+ else if (!(i = strtoul (arg+1,&t,10)))
+ response = "%.80s NO Empty message to %.80s\015\012";
+ else if (i > MAXAPPENDTXT) /* maybe relax this a little */
+ response = "%.80s NO Excessively large message to %.80s\015\012";
+ else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) {
+ /* get a literal buffer */
+ inliteral (ad->msg = (char *) fs_get (i+1),i);
+ /* get new command tail */
+ slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT);
+ if (strchr (ad->arg,'\012')) {
+ /* reset strtok mechanism, tie off if done */
+ if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0';
+ /* possible LITERAL+? */
+ if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') &&
+ (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) {
+ /* back over possible count */
+ for (j -= 4; j && isdigit (ad->arg[j]); j--);
+ if (ad->arg[j] == '{') {/* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (ad->arg + j + 1,NIL,10);
+ }
+ }
+ /* initialize stringstruct */
+ INIT (ad->message,mail_string,(void *) ad->msg,i);
+ return LONGT; /* ready to go */
+ }
+ flush (); /* didn't find end of line? */
+ fs_give ((void **) &ad->msg);
+ }
+ else response = badarg; /* not a literal */
+ return NIL; /* error */
+}
+
+/* Got COPY UID data
+ * Accepts: MAIL stream
+ * mailbox name
+ * UID validity
+ * source set of UIDs
+ * destination set of UIDs
+ */
+
+void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *sourceset,SEARCHSET *destset)
+{
+ if (cauidvalidity) fatal ("duplicate COPYUID/APPENDUID data");
+ cauidvalidity = uidvalidity;
+ csset = sourceset;
+ caset = destset;
+}
+
+
+/* Got APPEND UID data
+ * Accepts: mailbox name
+ * UID validity
+ * destination set of UIDs
+ */
+
+void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set)
+{
+ copyuid (NIL,mailbox,uidvalidity,NIL,set);
+}
+
+
+/* Got a referral
+ * Accepts: MAIL stream
+ * URL
+ * referral type code
+ */
+
+char *referral (MAILSTREAM *stream,char *url,long code)
+{
+ if (lstref) fs_give ((void **) &lstref);
+ lstref = cpystr (url); /* set referral */
+ /* set error if not a logged in referral */
+ if (code != REFAUTH) response = lose;
+ if (!lsterr) lsterr = cpystr ("Try referral URL");
+ return NIL; /* don't chase referrals for now */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *s,unsigned long msgno)
+{
+ /* nothing to do here */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *s,unsigned long number)
+{
+ /* note change in number of messages */
+ if ((s != tstream) && (nmsgs != number)) {
+ nmsgs = number; /* always update number of messages */
+ if (quell_events) existsquelled = T;
+ else {
+ PSOUT ("* ");
+ pnum (nmsgs);
+ PSOUT (" EXISTS\015\012");
+ }
+ recent = 0xffffffff; /* make sure update recent too */
+ }
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *s,unsigned long number)
+{
+ if (quell_events) fatal ("Impossible EXPUNGE event");
+ if (s != tstream) {
+ PSOUT ("* ");
+ pnum (number);
+ PSOUT (" EXPUNGE\015\012");
+ }
+ nmsgs--;
+ existsquelled = T; /* do EXISTS when command done */
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *s,unsigned long number)
+{
+ if (s != tstream) mail_elt (s,number)->spare2 = T;
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ mm_list_work ("LIST",delimiter,name,attributes);
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ mm_list_work ("LSUB",delimiter,name,attributes);
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ if (!quell_events) {
+ char tmp[MAILTMPLEN];
+ tmp[0] = tmp[1] = '\0';
+ if (status->flags & SA_MESSAGES)
+ sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
+ if (status->flags & SA_RECENT)
+ sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
+ if (status->flags & SA_UNSEEN)
+ sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
+ if (status->flags & SA_UIDNEXT)
+ sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
+ if (status->flags & SA_UIDVALIDITY)
+ sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
+ PSOUT ("* STATUS ");
+ pastring (mailbox);
+ PSOUT (" (");
+ PSOUT (tmp+1);
+ PBOUT (')');
+ CRLF;
+ }
+}
+
+/* Worker routine for LIST and LSUB
+ * Accepts: name of response
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_list_work (char *what,int delimiter,char *name,long attributes)
+{
+ char *s;
+ if (!quell_events) {
+ char tmp[MAILTMPLEN];
+ if (finding) {
+ PSOUT ("* MAILBOX ");
+ PSOUT (name);
+ }
+ /* new form */
+ else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
+ PSOUT ("* ");
+ PSOUT (what);
+ PSOUT (" (");
+ tmp[0] = tmp[1] = '\0';
+ if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors");
+ if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect");
+ if (attributes & LATT_MARKED) strcat (tmp," \\Marked");
+ if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked");
+ if (attributes & LATT_HASCHILDREN) strcat (tmp," \\HasChildren");
+ if (attributes & LATT_HASNOCHILDREN) strcat (tmp," \\HasNoChildren");
+ PSOUT (tmp+1);
+ switch (delimiter) {
+ case '\\': /* quoted delimiter */
+ case '"':
+ PSOUT (") \"\\");
+ PBOUT (delimiter);
+ PBOUT ('"');
+ break;
+ case '\0': /* no delimiter */
+ PSOUT (") NIL");
+ break;
+ default: /* unquoted delimiter */
+ PSOUT (") \"");
+ PBOUT (delimiter);
+ PBOUT ('"');
+ break;
+ }
+ PBOUT (' ');
+ /* output mailbox name */
+ if (proxylist && (s = strchr (name,'}'))) pastring (s+1);
+ else pastring (name);
+ }
+ CRLF;
+ }
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ SIZEDTEXT msg;
+ char *s,*code;
+ if (!quell_events && (!tstream || (stream != tstream))) {
+ switch (errflg) {
+ case NIL: /* information message, set as OK response */
+ if ((string[0] == '[') &&
+ ((string[1] == 'T') || (string[1] == 't')) &&
+ ((string[2] == 'R') || (string[2] == 'r')) &&
+ ((string[3] == 'Y') || (string[3] == 'y')) &&
+ ((string[4] == 'C') || (string[4] == 'c')) &&
+ ((string[5] == 'R') || (string[5] == 'r')) &&
+ ((string[6] == 'E') || (string[6] == 'e')) &&
+ ((string[7] == 'A') || (string[7] == 'a')) &&
+ ((string[8] == 'T') || (string[8] == 't')) &&
+ ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
+ trycreate = T;
+ case BYE: /* some other server signing off */
+ case PARSE: /* parse glitch, output unsolicited OK */
+ code = "* OK ";
+ break;
+ case WARN: /* warning, output unsolicited NO (kludge!) */
+ code = "* NO ";
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ code = "* BAD ";
+ break;
+ }
+ PSOUT (code);
+ msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),
+ "\015\012")) ?
+ (s - string) : strlen (string);
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+}
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ SIZEDTEXT msg;
+ char *s;
+ msg.size =
+ (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
+ (s - string) : strlen (string);
+ switch (errflg) {
+ case NIL: /* information message, set as OK response */
+ if (response == win) { /* only if no other response yet */
+ if (lsterr) { /* if there was a previous message */
+ if (!quell_events) {
+ PSOUT ("* OK "); /* blat it out */
+ PSOUT (lsterr);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+ fs_give ((void **) &lsterr);
+ }
+ lsterr = cpystr (string); /* copy string for later use */
+ if (s) lsterr[s - string] = NIL;
+ }
+ break;
+ case PARSE: /* parse glitch, output unsolicited OK */
+ if (!quell_events) {
+ PSOUT ("* OK [PARSE] ");
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+ break;
+ case WARN: /* warning, output unsolicited NO */
+ /* ignore "Mailbox is empty" (KLUDGE!) */
+ if (strcmp (string,"Mailbox is empty")) {
+ if (lstwrn) { /* have previous warning? */
+ if (!quell_events) {
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ PFLUSH (); /* make sure client sees it immediately */
+ }
+ fs_give ((void **) &lstwrn);
+ }
+ lstwrn = cpystr (string); /* note last warning */
+ if (s) lstwrn[s - string] = NIL;
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ response = trycreate ? losetry : lose;
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr (string); /* note last error */
+ if (s) lsterr[s - string] = NIL;
+ break;
+ }
+}
+
+/* Return last error
+ */
+
+char *lasterror (void)
+{
+ if (lsterr) return lsterr;
+ if (lstwrn) return lstwrn;
+ return "<unknown>";
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ mm_log (string,WARN); /* shouldn't happen normally */
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network user name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER);
+ strncpy (password,pass,256); /* and password */
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *s)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *s)
+{
+ /* go non-critical, pending death? */
+ if (!--critical && (state == LOGOUT)) {
+ /* clean up iff needed */
+ if (s && (stream != s) && !s->lock && (s->dtb->flags & DR_XPOINT))
+ s = mail_close (s);
+ longjmp (jmpenv,1); /* die now */
+ }
+}
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
+ PFLUSH (); /* dump output buffer */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ settimeout (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ if (!quell_events) { /* otherwise die before more damage is done */
+ PSOUT ("* NO Disk error: ");
+ PSOUT (strerror (errcode));
+ CRLF;
+ }
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ SIZEDTEXT msg;
+ char *s;
+ msg.size =
+ (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
+ (s - string) : strlen (string);
+ if (!quell_events) {
+ PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH ();
+ }
+ syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",string);
+}
diff --git a/imap/src/imapd/makefile.nt b/imap/src/imapd/makefile.nt
new file mode 100644
index 00000000..41216701
--- /dev/null
+++ b/imap/src/imapd/makefile.nt
@@ -0,0 +1,55 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/imapd/makefile.ntk b/imap/src/imapd/makefile.ntk
new file mode 100644
index 00000000..5c5500e6
--- /dev/null
+++ b/imap/src/imapd/makefile.ntk
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/imapd/makefile.w2k b/imap/src/imapd/makefile.w2k
new file mode 100644
index 00000000..e10b8271
--- /dev/null
+++ b/imap/src/imapd/makefile.w2k
@@ -0,0 +1,56 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/Makefile b/imap/src/ipopd/Makefile
new file mode 100644
index 00000000..accb5408
--- /dev/null
+++ b/imap/src/ipopd/Makefile
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD client Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.o
+ $(CC) $(CFLAGS) -o ipop2d ipop2d.o $(LDFLAGS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.o
+ $(CC) $(CFLAGS) -o ipop3d ipop3d.o $(LDFLAGS)
+
+ipop2d.o: $C/mail.h $C/misc.h $C/osdep.h
+
+ipop3d.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o ipop2d ipop3d || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/ipopd/ipop2d.c b/imap/src/ipopd/ipop2d.c
new file mode 100644
index 00000000..14cbe52f
--- /dev/null
+++ b/imap/src/ipopd/ipop2d.c
@@ -0,0 +1,711 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IPOP2D - IMAP to POP2 conversion server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 28 October 1990
+ * Last Edited: 13 February 2008
+ */
+
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <time.h>
+#include "c-client.h"
+
+
+/* Autologout timer */
+#define KODTIMEOUT 60*5
+#define LOGINTIMEOUT 60*3
+#define TIMEOUT 60*30
+
+
+/* Size of temporary buffers */
+#define TMPLEN 1024
+
+
+/* Server states */
+
+#define LISN 0
+#define AUTH 1
+#define MBOX 2
+#define ITEM 3
+#define NEXT 4
+#define DONE 5
+
+/* Global storage */
+
+char *version = "75"; /* edit number of this server */
+short state = LISN; /* server state */
+short critical = NIL; /* non-zero if in critical code */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+time_t idletime = 0; /* time we went idle */
+unsigned long nmsgs = 0; /* number of messages */
+unsigned long current = 1; /* current message number */
+unsigned long size = 0; /* size of current message */
+char status[MAILTMPLEN]; /* space for status string */
+char *user = ""; /* user name */
+char *pass = ""; /* password */
+unsigned long *msg = NIL; /* message translation vector */
+char *logout = "Logout";
+char *goodbye = "+ Sayonara\015\012";
+
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void sayonara (int status);
+void clkint ();
+void kodint ();
+void hupint ();
+void trmint ();
+short c_helo (char *t,int argc,char *argv[]);
+short c_fold (char *t);
+short c_read (char *t);
+short c_retr (char *t);
+short c_acks (char *t);
+short c_ackd (char *t);
+short c_nack (char *t);
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ char *s,*t;
+ char cmdbuf[TMPLEN];
+ char *pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ s+1 : argv[0]) : "ipop2d";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
+#include "linkage.c"
+ if (mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) {
+ goodbye = "- POP2 server disabled on this system\015\012";
+ sayonara (1);
+ }
+ /* initialize server */
+ server_init (pgmname,"pop",NIL,clkint,kodint,hupint,trmint,NIL);
+ /* There are reports of POP2 clients which get upset if anything appears
+ * between the "+" and the "POP2" in the greeting.
+ */
+ printf ("+ POP2 %s %s.%s server ready\015\012",tcp_serverhost (),
+ CCLIENTVERSION,version);
+ fflush (stdout); /* dump output buffer */
+ state = AUTH; /* initial server state */
+ while (state != DONE) { /* command processing loop */
+ idletime = time (0); /* get a command under timeout */
+ alarm ((state != AUTH) ? TIMEOUT : LOGINTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ while (!fgets (cmdbuf,TMPLEN-1,stdin)) {
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Unexpected client disconnect";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = cmdbuf,"%.80s while reading line",e);
+ state = DONE;
+ stream = mail_close (stream);
+ goodbye = NIL;
+ sayonara (1);
+ }
+ }
+ alarm (0); /* make sure timeout disabled */
+ idletime = 0; /* no longer idle */
+ /* find end of line */
+ if (!strchr (cmdbuf,'\012')) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "- Command line too long\015\012";
+ state = DONE;
+ }
+ else if (!(s = strtok (cmdbuf," \015\012"))) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Missing or null command\015\012";
+ state = DONE;
+ }
+ else { /* dispatch based on command */
+ ucase (s); /* canonicalize case */
+ /* snarf argument */
+ t = strtok (NIL,"\015\012");
+ if ((state == AUTH) && !strcmp (s,"HELO")) state = c_helo (t,argc,argv);
+ else if ((state == MBOX || state == ITEM) && !strcmp (s,"FOLD"))
+ state = c_fold (t);
+ else if ((state == MBOX || state == ITEM) && !strcmp (s,"READ"))
+ state = c_read (t);
+ else if ((state == ITEM) && !strcmp (s,"RETR")) state = c_retr (t);
+ else if ((state == NEXT) && !strcmp (s,"ACKS")) state = c_acks (t);
+ else if ((state == NEXT) && !strcmp (s,"ACKD")) state = c_ackd (t);
+ else if ((state == NEXT) && !strcmp (s,"NACK")) state = c_nack (t);
+ else if ((state == AUTH || state == MBOX || state == ITEM) &&
+ !strcmp (s,"QUIT")) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ state = DONE; /* done in either case */
+ if (t) goodbye = "- Bogus argument given to QUIT\015\012";
+ else { /* expunge the stream */
+ if (stream && nmsgs) stream = mail_close_full (stream,CL_EXPUNGE);
+ stream = NIL; /* don't repeat it */
+ }
+ }
+ else { /* some other or inappropriate command */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Bogus or out of sequence command\015\012";
+ state = DONE;
+ }
+ }
+ fflush (stdout); /* make sure output blatted */
+ }
+ /* clean up the stream */
+ if (stream) mail_close (stream);
+ sayonara (0);
+ return 0; /* stupid compilers */
+}
+
+
+/* Say goodbye
+ * Accepts: exit status
+ *
+ * Does not return
+ */
+
+void sayonara (int status)
+{
+ logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
+ if (goodbye) { /* have a goodbye message? */
+ fputs (goodbye,stdout);
+ fflush (stdout); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (status); /* all done */
+}
+
+/* Clock interrupt
+ */
+
+void clkint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Autologout; idle for too long\015\012";
+ logout = "Autologout";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Kiss Of Death interrupt
+ */
+
+void kodint ()
+{
+ /* only if in command wait */
+ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Killed (lost mailbox lock)\015\012";
+ logout = "Killed (lost mailbox lock)";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+ }
+}
+
+
+/* Hangup interrupt
+ */
+
+void hupint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = NIL;
+ logout = "Hangup";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Termination interrupt
+ */
+
+void trmint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Killed (terminated)\015\012";
+ logout = "Killed (terminated)";
+ if (critical) state = DONE; /* mark state done in either case */
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions.
+ */
+ else sayonara (1); /* die die die */
+}
+
+/* Parse HELO command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_helo (char *t,int argc,char *argv[])
+{
+ char *s,*u,*p;
+ char tmp[TMPLEN];
+ if ((!(t && *t && (u = strtok (t," ")) && (p = strtok (NIL,"\015\012")))) ||
+ (strlen (p) >= TMPLEN)) { /* get user name and password */
+ fputs ("- Missing user or password\015\012",stdout);
+ return DONE;
+ }
+ /* copy password, handle quoting */
+ for (s = tmp; *p; p++) *s++ = (*p == '\\') ? *++p : *p;
+ *s = '\0'; /* tie off string */
+ pass = cpystr (tmp);
+ if (!(s = strchr (u,':'))) { /* want remote mailbox? */
+ /* no, delimit user from possible admin */
+ if (s = strchr (u,'*')) *s++ = '\0';
+ if (server_login (user = cpystr (u),pass,s,argc,argv)) {
+ syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s",s ? "Admin " : "",
+ user,tcp_clienthost ());
+ return c_fold ("INBOX"); /* local; select INBOX */
+ }
+ }
+#ifndef DISABLE_POP_PROXY
+ /* can't do if can't log in as anonymous */
+ else if (anonymous_login (argc,argv)) {
+ *s++ = '\0'; /* separate host name from user name */
+ user = cpystr (s); /* note user name */
+ syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",u,user,
+ tcp_clienthost ());
+ /* initially remote INBOX */
+ sprintf (tmp,"{%.128s/user=%.128s}INBOX",u,user);
+ /* disable rimap just in case */
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+ return c_fold (tmp);
+ }
+#endif
+ fputs ("- Bad login\015\012",stdout);
+ return DONE;
+}
+
+/* Parse FOLD command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_fold (char *t)
+{
+ unsigned long i,j,flags;
+ char *s = NIL,tmp[2*TMPLEN];
+ NETMBX mb;
+ if (!(t && *t)) { /* make sure there's an argument */
+ fputs ("- Missing mailbox name\015\012",stdout);
+ return DONE;
+ }
+ myusername_full (&flags); /* get user type flags */
+ /* expunge old stream */
+ if (stream && nmsgs) mail_expunge (stream);
+ nmsgs = 0; /* no more messages */
+ if (msg) fs_give ((void **) &msg);
+#ifndef DISABLE_POP_PROXY
+ if (flags == MU_ANONYMOUS) { /* don't permit proxy to leave IMAP */
+ if (stream) { /* not first time */
+ if (!(stream->mailbox && (s = strchr (stream->mailbox,'}'))))
+ fatal ("bad previous mailbox name");
+ strncpy (tmp,stream->mailbox,i = (++s - stream->mailbox));
+ if (i >= TMPLEN) fatal ("ridiculous network prefix");
+ strcpy (tmp+i,t); /* append mailbox to initial spec */
+ t = tmp;
+ }
+ /* must be net name first time */
+ else if (!mail_valid_net_parse (t,&mb)) fatal ("anonymous folder bogon");
+ }
+#endif
+ /* open mailbox, note # of messages */
+ if (j = (stream = mail_open (stream,t,NIL)) ? stream->nmsgs : 0) {
+ sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */
+ mail_fetch_fast (stream,tmp,NIL);
+ msg = (unsigned long *) fs_get ((stream->nmsgs + 1) *
+ sizeof (unsigned long));
+ for (i = 1; i <= j; i++) /* find undeleted messages, add to vector */
+ if (!mail_elt (stream,i)->deleted) msg[++nmsgs] = i;
+ }
+#ifndef DISABLE_POP_PROXY
+ if (!stream && (flags == MU_ANONYMOUS)) {
+ fputs ("- Bad login\015\012",stdout);
+ return DONE;
+ }
+#endif
+ printf ("#%lu messages in %s\015\012",nmsgs,stream ? stream->mailbox :
+ "<none>");
+ return MBOX;
+}
+
+/* Parse READ command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_read (char *t)
+{
+ MESSAGECACHE *elt = NIL;
+ if (t && *t) { /* have a message number argument? */
+ /* validity check message number */
+ if (((current = strtoul (t,NIL,10)) < 1) || (current > nmsgs)) {
+ fputs ("- Invalid message number given to READ\015\012",stdout);
+ return DONE;
+ }
+ }
+ else if (current > nmsgs) { /* at end of mailbox? */
+ fputs ("=0 No more messages\015\012",stdout);
+ return MBOX;
+ }
+ /* set size if message valid and exists */
+ size = msg[current] ? (elt = mail_elt(stream,msg[current]))->rfc822_size : 0;
+ if (elt) sprintf (status,"Status: %s%s\015\012",
+ elt->seen ? "R" : " ",elt->recent ? " " : "O");
+ else status[0] = '\0'; /* no status */
+ size += strlen (status); /* update size to reflect status */
+ /* display results */
+ printf ("=%lu characters in message %lu\015\012",size + 2,current);
+ return ITEM;
+}
+
+
+/* Parse RETR command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_retr (char *t)
+{
+ unsigned long i,j;
+ STRING *bs;
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to RETR\015\012",stdout);
+ return DONE;
+ }
+ if (size) { /* message size valid? */
+ t = mail_fetch_header (stream,msg[current],NIL,NIL,&i,FT_PEEK);
+ if (i > 2) { /* only if there is something */
+ i -= 2; /* lop off last two octets */
+ while (i) { /* blat the header */
+ if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
+ if (i -= j) t += j; /* advance to incomplete data */
+ }
+ }
+ fputs (status,stdout); /* yes, output message */
+ fputs ("\015\012",stdout); /* delimit header from text */
+ if (t = mail_fetch_text (stream,msg[current],NIL,&i,FT_RETURNSTRINGSTRUCT))
+ while (i) { /* blat the text */
+ if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
+ if (i -= j) t += j; /* advance to incomplete data */
+ }
+ else for (bs = &stream->private.string; i--; )
+ if (putc (SNX (bs),stdout) == EOF) return DONE;
+ fputs ("\015\012",stdout); /* trailer to coddle PCNFS' NFSMAIL */
+ }
+ else return DONE; /* otherwise go away */
+ return NEXT;
+}
+
+/* Parse ACKS command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_acks (char *t)
+{
+ char tmp[TMPLEN];
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to ACKS\015\012",stdout);
+ return DONE;
+ }
+ /* mark message as seen */
+ sprintf (tmp,"%lu",msg[current++]);
+ mail_setflag (stream,tmp,"\\Seen");
+ return c_read (NIL); /* end message reading transaction */
+}
+
+
+/* Parse ACKD command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_ackd (char *t)
+{
+ char tmp[TMPLEN];
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to ACKD\015\012",stdout);
+ return DONE;
+ }
+ /* mark message as seen and deleted */
+ sprintf (tmp,"%lu",msg[current]);
+ mail_setflag (stream,tmp,"\\Seen \\Deleted");
+ msg[current++] = 0; /* mark message as deleted */
+ return c_read (NIL); /* end message reading transaction */
+}
+
+
+/* Parse NACK command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_nack (char *t)
+{
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to NACK\015\012",stdout);
+ return DONE;
+ }
+ return c_read (NIL); /* end message reading transaction */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* Never called */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* Can't use this mechanism. POP has no means of notifying the client of
+ new mail during the session. */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ if (state != DONE) { /* ignore if closing */
+ /* someone else screwed us */
+ goodbye = "- Mailbox expunged from under me!\015\012";
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1);
+ }
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ /* This isn't used */
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case NIL: /* information message */
+ case PARSE: /* parse glitch */
+ break; /* too many of these to log */
+ case WARN: /* warning */
+ syslog (LOG_DEBUG,"%s",string);
+ break;
+ case BYE: /* driver broke connection */
+ if (state != DONE) {
+ char tmp[MAILTMPLEN];
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
+ sayonara (1);
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ syslog (LOG_NOTICE,"%s",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ /* Not doing anything here for now */
+}
+
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
+ strncpy (password,pass,255); /* and password */
+ username[NETMAXUSER] = password[255] = '\0';
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ --critical;
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ alarm (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ mm_log (string,ERROR); /* shouldn't happen normally */
+}
diff --git a/imap/src/ipopd/ipop3d.c b/imap/src/ipopd/ipop3d.c
new file mode 100644
index 00000000..41dd96a6
--- /dev/null
+++ b/imap/src/ipopd/ipop3d.c
@@ -0,0 +1,1082 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IPOP3D - IMAP to POP3 conversion server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 November 1990
+ * Last Edited: 19 February 2008
+ */
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <time.h>
+#include "c-client.h"
+
+
+#define CRLF PSOUT ("\015\012") /* primary output terpri */
+
+
+/* Autologout timer */
+#define KODTIMEOUT 60*5
+#define LOGINTIMEOUT 60*3
+#define TIMEOUT 60*10
+
+
+/* Server states */
+
+#define AUTHORIZATION 0
+#define TRANSACTION 1
+#define UPDATE 2
+#define LOGOUT 3
+
+/* Eudora food */
+
+#define STATUS "Status: %s%s\015\012"
+#define SLEN (sizeof (STATUS)-3)
+
+
+/* Global storage */
+
+char *version = "104"; /* edit number of this server */
+short state = AUTHORIZATION; /* server state */
+short critical = NIL; /* non-zero if in critical code */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+time_t idletime = 0; /* time we went idle */
+unsigned long nmsgs = 0; /* current number of messages */
+unsigned long ndele = 0; /* number of deletes */
+unsigned long nseen = 0; /* number of mark-seens */
+unsigned long last = 0; /* highest message accessed */
+unsigned long il = 0; /* initial last message */
+char challenge[128]; /* challenge */
+char *host = NIL; /* remote host name */
+char *user = NIL; /* user name */
+char *pass = NIL; /* password */
+char *initial = NIL; /* initial response */
+long *msg = NIL; /* message translation vector */
+short *flags = NIL; /* flags */
+char *logout = "Logout";
+char *goodbye = "+OK Sayonara\015\012";
+
+
+/* POP3 flags */
+
+#define DELE 0x1
+#define SEEN 0x2
+
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void sayonara (int status);
+void clkint ();
+void kodint ();
+void hupint ();
+void trmint ();
+int pass_login (char *t,int argc,char *argv[]);
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
+char *responder (void *challenge,unsigned long clen,unsigned long *rlen);
+int mbxopen (char *mailbox);
+long blat (char *text,long lines,unsigned long size,STRING *st);
+void rset ();
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ unsigned long i,j,k;
+ char *s,*t;
+ char tmp[MAILTMPLEN];
+ time_t autologouttime;
+ char *pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ s+1 : argv[0]) : "ipop3d";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
+#include "linkage.c"
+ /* initialize server */
+ server_init (pgmname,"pop3","pop3s",clkint,kodint,hupint,trmint,NIL);
+ mail_parameters (NIL,SET_BLOCKENVINIT,VOIDT);
+ s = myusername_full (&i); /* get user name and flags */
+ mail_parameters (NIL,SET_BLOCKENVINIT,NIL);
+ if (i == MU_LOGGEDIN) { /* allow EXTERNAL if logged in already */
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,(void *) "EXTERNAL");
+ mail_parameters (NIL,SET_EXTERNALAUTHID,(void *) s);
+ }
+ { /* set up MD5 challenge */
+ AUTHENTICATOR *auth = mail_lookup_auth (1);
+ while (auth && compare_cstring (auth->name,"CRAM-MD5")) auth = auth->next;
+ /* build challenge -- less than 128 chars */
+ if (auth && auth->server && !(auth->flags & AU_DISABLE))
+ sprintf (challenge,"<%lx.%lx@%.64s>",(unsigned long) getpid (),
+ (unsigned long) time (0),tcp_serverhost ());
+ else challenge[0] = '\0'; /* no MD5 authentication */
+ }
+ /* There are reports of POP3 clients which get upset if anything appears
+ * between the "+OK" and the "POP3" in the greeting.
+ */
+ PSOUT ("+OK POP3 ");
+ if (!challenge[0]) { /* if no MD5 enable, output host name */
+ PSOUT (tcp_serverhost ());
+ PBOUT (' ');
+ }
+ PSOUT (CCLIENTVERSION);
+ PBOUT ('.');
+ PSOUT (version);
+ PSOUT (" server ready");
+ if (challenge[0]) { /* if MD5 enable, output challenge here */
+ PBOUT (' ');
+ PSOUT (challenge);
+ }
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ autologouttime = time (0) + LOGINTIMEOUT;
+ /* command processing loop */
+ while ((state != UPDATE) && (state != LOGOUT)) {
+ idletime = time (0); /* get a command under timeout */
+ alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ /* read command line */
+ while (!PSIN (tmp,MAILTMPLEN)) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Unexpected client disconnect";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading line",e);
+ goodbye = NIL;
+ rset (); /* try to gracefully close the stream */
+ if (state == TRANSACTION) mail_close (stream);
+ stream = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ alarm (0); /* make sure timeout disabled */
+ idletime = 0; /* no longer idle */
+
+ if (!strchr (tmp,'\012')) /* find end of line */
+ PSOUT ("-ERR Command line too long\015\012");
+ else if (!(s = strtok (tmp," \015\012")))
+ PSOUT ("-ERR Null command\015\012");
+ else { /* dispatch based on command */
+ ucase (s); /* canonicalize case */
+ /* snarf argument */
+ t = strtok (NIL,"\015\012");
+ /* QUIT command always valid */
+ if (!strcmp (s,"QUIT")) state = UPDATE;
+ else if (!strcmp (s,"CAPA")) {
+ AUTHENTICATOR *auth;
+ PSOUT ("+OK Capability list follows:\015\012");
+ PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012");
+ if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
+ else PSOUT ("STLS\015\012");
+ if (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))
+ PSOUT ("USER\015\012");
+ /* display secure server authenticators */
+ for (auth = mail_lookup_auth (1), s = "SASL"; auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ if (s) {
+ PSOUT (s);
+ s = NIL;
+ }
+ PBOUT (' ');
+ PSOUT (auth->name);
+ }
+ PSOUT (s ? ".\015\012" : "\015\012.\015\012");
+ }
+
+ else switch (state) { /* else dispatch based on state */
+ case AUTHORIZATION: /* waiting to get logged in */
+ if (!strcmp (s,"AUTH")) {
+ if (t && *t) { /* mechanism given? */
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ s = strtok (t," "); /* get mechanism name */
+ /* get initial response */
+ if (initial = strtok (NIL,"\015\012")) {
+ if ((*initial == '=') && !initial[1]) ++initial;
+ else if (!*initial) initial = NIL;
+ }
+ if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) {
+ PSOUT ("-ERR Bad authentication\015\012");
+ syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s,
+ tcp_clienthost ());
+ }
+ else if ((state = mbxopen ("INBOX")) == TRANSACTION)
+ syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%lu/%lu",
+ user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"Auth user=%.80s host=%.80s no mailbox",
+ user,tcp_clienthost ());
+ }
+ else {
+ AUTHENTICATOR *auth;
+ PSOUT ("+OK Supported authentication mechanisms:\015\012");
+ i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
+ for (auth = mail_lookup_auth (1); auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) &&
+ (i || (auth->flags & AU_SECURE))) {
+ PSOUT (auth->name);
+ CRLF;
+ }
+ PBOUT ('.');
+ CRLF;
+ }
+ }
+
+ else if (!strcmp (s,"APOP")) {
+ if (challenge[0]) { /* can do it if have an MD5 challenge */
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ /* get user name */
+ if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012"))))
+ PSOUT ("-ERR Missing APOP argument\015\012");
+ else if (!(user = apop_login (challenge,s,t,argc,argv)))
+ PSOUT ("-ERR Bad APOP\015\012");
+ else if ((state = mbxopen ("INBOX")) == TRANSACTION)
+ syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%lu/%lu",
+ user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"APOP user=%.80s host=%.80s no mailbox",
+ user,tcp_clienthost ());
+ }
+ else PSOUT ("-ERR Not supported\015\012");
+ }
+ /* (chuckle) */
+ else if (!strcmp (s,"RPOP"))
+ PSOUT ("-ERR Nice try, bunkie\015\012");
+ else if (!strcmp (s,"STLS")) {
+ if (t = ssl_start_tls (pgmname)) {
+ PSOUT ("-ERR STLS failed: ");
+ PSOUT (t);
+ CRLF;
+ }
+ else PSOUT ("+OK STLS completed\015\012");
+ }
+ else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
+ !strcmp (s,"USER")) {
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ if (t && *t) { /* if user name given */
+ /* skip leading whitespace (bogus clients!) */
+ while (*t == ' ') ++t;
+ /* remote user name? */
+ if (s = strchr (t,':')) {
+ *s++ = '\0'; /* tie off host name */
+ host = cpystr (t);/* copy host name */
+ user = cpystr (s);/* copy user name */
+ }
+ /* local user name */
+ else user = cpystr (t);
+ PSOUT ("+OK User name accepted, password please\015\012");
+ }
+ else PSOUT ("-ERR Missing username argument\015\012");
+ }
+ else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
+ user && *user && !strcmp (s,"PASS"))
+ state = pass_login (t,argc,argv);
+ else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
+ break;
+
+ case TRANSACTION: /* logged in */
+ if (!strcmp (s,"STAT")) {
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ /* message still exists? */
+ if (msg[i] && !(flags[i] & DELE)) {
+ j++; /* count one more undeleted message */
+ k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
+ }
+ sprintf (tmp,"+OK %lu %lu\015\012",j,k);
+ PSOUT (tmp);
+ }
+ else if (!strcmp (s,"LIST")) {
+ if (t && *t) { /* argument do single message */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ sprintf (tmp,"+OK %lu %lu\015\012",i,
+ mail_elt(stream,msg[i])->rfc822_size + SLEN);
+ PSOUT (tmp);
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else { /* entire mailbox */
+ PSOUT ("+OK Mailbox scan listing follows\015\012");
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ if (msg[i] && !(flags[i] & DELE)) {
+ sprintf (tmp,"%lu %lu\015\012",i,
+ mail_elt (stream,msg[i])->rfc822_size + SLEN);
+ PSOUT (tmp);
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ }
+ else if (!strcmp (s,"UIDL")) {
+ if (t && *t) { /* argument do single message */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ sprintf (tmp,"+OK %lu %08lx%08lx\015\012",i,stream->uid_validity,
+ mail_uid (stream,msg[i]));
+ PSOUT (tmp);
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else { /* entire mailbox */
+ PSOUT ("+OK Unique-ID listing follows\015\012");
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ if (msg[i] && !(flags[i] & DELE)) {
+ sprintf (tmp,"%lu %08lx%08lx\015\012",i,stream->uid_validity,
+ mail_uid (stream,msg[i]));
+ PSOUT (tmp);
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ }
+
+ else if (!strcmp (s,"RETR")) {
+ if (t && *t) { /* must have an argument */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ MESSAGECACHE *elt;
+ /* update highest message accessed */
+ if (i > last) last = i;
+ sprintf (tmp,"+OK %lu octets\015\012",
+ (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
+ PSOUT (tmp);
+ /* if not marked seen or noted to be marked */
+ if (!(elt->seen || (flags[i] & SEEN))) {
+ ++nseen; /* note that we need to mark it seen */
+ flags[i] |= SEEN;
+ }
+ /* get header */
+ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
+ blat (t,-1,k,NIL);/* write up to trailing CRLF */
+ /* build status */
+ sprintf (tmp,STATUS,elt->seen ? "R" : " ",
+ elt->recent ? " " : "O");
+ if (k < 4) CRLF; /* don't write Status: if no header */
+ /* normal header ending with CRLF CRLF? */
+ else if (t[k-3] == '\012') {
+ PSOUT (tmp); /* write status */
+ CRLF; /* then write second CRLF */
+ }
+ else { /* abnormal - no blank line at end of header */
+ CRLF; /* write CRLF first then */
+ PSOUT (tmp);
+ }
+ /* output text */
+ t = mail_fetch_text (stream,msg[i],NIL,&k,
+ FT_RETURNSTRINGSTRUCT | FT_PEEK);
+ if (k) { /* only if there is a text body */
+ blat (t,-1,k,&stream->private.string);
+ CRLF; /* end of list */
+ }
+ PBOUT ('.');
+ CRLF;
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else PSOUT ("-ERR Missing message number argument\015\012");
+ }
+
+ else if (!strcmp (s,"DELE")) {
+ if (t && *t) { /* must have an argument */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ /* update highest message accessed */
+ if (i > last) last = i;
+ flags[i] |= DELE; /* note that deletion is requested */
+ PSOUT ("+OK Message deleted\015\012");
+ ++ndele; /* one more message deleted */
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else PSOUT ("-ERR Missing message number argument\015\012");
+ }
+ else if (!strcmp (s,"NOOP"))
+ PSOUT ("+OK No-op to you too!\015\012");
+ else if (!strcmp (s,"LAST")) {
+ sprintf (tmp,"+OK %lu\015\012",last);
+ PSOUT (tmp);
+ }
+ else if (!strcmp (s,"RSET")) {
+ rset (); /* reset the mailbox */
+ PSOUT ("+OK Reset state\015\012");
+ }
+
+ else if (!strcmp (s,"TOP")) {
+ if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ /* skip whitespace */
+ while (*s == ' ') s++;
+ /* make sure line count argument good */
+ if ((*s >= '0') && (*s <= '9')) {
+ MESSAGECACHE *elt = mail_elt (stream,msg[i]);
+ j = strtoul (s,NIL,10);
+ /* update highest message accessed */
+ if (i > last) last = i;
+ PSOUT ("+OK Top of message follows\015\012");
+ /* get header */
+ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
+ blat (t,-1,k,NIL);/* write up to trailing CRLF */
+ /* build status */
+ sprintf (tmp,STATUS,elt->seen ? "R" : " ",
+ elt->recent ? " " : "O");
+ if (k < 4) CRLF; /* don't write Status: if no header */
+ /* normal header ending with CRLF CRLF? */
+ else if (t[k-3] == '\012') {
+ PSOUT (tmp); /* write status */
+ CRLF; /* then write second CRLF */
+ }
+ else { /* abnormal - no blank line at end of header */
+ CRLF; /* write CRLF first then */
+ PSOUT (tmp);
+ }
+ if (j) { /* want any text lines? */
+ /* output text */
+ t = mail_fetch_text (stream,msg[i],NIL,&k,
+ FT_PEEK | FT_RETURNSTRINGSTRUCT);
+ /* tie off final line if full text output */
+ if (k && (j -= blat (t,j,k,&stream->private.string))) CRLF;
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ else PSOUT ("-ERR Bad line count argument\015\012");
+ }
+ else PSOUT ("-ERR Bad message number argument\015\012");
+ }
+
+ else if (!strcmp (s,"XTND"))
+ PSOUT ("-ERR Sorry I can't do that\015\012");
+ else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
+ break;
+ default:
+ PSOUT ("-ERR Server in unknown state\015\012");
+ break;
+ }
+ }
+ PFLUSH (); /* make sure output finished */
+ if (autologouttime) { /* have an autologout in effect? */
+ /* cancel if no longer waiting for login */
+ if (state != AUTHORIZATION) autologouttime = 0;
+ /* took too long to login */
+ else if (autologouttime < time (0)) {
+ goodbye = "-ERR Autologout\015\012";
+ logout = "Autologout";
+ state = LOGOUT; /* sayonara */
+ }
+ }
+ }
+
+ /* open and need to update? */
+ if (stream && (state == UPDATE)) {
+ if (nseen) { /* only bother if messages need marking seen */
+ *(s = tmp) = '\0'; /* clear sequence */
+ for (i = 1; i <= nmsgs; ++i) if (flags[i] & SEEN) {
+ for (j = i + 1, k = 0; (j <= nmsgs) && (flags[j] & SEEN); ++j) k = j;
+ if (k) sprintf (s,",%lu:%lu",i,k);
+ else sprintf (s,",%lu",i);
+ s += strlen (s); /* point to end of string */
+ if ((s - tmp) > (MAILTMPLEN - 30)) {
+ mail_setflag (stream,tmp + 1,"\\Seen");
+ *(s = tmp) = '\0'; /* restart sequence */
+ }
+ i = j; /* continue after the range */
+ }
+ if (tmp[0]) mail_setflag (stream,tmp + 1,"\\Seen");
+ }
+ if (ndele) { /* any messages to delete? */
+ *(s = tmp) = '\0'; /* clear sequence */
+ for (i = 1; i <= nmsgs; ++i) if (flags[i] & DELE) {
+ for (j = i + 1, k = 0; (j <= nmsgs) && (flags[j] & DELE); ++j) k = j;
+ if (k) sprintf (s,",%lu:%lu",i,k);
+ else sprintf (s,",%lu",i);
+ s += strlen (s); /* point to end of string */
+ if ((s - tmp) > (MAILTMPLEN - 30)) {
+ mail_setflag (stream,tmp + 1,"\\Deleted");
+ *(s = tmp) = '\0'; /* restart sequence */
+ }
+ i = j; /* continue after the range */
+ }
+ if (tmp[0]) mail_setflag (stream,tmp + 1,"\\Deleted");
+ mail_expunge (stream);
+ }
+ syslog (LOG_INFO,"Update user=%.80s host=%.80s nmsgs=%lu ndele=%lu nseen=%lu",
+ user,tcp_clienthost (),stream->nmsgs,ndele,nseen);
+ mail_close (stream);
+ }
+ sayonara (0);
+ return 0; /* stupid compilers */
+}
+
+
+/* Say goodbye
+ * Accepts: exit status
+ *
+ * Does not return
+ */
+
+void sayonara (int status)
+{
+ logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
+ if (goodbye) { /* have a goodbye message? */
+ PSOUT (goodbye);
+ PFLUSH (); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (status); /* all done */
+}
+
+/* Clock interrupt
+ */
+
+void clkint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Autologout; idle for too long\015\012";
+ logout = "Autologout";
+ if (critical) state = LOGOUT; /* badly hosed if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1);
+ }
+}
+
+
+/* Kiss Of Death interrupt
+ */
+
+void kodint ()
+{
+ /* only if idle */
+ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Received Kiss of Death\015\012";
+ logout = "Killed (lost mailbox lock)";
+ if (critical) state =LOGOUT;/* must defer if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+ }
+}
+
+
+/* Hangup interrupt
+ */
+
+void hupint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = NIL; /* nobody left to talk to */
+ logout = "Hangup";
+ if (critical) state = LOGOUT; /* must defer if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Termination interrupt
+ */
+
+void trmint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Killed\015\012";
+ logout = "Killed";
+ if (critical) state = LOGOUT; /* must defer if in critical code */
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions.
+ */
+ else sayonara (1); /* die die die */
+}
+
+/* Parse PASS command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+int pass_login (char *t,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ /* flush old passowrd */
+ if (pass) fs_give ((void **) &pass);
+ if (!(t && *t)) { /* if no password given */
+ PSOUT ("-ERR Missing password argument\015\012");
+ return AUTHORIZATION;
+ }
+ pass = cpystr (t); /* copy password argument */
+ if (!host) { /* want remote mailbox? */
+ /* no, delimit user from possible admin */
+ if (t = strchr (user,'*')) *t++ ='\0';
+ /* attempt the login */
+ if (server_login (user,pass,t,argc,argv)) {
+ int ret = mbxopen ("INBOX");
+ if (ret == TRANSACTION) /* mailbox opened OK? */
+ syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s nmsgs=%lu/%lu",
+ t ? "Admin " : "",user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s no mailbox",
+ t ? "Admin " : "",user,tcp_clienthost ());
+ return ret;
+ }
+ }
+#ifndef DISABLE_POP_PROXY
+ /* remote; build remote INBOX */
+ else if (anonymous_login (argc,argv)) {
+ syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
+ user,tcp_clienthost ());
+ sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user);
+ /* disable rimap just in case */
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+ return mbxopen (tmp);
+ }
+#endif
+ /* vague error message to confuse crackers */
+ PSOUT ("-ERR Bad login\015\012");
+ return AUTHORIZATION;
+}
+
+/* Authentication responder
+ * Accepts: challenge
+ * length of challenge
+ * pointer to response length return location if non-NIL
+ * Returns: response
+ */
+
+#define RESPBUFLEN 8*MAILTMPLEN
+
+char *responder (void *challenge,unsigned long clen,unsigned long *rlen)
+{
+ unsigned long i,j;
+ unsigned char *t,resp[RESPBUFLEN];
+ char tmp[MAILTMPLEN];
+ if (initial) { /* initial response given? */
+ if (clen) return NIL; /* not permitted */
+ /* set up response */
+ t = (unsigned char *) initial;
+ initial = NIL; /* no more initial response */
+ return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i);
+ }
+ PSOUT ("+ ");
+ for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++)
+ if (t[j] > ' ') PBOUT (t[j]);
+ fs_give ((void **) &t);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ resp[RESPBUFLEN-1] = '\0'; /* last buffer character is guaranteed NUL */
+ alarm (LOGINTIMEOUT); /* get a response under timeout */
+ clearerr (stdin); /* clear stdin errors */
+ /* read buffer */
+ while (!PSIN ((char *) resp,RESPBUFLEN)) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Command stream end of file";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading authentication",e);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) {
+ int c;
+ while ((c = PBIN ()) != '\012') if (c == EOF) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Command stream end of file";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading auth char",e);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ return NIL;
+ }
+ alarm (0); /* make sure timeout disabled */
+ if (t[-1] == '\015') --t; /* remove CR */
+ *t = '\0'; /* tie off buffer */
+ return (resp[0] != '*') ?
+ (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
+}
+
+/* Select mailbox
+ * Accepts: mailbox name
+ * Returns: new state
+ */
+
+int mbxopen (char *mailbox)
+{
+ unsigned long i,j;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (msg) fs_give ((void **) &msg);
+ /* open mailbox */
+ if (!(stream = mail_open (stream,mailbox,NIL)))
+ goodbye = "-ERR Unable to open user's INBOX\015\012";
+ else if (stream->rdonly) /* make sure not readonly */
+ goodbye = "-ERR Can't get lock. Mailbox in use\015\012";
+ else {
+ nmsgs = 0; /* no messages yet */
+ if (j = stream->nmsgs) { /* if mailbox non-empty */
+ sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* create 1-origin tables */
+ msg = (long *) fs_get (++j * sizeof (long));
+ flags = (short *) fs_get (j * sizeof (short));
+ /* build map */
+ for (i = 1; i < j; ++i) if (!(elt = mail_elt (stream,i))->deleted) {
+ msg[++nmsgs] = i; /* note the presence of this message */
+ if (elt->seen) il = nmsgs;/* and set up initial LAST */
+ }
+ /* make sure unused map entries are zero */
+ for (i = nmsgs + 1; i < j; ++i) msg[i] = 0;
+ rset (); /* do implicit RSET */
+ sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs);
+ PSOUT (tmp);
+ return TRANSACTION;
+ }
+ syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s",
+ user,tcp_clienthost ());
+ return UPDATE;
+}
+
+/* Blat a string with dot checking
+ * Accepts: string
+ * maximum number of lines if greater than zero
+ * maximum number of bytes to output
+ * alternative stringstruct
+ * Returns: number of lines output
+ *
+ * This routine is uglier and kludgier than it should be, just to be robust
+ * in the case of a message which doesn't end in a newline. Yes, this routine
+ * does truncate the last two bytes from the text. Since it is normally a
+ * newline and the main routine adds it back, it usually does not make a
+ * difference. But if it isn't, since the newline is required and the octet
+ * counts have to match, there's no choice but to truncate.
+ */
+
+long blat (char *text,long lines,unsigned long size,STRING *st)
+{
+ char c,d,e;
+ long ret = 0;
+ /* no-op if zero lines or empty string */
+ if (!(lines && (size-- > 2))) return 0;
+ if (text) {
+ c = *text++; d = *text++; /* collect first two bytes */
+ if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
+ while (lines && --size) { /* copy loop */
+ e = *text++; /* get next byte */
+ PBOUT (c); /* output character */
+ if (c == '\012') { /* end of line? */
+ ret++; --lines; /* count another line */
+ /* double leading dot as necessary */
+ if (lines && size && (d == '.')) PBOUT ('.');
+ }
+ c = d; d = e; /* move to next character */
+ }
+ }
+ else {
+ c = SNX (st); d = SNX (st); /* collect first two bytes */
+ if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
+ while (lines && --size) { /* copy loop */
+ e = SNX (st); /* get next byte */
+ PBOUT (c); /* output character */
+ if (c == '\012') { /* end of line? */
+ ret++; --lines; /* count another line */
+ /* double leading dot as necessary */
+ if (lines && size && (d == '.')) PBOUT ('.');
+ }
+ c = d; d = e; /* move to next character */
+ }
+ }
+ return ret;
+}
+
+/* Reset mailbox
+ */
+
+void rset ()
+{
+ /* clear all flags */
+ if (flags) memset ((void *) flags,0,(nmsgs + 1) * sizeof (short));
+ ndele = nseen = 0; /* no more deleted or seen messages */
+ last = il; /* restore previous LAST value */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* Never called */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* Can't use this mechanism. POP has no means of notifying the client of
+ new mail during the session. */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ unsigned long i = number + 1;
+ msg[number] = 0; /* I bet that this will annoy someone */
+ while (i <= nmsgs) --msg[i++];
+}
+
+
+/* Message flag status change
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ /* This isn't used */
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case NIL: /* information message */
+ case PARSE: /* parse glitch */
+ break; /* too many of these to log */
+ case WARN: /* warning */
+ syslog (LOG_DEBUG,"%s",string);
+ break;
+ case BYE: /* driver broke connection */
+ if (state != UPDATE) {
+ char tmp[MAILTMPLEN];
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ syslog (LOG_NOTICE,"%s",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ /* Not doing anything here for now */
+}
+
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
+ if (pass) {
+ strncpy (password,pass,255);/* and password */
+ fs_give ((void **) &pass);
+ }
+ else memset (password,0,256); /* no password to send, abort login */
+ username[NETMAXUSER] = password[255] = '\0';
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ --critical;
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ alarm (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ mm_log (string,ERROR); /* shouldn't happen normally */
+}
diff --git a/imap/src/ipopd/ipopd.8 b/imap/src/ipopd/ipopd.8
new file mode 100644
index 00000000..95a5b099
--- /dev/null
+++ b/imap/src/ipopd/ipopd.8
@@ -0,0 +1,75 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH IPOPD 8 "August 30, 2006"
+.UC 5
+.SH NAME
+IPOPd \- Post Office Protocol server
+.SH SYNOPSIS
+.B /usr/etc/ipop2d
+.PP
+.B /usr/etc/ipop3d
+.SH DESCRIPTION
+.I ipop2d
+and
+.I ipop3d
+are servers which support the
+.B POP2
+and
+.B POP3
+remote mail access protocols respectively.
+.I ipop2d
+and
+.I ipop3d
+can also be used by
+.B POP2
+and
+.B POP3
+clients respecitively to access mailboxes on
+.B IMAP
+servers by specifying a login user name in the form <host>:<user>
+e.g.,
+.B SERVER.WASHINGTON.EDU:SMITH.
+.PP
+These daemons contain CRAM-MD5 and APOP support. See the md5.txt
+documentation file for additional information.
+.PP
+.I ipop2d
+and
+.I ipop3d
+are invoked by the internet server (see
+.IR inetd (8)),
+normally for requests to connect to the
+.B POP
+port as indicated by the
+.I /etc/services
+file (see
+.IR services (5)).
+.SH "SEE ALSO"
+imapd(8)
+.SH BUGS
+The
+.B POP2
+and
+.B POP3
+protocols are intrinsically less flexible than
+.B IMAP
+and do not maintain `read' vs `unread' state on the server. As a result,
+most
+.B POP
+based software transfers all the mail from the server to the client and
+deletes it from the server. This necessarily locks the user into using only
+a single client.
+.PP
+.B POP3
+does not allow you to specify an alternate folder from the user's default.
diff --git a/imap/src/ipopd/makefile.nt b/imap/src/ipopd/makefile.nt
new file mode 100644
index 00000000..ef32819b
--- /dev/null
+++ b/imap/src/ipopd/makefile.nt
@@ -0,0 +1,57 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/makefile.ntk b/imap/src/ipopd/makefile.ntk
new file mode 100644
index 00000000..4a90a7a0
--- /dev/null
+++ b/imap/src/ipopd/makefile.ntk
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/makefile.w2k b/imap/src/ipopd/makefile.w2k
new file mode 100644
index 00000000..efa63c2f
--- /dev/null
+++ b/imap/src/ipopd/makefile.w2k
@@ -0,0 +1,56 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/Makefile b/imap/src/mailutil/Makefile
new file mode 100644
index 00000000..acc58bbd
--- /dev/null
+++ b/imap/src/mailutil/Makefile
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: mailutil Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 2 February 1993
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+mailutil: $(CCLIENTLIB) mailutil.o
+ $(CC) $(CFLAGS) -o mailutil mailutil.o $(LDFLAGS)
+
+mailutil.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o mailutil
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mailutil/mailutil.1 b/imap/src/mailutil/mailutil.1
new file mode 100644
index 00000000..964fb1b8
--- /dev/null
+++ b/imap/src/mailutil/mailutil.1
@@ -0,0 +1,264 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH mailutil 1 "March 3, 2008"
+.SH NAME
+mailutil - mail utility program
+.nh
+.SH SYNTAX
+.B mailutil command [switches] [arguments]
+.PP
+All commands accept the -d, -v, and -u switches in addition to any
+command-specific switches.
+.PP
+.B mailutil check [MAILBOX]
+.PP
+.B mailutil create MAILBOX
+.PP
+.B mailutil delete MAILBOX
+.PP
+.B mailutil rename SOURCE DESTINATION
+.PP
+.B mailutil copy [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil move [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil append [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil appenddelete [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil prune MAILBOX CRITERIA
+.PP
+.B mailutil transfer [-m mode] [-rw] [-kw] [-ig] SOURCE DESTINATION
+.SH DESCRIPTION
+.B mailutil
+replaces the old chkmail, imapcopy, imapmove, imapxfer, mbxcopy,
+mbxcreat, and mbxcvt programs.
+.PP
+.B mailutil check
+determines whether new mail exists in the given mailbox (the default
+is INBOX). The number of new messages is defined as the number of
+messages that have "Recent" status set. If the mailbox contains no
+new messages,
+.B mailutil check
+will indicate that no new mail is present;
+otherwise, it will report the number of new messages. In either case,
+it will also indicate the canonical form of the name of the mailbox.
+.PP
+.B mailutil create
+creates a new
+.I mailbox
+with the given name. The mailbox name must not already exist. A mailbox
+can be created in a particular format by prefixing the name with
+.I #driver.
+followed by the format name and a
+.I /
+character. For example, the command
+.br
+ mailutil create #driver.mbx/junkmail
+.br
+will create a new mailbox named "junkmail" in mbx format.
+.PP
+.B mailutil delete
+deletes an existing
+.I mailbox
+with the given name.
+.PP
+.B mailutil rename
+renames an existing mailbox to a new name (which must not already exist).
+This only works if the old and new names are in the same mail store. A
+more general means to rename a mailbox is to do a
+.B mailutil copy
+of the old name to the new name, followed by a
+.B mailutil delete
+of the old name.
+.PP
+.B mailutil copy
+creates a new mailbox and copies messages from the old mailbox to the
+new mailbox. As in
+.B mailutil create
+a mailbox format can be specified with the new mailbox. For example, the
+command
+.br
+ mailutil copy INBOX #driver.mbx/INBOX
+.br
+will copy messages from your existing INBOX to an mbx-format INBOX.
+.PP
+.B mailutil move
+is similar to
+.B mailutil copy
+but in addition will also remove (delete and expunge) the messages from the
+old mailbox after copying them to the new mailbox.
+.PP
+.B mailutil append
+and
+.B mailutil appenddelete
+are similar to
+.B mailutil copy
+and
+.B mailutil move
+respectively except that they do not create the destination mailbox.
+.PP
+.B mailutil prune
+prunes the mailbox of messages which match certain criteria, which are
+in the form of IMAP2 (RFC 1176) SEARCH arguments. For example, the
+command.
+.br
+ mailutil prune INBOX "before 1-jan-2004"
+.br
+will delete and expunge all messages written before January 1, 2004.
+.PP
+Note that mailutil implements pruning by deleting the matching messages,
+and then expunging the mailbox. Consequently, mailutil will also expunge
+any messages which were deleted at the time of the pruning.
+.PP
+.B mailutil transfer
+copies an entire hierarchy of mailboxes from the named source to the
+named destination. Mailboxes are created on the destination as
+needed. Any error in copying messages will cause the transfer to stop.
+.PP
+Normally, any error in creation will cause the transfer to stop.
+However, if
+.B -m MODE
+or
+.B -merge MODE
+is specified, a merging transfer is performed. The
+.B MODE
+argument indicats the type of merge:
+.PP
+.B -m[erge] prompt
+indicates that the user should be asked for an alternative name to create.
+If creating the new name fails, the user will be asked again.
+.PP
+.B -m[erge] append
+indicates that it's alright to copy the messages into an existing mailbox
+with that name. If the mailbox does not exist, the user will be prompted
+for an alternative name.
+.PP
+.B -m[erge] suffix=XXXX
+where XXXX is any string, indicates that an alternative name should be
+built by appending the given suffix to the name. It that alternative name
+can't be created, then the user will be prompted for an alternative name.
+.PP
+The source hierarchy consists of all mailboxes which start
+with the given source name. With the exception of a remote system
+specification (within "{}" braces), the source name is used as the
+name of the destination. The destination hierarchy is a prefix
+applied to any new names being created. For example,
+.br
+ mailutil transfer foo bar
+.br
+will copy all mailboxes with names beginning with "foo" to names
+beginning with "bar" (hence "foobar" will be copied to "barfoobar").
+Similarly,
+.br
+ mailutil transfer "{imap.foo.com}" "{imap.bar.com}old/"
+.br
+will copy all mailboxes from the imap.foo.com IMAP server to
+equivalent names starting with "old/" on the imap.bar.com IMAP server.
+.SH FLAGS
+The
+.B -d
+or
+.B -debug
+flag prints full debugging telemetry including protocol operations.
+.PP
+The
+.B -v
+or
+.B -verbose
+flag prints verbose (non-error) telemetry.
+.PP
+The
+.B -u USERID
+or
+.B -user USERID
+switch attempts to become the indicated user. This is for the benefit of
+system administrators who want to do mailutil operations on a userid that
+does not normally have shell access.
+.PP
+The
+.B -rw
+or
+.B -rwcopy
+flag causes the source mailbox to be open in readwrite mode rather than
+readonly mode. Normally, mailutil tries to use readonly mode to avoid
+altering any flags in the source mailbox, but some mailbox types, e.g.
+POP3, can't be open in readonly mode.
+.PP
+The
+.B -kw
+or
+.B -kwcopy
+flag causes the keywords of the source mailbox to be created in the
+destination mailbox. Normally, mailutil does not create keywords in
+the destination mailbox so only those keywords that are already defined
+in the destination mailbox will be preserved. Note that some IMAP servers
+may automatically create keywords, so this flag may not be necessary.
+.PP
+The
+.B -ig
+or
+.B -ignore
+flag causes the keywords of the source mailbox to be ignored completely
+and no attempt is made to copy them to the destination mailbox.
+.PP
+The
+.B -ig[nore]
+and
+.B -kw[copy]
+flags are mutually exclusive.
+.SH ARGUMENTS
+The arguments are standard c-client mailbox names. A
+variety of mailbox name formats and types of mailboxes are supported
+by c-client; examples of the most common forms of names are:
+.PP
+.I
+.IP Name 15
+.I Meaning
+.IP INBOX
+primary incoming mail folder on the local system
+.IP archive/tx-project
+mail folder named "tx-project" in "archive" subdirectory of local
+filesystem home directory
+.IP {imapserver.foo.com}INBOX
+primary incoming mail folder on IMAP server system
+"imapserver.foo.com"
+.IP {imapserver.foo.com}archive/tx-project
+mail folder named "tx-project" in "archive" subdirectory on IMAP
+server system "imapserver.foo.com"
+.IP #news.comp.mail.misc
+newsgroup "comp.mail.misc" on local filesystem
+.IP {newserver.foo.com/nntp}comp.mail.misc
+newsgroup "comp.mail.misc" on NNTP server system "newserver.foo.com"
+.IP {popserver.foo.com/pop3}
+mail folder on POP3 server system "popserver.foo.com"
+.LP
+See your system manager for more information about the types of
+mailboxes which are available on your system.
+.SH RESTRICTIONS
+You must surround a
+.I {host}mailbox
+argument with quotation marks if you run
+.B mailutil
+from
+.IR csh (1)
+or another shell for which braces have special meaning.
+.PP
+You must surround a
+.I #driver.format/mailbox
+argument with quotation marks if you run
+.B mailutil
+from a shell in which "#" is the comment character.
+.SH AUTHOR
+Mark Crispin, MRC@Washington.EDU
diff --git a/imap/src/mailutil/mailutil.c b/imap/src/mailutil/mailutil.c
new file mode 100644
index 00000000..2489195f
--- /dev/null
+++ b/imap/src/mailutil/mailutil.c
@@ -0,0 +1,942 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail utility
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 2 February 1994
+ * Last Edited: 19 February 2008
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "c-client.h"
+#ifdef SYSCONFIG /* defined in env_unix.h */
+#include <pwd.h>
+#endif
+
+/* Globals */
+
+char *version = "13"; /* edit number */
+int debugp = NIL; /* flag saying debug */
+int verbosep = NIL; /* flag saying verbose */
+int rwcopyp = NIL; /* flag saying readwrite copy (for POP) */
+int kwcopyp = NIL; /* flag saying keyword copy */
+int ignorep = NIL; /* flag saying ignore keywords */
+int critical = NIL; /* flag saying in critical code */
+int trycreate = NIL; /* [TRYCREATE] seen */
+char *suffix = NIL; /* suffer merge mode suffix text */
+int ddelim = -1; /* destination delimiter */
+FILE *f = NIL;
+
+/* Usage strings */
+
+char *usage2 = "usage: %s %s\n\n%s\n";
+char *usage3 = "usage: %s %s %s\n\n%s\n";
+char *usgchk = "check [MAILBOX]";
+char *usgcre = "create MAILBOX";
+char *usgdel = "delete MAILBOX";
+char *usgren = "rename SOURCE DESTINATION";
+char *usgcpymov = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
+char *usgappdel = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
+char *usgprn = "prune mailbox SEARCH_CRITERIA";
+char *usgxfr = "transfer [-rw[copy]] [-kw[copy]] [-ig[nore]] [-m[erge] m] SOURCE DEST";
+#ifdef SYSCONFIG
+char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]] [-u[ser] userid] [--]";
+#else
+char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]]";
+#endif
+
+/* Merge modes */
+
+#define mPROMPT 1
+#define mAPPEND 2
+#define mSUFFIX 3
+
+
+/* Function prototypes */
+
+void ms_init (STRING *s,void *data,unsigned long size);
+char ms_next (STRING *s);
+void ms_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+SEARCHPGM *prune_criteria (char *criteria);
+int prune_criteria_number (unsigned long *number,char **r);
+int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
+ int mode);
+long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+
+
+/* Append package */
+
+typedef struct append_package {
+ MAILSTREAM *stream; /* source stream */
+ unsigned long msgno; /* current message number */
+ unsigned long msgmax; /* maximum message number */
+ char *flags; /* current flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPENDPACKAGE;
+
+
+/* Message string driver for message stringstructs */
+
+STRINGDRIVER mstring = {
+ ms_init, /* initialize string structure */
+ ms_next, /* get next byte in string structure */
+ ms_setpos /* set position in string structure */
+};
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void ms_init (STRING *s,void *data,unsigned long size)
+{
+ APPENDPACKAGE *md = (APPENDPACKAGE *) data;
+ s->data = data; /* note stream/msgno and header length */
+ mail_fetch_header (md->stream,md->msgno,NIL,NIL,&s->data1,
+ FT_PREFETCHTEXT|FT_PEEK);
+#if 0
+ s->size = size; /* message size */
+#else /* This kludge is necessary because of broken IMAP servers (sigh!) */
+ mail_fetch_text (md->stream,md->msgno,NIL,&s->size,FT_PEEK);
+ s->size += s->data1; /* header + body size */
+#endif
+ SETPOS (s,0);
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char ms_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void ms_setpos (STRING *s,unsigned long i)
+{
+ APPENDPACKAGE *md = (APPENDPACKAGE *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetch_header (md->stream,md->msgno,NIL,NIL,NIL,FT_PEEK);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetch_text (md->stream,md->msgno,NIL,NIL,FT_PEEK);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ MAILSTREAM *source = NIL;
+ MAILSTREAM *dest = NIL;
+ SEARCHPGM *criteria;
+ char c,*s,*dp,*t,*t1,tmp[MAILTMPLEN],mbx[MAILTMPLEN];
+ unsigned long m,len,curlen,start,last;
+ int i;
+ int merge = NIL;
+ int retcode = 1;
+ int moreswitchp = T;
+ char *cmd = NIL;
+ char *src = NIL;
+ char *dst = NIL;
+ char *pgm = argc ? argv[0] : "mailutil";
+#include "linkage.c"
+ for (i = 1; i < argc; i++) {
+ s = argv[i]; /* pick up argument */
+ /* parse switches */
+ if (moreswitchp && (*s == '-')) {
+ if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T;
+ else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T;
+ else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T;
+ else if (!strcmp (s,"-kwcopy") || !strcmp (s,"-kw")) kwcopyp = T;
+ else if (!strcmp (s,"-ignore") || !strcmp (s,"-ig")) ignorep = T;
+ else if ((!strcmp (s,"-merge") || !strcmp (s,"-m")) && (++i < argc)) {
+ if (!strcmp (s = argv[i],"prompt")) merge = mPROMPT;
+ else if (!strcmp (s,"append")) merge = mAPPEND;
+ else if (!strncmp (s,"suffix=",7) && s[7]) {
+ merge = mSUFFIX;
+ suffix = cpystr (s+7);
+ }
+ else {
+ printf ("unknown merge option: %s\n",s);
+ exit (retcode);
+ }
+ }
+
+#ifdef SYSCONFIG
+ else if ((!strcmp (s,"-user") || !strcmp (s,"-u")) && (++i < argc)) {
+ struct passwd *pw = getpwnam (s = argv[i]);
+ if (!pw) {
+ printf ("unknown user id: %s\n",argv[i]);
+ exit (retcode);
+ }
+ else if (setuid (pw->pw_uid)) {
+ perror ("unable to change user id");
+ exit (retcode);
+ }
+ }
+#endif
+ /* -- means no more switches, so mailbox
+ name can start with "-" */
+ else if ((s[1] == '-') && !s[2]) moreswitchp = NIL;
+ else {
+ printf ("unknown switch: %s\n",s);
+ exit (retcode);
+ }
+ }
+ else if (!cmd) cmd = s; /* first non-switch is command */
+ else if (!src) src = s; /* second non-switch is source */
+ else if (!dst) dst = s; /* third non-switch is destination */
+ else {
+ printf ("unknown argument: %s\n",s);
+ exit (retcode);
+ }
+ }
+ if (kwcopyp && ignorep) {
+ puts ("-kwcopy and -ignore are mutually exclusive");
+ exit (retcode);
+ }
+ if (!cmd) cmd = ""; /* prevent SEGV */
+
+ if (!strcmp (cmd,"check")) { /* check for new messages */
+ if (!src) src = "INBOX";
+ if (dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgchk,stdsw);
+ else if (mail_status (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,
+ src,SA_MESSAGES | SA_RECENT | SA_UNSEEN))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"create")) {
+ if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgcre,stdsw);
+ else if (mail_create (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"delete")) {
+ if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgdel,stdsw);
+ else if (mail_delete (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"rename")) {
+ if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgren,stdsw);
+ else if (mail_rename (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src,dst))
+ retcode = 0;
+ }
+
+ else if ((i = !strcmp (cmd,"move")) || !strcmp (cmd,"copy")) {
+ if (!src || !dst || merge) printf (usage3,pgm,cmd,usgcpymov,stdsw);
+ else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
+ (debugp ? OP_DEBUG : NIL))) {
+ dest = NIL; /* open destination stream if network */
+ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)))) {
+ if (mbxcopy (source,dest,dst,T,i,merge)) retcode = 0;
+ }
+ }
+ }
+ else if ((i = !strcmp (cmd,"appenddelete")) || !strcmp (cmd,"append")) {
+ if (!src || !dst || merge) printf (usage3,pgm,cmd,usgappdel,stdsw);
+ else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
+ (debugp ? OP_DEBUG : NIL))) {
+ dest = NIL; /* open destination stream if network */
+ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)))) {
+ if (mbxcopy (source,dest,dst,NIL,i,merge)) retcode = 0;
+ }
+ }
+ }
+
+ else if (!strcmp (cmd,"prune")) {
+ if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep ||
+ !(criteria = prune_criteria (dst))) printf (usage2,pgm,usgprn,stdsw);
+ else if ((source = mail_open (NIL,src,(debugp ? OP_DEBUG : NIL))) &&
+ mail_search_full (source,NIL,criteria,SE_FREE)) {
+ for (m = 1, s = t = NIL, len = start = last = 0; m <= source->nmsgs; m++)
+ if (mail_elt (source,m)->searched) {
+ if (s) { /* continuing a range? */
+ if (m == last + 1) last = m;
+ else { /* no, end of previous range? */
+ if (last != start) sprintf (t,":%lu,%lu",last,m);
+ /* no, just this message */
+ else sprintf (t,",%lu",m);
+ start = last = m; /* either way, start new range */
+ /* running out of space? */
+ if ((len - (curlen = (t += strlen (t)) - s)) < 20) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + curlen; /* relocate current pointer */
+ }
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = m);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ /* finish last range if necessary */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* delete/expunge any matching messages */
+ mail_flag (source,s,"\\Deleted",ST_SET);
+ m = source->nmsgs; /* get number of messages before purge */
+ mail_expunge (source);
+ printf ("%lu message(s) purged\n",m - source->nmsgs);
+ fs_give ((void **) &s); /* flush buffer */
+ }
+ else puts ("No matching messages, so nothing purged");
+ source = mail_close (source);
+ }
+ }
+
+ else if (!strcmp (cmd,"transfer")) {
+ if (!src || !dst) printf (usage2,pgm,usgxfr,stdsw);
+ else if ((*src == '{') && /* open source mailbox */
+ !(source = mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL))));
+ else if ((*dst == '{') && /* open destination server */
+ !(dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL))));
+ else if (!(f = tmpfile ())) puts ("can't open temporary file");
+ else {
+ if (verbosep) puts ("Listing mailboxes...");
+ if (dest) strcpy (strchr (strcpy (tmp,dest->mailbox),'}') + 1,
+ dp = strchr (dst,'}') + 1);
+ else {
+ dp = dst;
+ tmp[0] = '\0';
+ }
+ mail_list (dest,tmp,"");
+ rewind (f); /* list all mailboxes matching prefix */
+ if (ddelim < 0) { /* if server failed to give delimiter */
+ puts ("warning: unable to get destination hierarchy delimiter!");
+ ddelim = 0; /* default to none */
+ }
+ if (source) strcpy (strchr (strcpy (tmp,source->mailbox),'}') + 1,
+ strchr (src,'}') + 1);
+ else strcpy (tmp,src);
+ mail_list (source,tmp,"*");
+ rewind (f);
+ /* read back mailbox names */
+ for (retcode = 0; !retcode && (fgets (tmp,MAILTMPLEN-1,f)); ) {
+ if (t = strchr (tmp+1,'\n')) *t = '\0';
+ for (t = mbx,t1 = dest ? dest->mailbox : "",c = NIL; (c != '}') && *t1;
+ *t++ = c= *t1++);
+ for (t1 = dp; *t1; *t++ = *t1++);
+ /* point to name without delim or netspec */
+ t1 = source ? (strchr (tmp+1,'}') + 1) : tmp + 1;
+ /* src and mbx have different delimiters? */
+ if (ddelim && (ddelim != tmp[0]))
+ while (c = *t1++) { /* swap delimiters then */
+ if (c == ddelim) c = tmp[0] ? tmp[0] : 'x';
+ else if (c == tmp[0]) c = ddelim;
+ *t++ = c;
+ }
+ /* easy case */
+ else while (*t1) *t++ = *t1++;
+ *t++ = '\0';
+ if (verbosep) {
+ printf ("Copying %s\n => %s\n",tmp+1,mbx);
+ fflush (stdout);
+ }
+ if (source = mail_open (source,tmp+1,(debugp ? OP_DEBUG : NIL) |
+ (rwcopyp ? NIL : OP_READONLY))) {
+ if (!mbxcopy (source,dest,mbx,T,NIL,merge)) retcode = 1;
+ if (source->dtb->flags & DR_LOCAL) source = mail_close (source);
+ }
+ else printf ("can't open source mailbox %s\n",tmp+1);
+ }
+ }
+ }
+
+ else {
+ printf ("%s version %s.%s\n\n",pgm,CCLIENTVERSION,version);
+ printf (usage2,pgm,"command [switches] arguments",stdsw);
+ printf ("\nCommands:\n %s\n",usgchk);
+ puts (" ;; report number of messages and new messages");
+ printf (" %s\n",usgcre);
+ puts (" ;; create new mailbox");
+ printf (" %s\n",usgdel);
+ puts (" ;; delete existing mailbox");
+ printf (" %s\n",usgren);
+ puts (" ;; rename mailbox to a new name");
+ printf (" copy %s\n",usgcpymov);
+ printf (" move %s\n",usgcpymov);
+ puts (" ;; create new mailbox and copy/move messages");
+ printf (" append %s\n",usgappdel);
+ printf (" appenddelete %s\n",usgappdel);
+ puts (" ;; copy/move messages to existing mailbox");
+ printf (" %s\n",usgprn);
+ puts (" ;; prune mailbox of messages matching criteria");
+ printf (" %s\n",usgxfr);
+ puts (" ;; copy source hierarchy to destination");
+ puts (" ;; -merge modes are prompt, append, or suffix=xxxx");
+ }
+ /* close streams */
+ if (source) mail_close (source);
+ if (dest) mail_close (dest);
+ exit (retcode);
+ return retcode; /* stupid compilers */
+}
+
+/* Pruning criteria, somewhat extended from mail_criteria()
+ * Accepts: criteria
+ * Returns: search program if parse successful, else NIL
+ */
+
+SEARCHPGM *prune_criteria (char *criteria)
+{
+ SEARCHPGM *pgm = NIL;
+ char *criterion,*r,tmp[MAILTMPLEN];
+ int f;
+ if (criteria) { /* only if criteria defined */
+ /* make writeable copy of criteria */
+ criteria = cpystr (criteria);
+ /* for each criterion */
+ for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
+ criterion; (criterion = strtok_r (NIL," ",&r))) {
+ f = NIL; /* init then scan the criterion */
+ switch (*ucase (criterion)) {
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (criterion+1,"LL")) f = T;
+ else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
+ break;
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (criterion+1,"CC"))
+ f = mail_criteria_string (&pgm->bcc,&r);
+ else if (!strcmp (criterion+1,"EFORE"))
+ f = mail_criteria_date (&pgm->before,&r);
+ else if (!strcmp (criterion+1,"ODY"))
+ f = mail_criteria_string (&pgm->body,&r);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
+ break;
+ case 'D': /* possible DELETED, DRAFT */
+ if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
+ else if (!strcmp (criterion+1,"RAFT")) f = pgm->draft = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
+ else if (!strcmp (criterion+1,"ROM"))
+ f = mail_criteria_string (&pgm->from,&r);
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (criterion+1,"EYWORD"))
+ f = mail_criteria_string (&pgm->keyword,&r);
+ break;
+ case 'L': /* possible LARGER */
+ if (!strcmp (criterion+1,"ARGER"))
+ f = prune_criteria_number (&pgm->larger,&r);
+
+ case 'N': /* possible NEW */
+ if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
+ break;
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
+ else if (!strcmp (criterion+1,"N"))
+ f = mail_criteria_date (&pgm->on,&r);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SENT*, SINCE, SMALLER,
+ SUBJECT */
+ if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
+ else if (!strncmp (criterion+1,"ENT",3)) {
+ if (!strcmp (criterion+4,"BEFORE"))
+ f = mail_criteria_date (&pgm->sentbefore,&r);
+ else if (!strcmp (criterion+4,"ON"))
+ f = mail_criteria_date (&pgm->senton,&r);
+ else if (!strcmp (criterion+4,"SINCE"))
+ f = mail_criteria_date (&pgm->sentsince,&r);
+ }
+ else if (!strcmp (criterion+1,"INCE"))
+ f = mail_criteria_date (&pgm->since,&r);
+ else if (!strcmp (criterion+1,"MALLER"))
+ f = prune_criteria_number (&pgm->smaller,&r);
+ else if (!strcmp (criterion+1,"UBJECT"))
+ f = mail_criteria_string (&pgm->subject,&r);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (criterion+1,"EXT"))
+ f = mail_criteria_string (&pgm->text,&r);
+ else if (!strcmp (criterion+1,"O"))
+ f = mail_criteria_string (&pgm->to,&r);
+ break;
+ case 'U': /* possible UN* */
+ if (criterion[1] == 'N') {
+ if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
+ else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
+ else if (!strcmp (criterion+2,"DRAFT")) f = pgm->undraft = T;
+ else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
+ else if (!strcmp (criterion+2,"KEYWORD"))
+ f = mail_criteria_string (&pgm->unkeyword,&r);
+ else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
+ }
+ break;
+ default: /* we will barf below */
+ break;
+ }
+
+ if (!f) { /* if can't identify criterion */
+ sprintf (tmp,"Unknown search criterion: %.30s",criterion);
+ MM_LOG (tmp,ERROR);
+ mail_free_searchpgm (&pgm);
+ break;
+ }
+ }
+ /* no longer need copy of criteria */
+ fs_give ((void **) &criteria);
+ }
+ return pgm;
+}
+
+
+/* Parse a number
+ * Accepts: pointer to integer to return
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int prune_criteria_number (unsigned long *number,char **r)
+{
+ char *t;
+ STRINGLIST *s = NIL;
+ /* parse the date and return fn if OK */
+ int ret = (mail_criteria_string (&s,r) &&
+ (*number = strtoul ((char *) s->text.data,&t,10)) && !*t) ?
+ T : NIL;
+ if (s) mail_free_stringlist (&s);
+ return ret;
+}
+
+/* Copy mailbox
+ * Accepts: stream open on source
+ * halfopen stream for destination or NIL
+ * destination mailbox name
+ * non-zero to create destination mailbox
+ * non-zero to delete messages from source after copying
+ * merge mode
+ * Returns: T if success, NIL if error
+ */
+
+int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
+ int mode)
+{
+ char *s,tmp[MAILTMPLEN];
+ APPENDPACKAGE ap;
+ STRING st;
+ char *ndst = NIL;
+ int ret = NIL;
+ trycreate = NIL; /* no TRYCREATE yet */
+ if (create) while (!mail_create (dest,dst) && (mode != mAPPEND)) {
+ switch (mode) {
+ case mPROMPT: /* prompt user for new name */
+ tmp[0] = '\0';
+ while (!tmp[0]) { /* read name */
+ fputs ("alternative name: ",stdout);
+ fflush (stdout);
+ fgets (tmp,MAILTMPLEN-1,stdin);
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ }
+ if (ndst) fs_give ((void **) &ndst);
+ ndst = cpystr (tmp);
+ break;
+ case mSUFFIX: /* try again with new suffix */
+ if (ndst) fs_give ((void **) &ndst);
+ sprintf (ndst = (char *) fs_get (strlen (dst) + strlen (suffix) + 1),
+ "%s%s",dst,suffix);
+ printf ("retry to create %s\n",ndst);
+ mode = mPROMPT; /* switch to prompt mode if name fails */
+ break;
+ case NIL: /* not merging */
+ return NIL;
+ }
+ if (ndst) dst = ndst; /* if alternative name given, use it */
+ }
+
+ if (kwcopyp) {
+ int i;
+ size_t len;
+ char *dummymsg = "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\n\r\ndummy\r\n";
+ for (i = 0,len = 0; i < NUSERFLAGS; ++i)
+ if (source->user_flags[i]) len += strlen (source->user_flags[i]) + 1;
+ if (len) { /* easy if no user flags to copy... */
+ char *t;
+ char *tail = "\\Deleted)";
+ char *flags = (char *) fs_get (1 + len + strlen (tail) + 1);
+ s = flags; *s++ = '(';
+ for (i = 0; i < NUSERFLAGS; ++i) if (t = source->user_flags[i]) {
+ while (*t) *s++ = *t++;
+ *s++ = ' ';
+ }
+ strcpy (s,tail); /* terminate flags list */
+ if ((dst[0] == '#') && ((dst[1] == 'D') || (dst[1] == 'd')) &&
+ ((dst[2] == 'R') || (dst[2] == 'r')) &&
+ ((dst[3] == 'I') || (dst[3] == 'i')) &&
+ ((dst[4] == 'V') || (dst[4] == 'v')) &&
+ ((dst[5] == 'E') || (dst[5] == 'e')) &&
+ ((dst[6] == 'R') || (dst[6] == 'r')) && (dst[7] == '.') &&
+ (t = strchr (dst+8,'/'))) ++t;
+ else t = dst;
+ INIT (&st,mail_string,dummymsg,strlen (dummymsg));
+ if (!(mail_append (dest,dst,&st) &&
+ (dest = mail_open (dest,t,debugp ? OP_DEBUG : NIL)))) {
+ fs_give ((void **) &flags);
+ return NIL;
+ }
+ mail_setflag (dest,"*",flags);
+ mail_expunge (dest);
+ fs_give ((void **) &flags);
+ }
+ }
+
+ if (source->nmsgs) { /* non-empty source */
+ if (verbosep) printf ("%s [%lu message(s)] => %s\n",
+ source->mailbox,source->nmsgs,dst);
+ ap.stream = source; /* prepare append package */
+ ap.msgno = 0;
+ ap.msgmax = source->nmsgs;
+ ap.flags = ap.date = NIL;
+ ap.message = &st;
+ /* make sure we have all messages */
+ sprintf (tmp,"1:%lu",ap.msgmax);
+ mail_fetchfast (source,tmp);
+ if (mail_append_multiple (dest,dst,mm_append,(void *) &ap)) {
+ --ap.msgno; /* make sure user knows it won */
+ if (verbosep) printf ("[Ok %lu messages(s)]\n",ap.msgno);
+ if (del && ap.msgno) { /* delete source messages */
+ sprintf (tmp,"1:%lu",ap.msgno);
+ mail_flag (source,tmp,"\\Deleted",ST_SET);
+ /* flush moved messages */
+ mail_expunge (source);
+ }
+ ret = T;
+ }
+ else if ((mode == mAPPEND) && trycreate)
+ ret = mbxcopy (source,dest,dst,create,del,mPROMPT);
+ else if (verbosep) puts ("[Failed]");
+ }
+ else { /* empty source */
+ if (verbosep) printf ("%s [empty] => %s\n",source->mailbox,dst);
+ ret = T;
+ }
+ if (ndst) fs_give ((void **) &ndst);
+ return ret;
+}
+
+/* Append callback
+ * Accepts: mail stream
+ * append package
+ * pointer to return flags
+ * pointer to return date
+ * pointer to return message stringstruct
+ * Returns: T on success
+ */
+
+long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ char *t,*t1,tmp[MAILTMPLEN];
+ unsigned long u;
+ MESSAGECACHE *elt;
+ APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+ *flags = *date = NIL; /* assume no flags or date */
+ if (ap->flags) fs_give ((void **) &ap->flags);
+ if (ap->date) fs_give ((void **) &ap->date);
+ mail_gc (ap->stream,GC_TEXTS);
+ if (++ap->msgno <= ap->msgmax) {
+ /* initialize flag string */
+ memset (t = tmp,0,MAILTMPLEN);
+ /* output system flags */
+ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) strcat (t," \\Seen");
+ if (elt->deleted) strcat (t," \\Deleted");
+ if (elt->flagged) strcat (t," \\Flagged");
+ if (elt->answered) strcat (t," \\Answered");
+ if (elt->draft) strcat (t," \\Draft");
+ /* any user flags? */
+ if (!ignorep && (u = elt->user_flags)) do
+ if ((t1 = ap->stream->user_flags[find_rightmost_bit (&u)]) &&
+ (MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1))){
+ *t++ = ' '; /* space delimiter */
+ strcpy (t,t1); /* copy the user flag */
+ }
+ while (u); /* until no more user flags */
+ *flags = ap->flags = cpystr (tmp + 1);
+ *date = ap->date = cpystr (mail_date (tmp,elt));
+ *message = ap->message; /* message stringstruct */
+ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
+ }
+ else *message = NIL; /* all done */
+ return LONGT;
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* dummy routine */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* note destination delimiter */
+ if (ddelim < 0) ddelim = delimiter;
+ /* if got a selectable name */
+ else if (!(attributes & LATT_NOSELECT) && *name)
+ fprintf (f,"%c%s\n",delimiter,name);
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* dummy routine */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ if (status->recent || status->unseen)
+ printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen);
+ else fputs ("No new messages,",stdout);
+ printf (" %lu total in %s\n",status->messages,mailbox);
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ if (!errflg && (string[0] == '[') &&
+ ((string[1] == 'T') || (string[1] == 't')) &&
+ ((string[2] == 'R') || (string[2] == 'r')) &&
+ ((string[3] == 'Y') || (string[3] == 'y')) &&
+ ((string[4] == 'C') || (string[4] == 'c')) &&
+ ((string[5] == 'R') || (string[5] == 'r')) &&
+ ((string[6] == 'E') || (string[6] == 'e')) &&
+ ((string[7] == 'A') || (string[7] == 'a')) &&
+ ((string[8] == 'T') || (string[8] == 't')) &&
+ ((string[9] == 'E') || (string[9] == 'e')) &&
+ (string[10] == ']'))
+ trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case BYE:
+ case NIL: /* no error */
+ if (verbosep) fprintf (stderr,"[%s]\n",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ fprintf (stderr,"warning: %s\n",string);
+ break;
+ case ERROR: /* error */
+ default:
+ fprintf (stderr,"%s\n",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ fprintf (stderr,"%s\n",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ char *s,tmp[MAILTMPLEN];
+ sprintf (s = tmp,"{%s/%s",mb->host,mb->service);
+ if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s",
+ strcpy (username,mb->user));
+ if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser);
+ if (*mb->user) strcat (s = tmp,"} password:");
+ else {
+ printf ("%s} username: ",tmp);
+ fgets (username,NETMAXUSER-1,stdin);
+ username[NETMAXUSER-1] = '\0';
+ if (s = strchr (username,'\n')) *s = '\0';
+ s = "password: ";
+ }
+ strcpy (password,getpass (s));
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ fprintf (stderr,"FATAL: %s\n",string);
+}
diff --git a/imap/src/mailutil/makefile.nt b/imap/src/mailutil/makefile.nt
new file mode 100644
index 00000000..1a6eb515
--- /dev/null
+++ b/imap/src/mailutil/makefile.nt
@@ -0,0 +1,50 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS)
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/makefile.ntk b/imap/src/mailutil/makefile.ntk
new file mode 100644
index 00000000..42ae5185
--- /dev/null
+++ b/imap/src/mailutil/makefile.ntk
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/makefile.w2k b/imap/src/mailutil/makefile.w2k
new file mode 100644
index 00000000..7f0c4614
--- /dev/null
+++ b/imap/src/mailutil/makefile.w2k
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mlock/Makefile b/imap/src/mlock/Makefile
new file mode 100644
index 00000000..b7dc44c1
--- /dev/null
+++ b/imap/src/mlock/Makefile
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MLOCK Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 8 February 1999
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = `cat $C/CFLAGS`
+
+all: mlock
+
+mlock: mlock.o
+ $(CC) $(CFLAGS) -o mlock mlock.o
+
+install: mlock
+ chgrp mail mlock
+ chmod 3711 mlock
+ cp -p mlock /etc/mlock
+
+clean:
+ rm -f *.o mlock || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mlock/mlock.c b/imap/src/mlock/mlock.c
new file mode 100644
index 00000000..1dca40ed
--- /dev/null
+++ b/imap/src/mlock/mlock.c
@@ -0,0 +1,175 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standalone Mailbox Lock program
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 February 1999
+ * Last Edited: 3 March 2008
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+#define LOCKPROTECTION 0664
+
+#ifndef MAXHOSTNAMELEN /* Solaris still sucks */
+#define MAXHOSTNAMELEN 256
+#endif
+
+/* Fatal error
+ * Accepts: Message string
+ * exit code
+ * Returns: code
+ */
+
+int die (char *msg,int code)
+{
+ syslog (LOG_NOTICE,"(%u) %s",code,msg);
+ write (1,"?",1); /* indicate "impossible" failure */
+ return code;
+}
+
+
+int main (int argc,char *argv[])
+{
+ int ld,i;
+ int tries = LOCKTIMEOUT * 60 - 1;
+ char *s,*dir,*file,*lock,*hitch,tmp[1024];
+ size_t dlen,len;
+ struct stat sb,fsb;
+ struct group *grp = getgrnam ("mail");
+ /* get syslog */
+ openlog (argv[0],LOG_PID,LOG_MAIL);
+ if (!grp || (grp->gr_gid != getegid ()))
+ return die ("not setgid mail",EX_USAGE);
+ if (argc != 3) return die ("invalid arguments",EX_USAGE);
+ for (s = argv[1]; *s; s++)
+ if (!isdigit (*s)) return die ("invalid fd",EX_USAGE);
+ /* find directory */
+ if ((*argv[2] != '/') || !(file = strrchr (argv[2],'/')) || !file[1])
+ return die ("invalid path",EX_USAGE);
+ /* calculate lengths of directory and file */
+ if (!(dlen = file - argv[2])) dlen = 1;
+ len = strlen (++file);
+ /* make buffers */
+ dir = (char *) malloc (dlen + 1);
+ lock = (char *) malloc (len + 6);
+ hitch = (char *) malloc (len + 6 + 40 + MAXHOSTNAMELEN);
+ if (!dir || !lock || !hitch) return die ("malloc failure",errno);
+ strncpy (dir,argv[2],dlen); /* connect to desired directory */
+ dir[dlen] = '\0';
+ printf ("dir=%s, file=%s\n",dir,file);
+ chdir (dir);
+ /* get device/inode of file descriptor */
+ if (fstat (atoi (argv[1]),&fsb)) return die ("fstat failure",errno);
+ /* better be a regular file */
+ if ((fsb.st_mode & S_IFMT) != S_IFREG)
+ return die ("fd not regular file",EX_USAGE);
+ /* now get device/inode of file */
+ if (lstat (file,&sb)) return die ("lstat failure",errno);
+ /* does it match? */
+ if ((sb.st_mode & S_IFMT) != S_IFREG)
+ return die ("name not regular file",EX_USAGE);
+ if ((sb.st_dev != fsb.st_dev) || (sb.st_ino != fsb.st_ino))
+ return die ("fd and name different",EX_USAGE);
+ /* build lock filename */
+ sprintf (lock,"%s.lock",file);
+ if (!lstat (lock,&sb) && ((sb.st_mode & S_IFMT) != S_IFREG))
+ return die ("existing lock not regular file",EX_NOPERM);
+
+ do { /* until OK or out of tries */
+ if (!stat (lock,&sb) && (time (0) > (sb.st_ctime + LOCKTIMEOUT * 60)))
+ unlink (lock); /* time out lock if enough time has passed */
+ /* SUN-OS had an NFS
+ * As kludgy as an albatross;
+ * And everywhere that it was installed,
+ * It was a total loss.
+ * -- MRC 9/25/91
+ */
+ /* build hitching post file name */
+ sprintf (hitch,"%s.%lu.%lu.",lock,(unsigned long) time (0),
+ (unsigned long) getpid ());
+ len = strlen (hitch); /* append local host name */
+ gethostname (hitch + len,MAXHOSTNAMELEN);
+ /* try to get hitching-post file */
+ if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
+ /* make sure others can break the lock */
+ chmod (hitch,LOCKPROTECTION);
+ /* get device/inode of hitch file */
+ if (fstat (ld,&fsb)) return die ("hitch fstat failure",errno);
+ close (ld); /* close the hitching-post */
+ /* Note: link() may return an error even if it actually succeeded. So we
+ * always check for success via the link count, and ignore the error if
+ * the link count is right.
+ */
+ /* tie hitching-post to lock */
+ i = link (hitch,lock) ? errno : 0;
+ /* success if link count now 2 */
+ if (stat (hitch,&sb) || (sb.st_nlink != 2) ||
+ (fsb.st_dev != sb.st_dev) || (fsb.st_ino != sb.st_ino)) {
+ ld = -1; /* failed to hitch */
+ if (i == EPERM) { /* was it because links not allowed? */
+ /* Probably a FAT filesystem on Linux. It can't be NFS, so try
+ * creating the lock file directly.
+ */
+ if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
+ /* get device/inode of lock file */
+ if (fstat (ld,&fsb)) return die ("lock fstat failure",errno);
+ close (ld); /* close the file */
+ }
+ /* give up immediately if protection failure */
+ else if (errno != EEXIST) tries = 0;
+ }
+ }
+ unlink (hitch); /* flush hitching post */
+ }
+ /* give up immediately if protection failure */
+ else if (errno == EACCES) tries = 0;
+ if (ld < 0) { /* lock failed */
+ if (tries--) sleep (1); /* sleep 1 second and try again */
+ else {
+ write (1,"-",1); /* hard failure */
+ return EX_CANTCREAT;
+ }
+ }
+ } while (ld < 0);
+ write (1,"+",1); /* indicate that all is well */
+ read (0,tmp,1); /* read continue signal from parent */
+ /* flush the lock file */
+ if (!stat (lock,&sb) && (fsb.st_dev == sb.st_dev) &&
+ (fsb.st_ino == sb.st_ino)) unlink (lock);
+ else syslog (LOG_NOTICE,"lock file %s/%s changed dev/inode",dir,lock);
+ return EX_OK;
+}
diff --git a/imap/src/mtest/Makefile b/imap/src/mtest/Makefile
new file mode 100644
index 00000000..a49be581
--- /dev/null
+++ b/imap/src/mtest/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+all: mtest
+
+mtest: $(CCLIENTLIB) mtest.o
+ $(CC) $(CFLAGS) -o mtest mtest.o $(LDFLAGS)
+
+mtest.o: $C/mail.h $C/misc.h $C/osdep.h $C/rfc822.h $C/smtp.h $C/nntp.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o mtest || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mtest/makefile.nt b/imap/src/mtest/makefile.nt
new file mode 100644
index 00000000..9486e42b
--- /dev/null
+++ b/imap/src/mtest/makefile.nt
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.ntk b/imap/src/mtest/makefile.ntk
new file mode 100644
index 00000000..91508162
--- /dev/null
+++ b/imap/src/mtest/makefile.ntk
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.os2 b/imap/src/mtest/makefile.os2
new file mode 100644
index 00000000..1bf27046
--- /dev/null
+++ b/imap/src/mtest/makefile.os2
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+# Thanks to Nicholas Paul Sheppard who contributed the original version
+
+CC = gcc
+CFLAGS = -O2 -Zomf
+LD = gcc
+LDFLAGS = -s -Zomf -Zcrtdll
+
+C = ..\c-client
+CCLIENTLIB = $C\\c-client.lib
+LIBS = $(CCLIENTLIB) -l socket
+
+mtest.exe: $(CCLIENTLIB) mtest.obj
+ $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+mtest.obj: mtest.c $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h
+ $(CC) $(CFLAGS) -I$C -o $@ -c $<
+
+clean:
+ if exist *.obj del *.obj
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.w2k b/imap/src/mtest/makefile.w2k
new file mode 100644
index 00000000..c5a1568b
--- /dev/null
+++ b/imap/src/mtest/makefile.w2k
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/mtest.c b/imap/src/mtest/mtest.c
new file mode 100644
index 00000000..69af568b
--- /dev/null
+++ b/imap/src/mtest/mtest.c
@@ -0,0 +1,813 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail library test program
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 July 1988
+ * Last Edited: 5 November 2007
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include "c-client.h"
+#include "imap4r1.h"
+
+/* Excellent reasons to hate ifdefs, and why my real code never uses them */
+
+#ifndef unix
+# define unix 0
+#endif
+
+#if unix
+# define UNIXLIKE 1
+# define MACOS 0
+# include <pwd.h>
+#else
+# define UNIXLIKE 0
+# ifdef noErr
+# define MACOS 1
+# include <Memory.h>
+# else
+# define MACOS 0
+# endif
+#endif
+
+char *curhst = NIL; /* currently connected host */
+char *curusr = NIL; /* current login user */
+char personalname[MAILTMPLEN]; /* user's personal name */
+
+static char *hostlist[] = { /* SMTP server host list */
+ "mailhost",
+ "localhost",
+ NIL
+};
+
+static char *newslist[] = { /* Netnews server host list */
+ "news",
+ NIL
+};
+
+int main (void);
+void mm (MAILSTREAM *stream,long debug);
+void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+void header (MAILSTREAM *stream,long msgno);
+void display_body (BODY *body,char *pfx,long i);
+void status (MAILSTREAM *stream);
+void prompt (char *msg,char *txt);
+void smtptest (long debug);
+
+/* Main program - initialization */
+
+int main ()
+{
+ MAILSTREAM *stream = NIL;
+ void *sdb = NIL;
+ char *s,tmp[MAILTMPLEN];
+ long debug;
+#include "linkage.c"
+#if MACOS
+ {
+ size_t *base = (size_t *) 0x000908;
+ /* increase stack size on a Mac */
+ SetApplLimit ((Ptr) (*base - (size_t) 65535L));
+ }
+#endif
+ curusr = cpystr (((s = myusername ()) && *s) ? s : "somebody");
+#if UNIXLIKE
+ {
+ char *suffix;
+ struct passwd *pwd = getpwnam (curusr);
+ if (pwd) {
+ strcpy (tmp,pwd->pw_gecos);
+ /* dyke out the office and phone poop */
+ if (suffix = strchr (tmp,',')) suffix[0] = '\0';
+ strcpy (personalname,tmp);/* make a permanent copy of it */
+ }
+ else personalname[0] = '\0';
+ }
+#else
+ personalname[0] = '\0';
+#endif
+ curhst = cpystr (mylocalhost ());
+ puts ("MTest -- C client test program");
+ if (!*personalname) prompt ("Personal name: ",personalname);
+ /* user wants protocol telemetry? */
+ prompt ("Debug protocol (y/n)?",tmp);
+ ucase (tmp);
+ debug = (tmp[0] == 'Y') ? T : NIL;
+ do {
+ prompt ("Mailbox ('?' for help): ",tmp);
+ if (!strcmp (tmp,"?")) {
+ puts ("Enter INBOX, mailbox name, or IMAP mailbox as {host}mailbox");
+ puts ("Known local mailboxes:");
+ mail_list (NIL,NIL,"%");
+ if (s = sm_read (&sdb)) {
+ puts ("Local subscribed mailboxes:");
+ do (mm_lsub (NIL,NIL,s,NIL));
+ while (s = sm_read (&sdb));
+ }
+ puts ("or just hit return to quit");
+ }
+ else if (tmp[0]) stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);
+ } while (!stream && tmp[0]);
+ mm (stream,debug); /* run user interface if opened */
+#if MACOS
+ /* clean up resolver */
+ if (resolveropen) CloseResolver ();
+#endif
+ return NIL;
+}
+
+/* MM command loop
+ * Accepts: MAIL stream
+ */
+
+void mm (MAILSTREAM *stream,long debug)
+{
+ void *sdb = NIL;
+ char cmd[MAILTMPLEN];
+ char *s,*arg;
+ unsigned long i;
+ unsigned long last = 0;
+ BODY *body;
+ status (stream); /* first report message status */
+ while (stream) {
+ prompt ("MTest>",cmd); /* prompt user, get command */
+ /* get argument */
+ if (arg = strchr (cmd,' ')) *arg++ = '\0';
+ switch (*ucase (cmd)) { /* dispatch based on command */
+ case 'B': /* Body command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) {
+ mail_fetchstructure (stream,last,&body);
+ if (body) display_body (body,NIL,(long) 0);
+ else puts ("%No body information available");
+ }
+ else puts ("?Bad message number");
+ break;
+ case 'C': /* Check command */
+ mail_check (stream);
+ status (stream);
+ break;
+ case 'D': /* Delete command */
+ if (arg) last = atoi (arg);
+ else {
+ if (last == 0) {
+ puts ("?Missing message number");
+ break;
+ }
+ arg = cmd;
+ sprintf (arg,"%lu",last);
+ }
+ if (last && (last <= stream->nmsgs))
+ mail_setflag (stream,arg,"\\DELETED");
+ else puts ("?Bad message number");
+ break;
+ case 'E': /* Expunge command */
+ mail_expunge (stream);
+ last = 0;
+ break;
+ case 'F': /* Find command */
+ if (!arg) {
+ arg = "%";
+ if (s = sm_read (&sdb)) {
+ puts ("Local network subscribed mailboxes:");
+ do if (*s == '{') (mm_lsub (NIL,NIL,s,NIL));
+ while (s = sm_read (&sdb));
+ }
+ }
+ puts ("Subscribed mailboxes:");
+ mail_lsub (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL,
+ NIL,arg);
+ puts ("Known mailboxes:");
+ mail_list (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL,
+ NIL,arg);
+ break;
+ case 'G':
+ mail_gc (stream,GC_ENV|GC_TEXTS|GC_ELT);
+ break;
+ case 'H': /* Headers command */
+ if (arg) {
+ if (!(last = atoi (arg))) {
+ mail_search (stream,arg);
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) header (stream,i);
+ break;
+ }
+ }
+ else if (last == 0) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) header (stream,last);
+ else puts ("?Bad message number");
+ break;
+ case 'L': /* Literal command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs))
+ puts (mail_fetch_message (stream,last,NIL,NIL));
+ else puts ("?Bad message number");
+ break;
+ case 'M':
+ mail_status (NIL,arg ? arg : stream->mailbox,
+ SA_MESSAGES|SA_RECENT|SA_UNSEEN|SA_UIDNEXT|SA_UIDVALIDITY);
+ break;
+ case 'N': /* New mailbox command */
+ if (!arg) {
+ puts ("?Missing mailbox");
+ break;
+ }
+ /* get the new mailbox */
+ while (!(stream = mail_open (stream,arg,debug))) {
+ prompt ("Mailbox: ",arg);
+ if (!arg[0]) break;
+ }
+ last = 0;
+ status (stream);
+ break;
+ case 'O': /* Overview command */
+ if (!arg) {
+ puts ("?Missing UID");
+ break;
+ }
+ mail_fetch_overview (stream,arg,overview_header);
+ break;
+ case 'P': /* Ping command */
+ mail_ping (stream);
+ status (stream);
+ break;
+ case 'Q': /* Quit command */
+ mail_close (stream);
+ stream = NIL;
+ break;
+ case 'S': /* Send command */
+ smtptest (debug);
+ break;
+ case '\0': /* null command (type next message) */
+ if (!last || (last++ >= stream->nmsgs)) {
+ puts ("%No next message");
+ break;
+ }
+ case 'T': /* Type command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) {
+ STRINGLIST *lines = mail_newstringlist ();
+ STRINGLIST *cur = lines;
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Date")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("From")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr (">From")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Subject")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("To")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("cc")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Newsgroups")));
+ printf ("%s",mail_fetchheader_full (stream,last,lines,NIL,NIL));
+ puts (mail_fetchtext (stream,last));
+ mail_free_stringlist (&lines);
+ }
+ else puts ("?Bad message number");
+ break;
+ case 'U': /* Undelete command */
+ if (arg) last = atoi (arg);
+ else {
+ if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ arg = cmd;
+ sprintf (arg,"%lu",last);
+ }
+ if (last > 0 && last <= stream->nmsgs)
+ mail_clearflag (stream,arg,"\\DELETED");
+ else puts ("?Bad message number");
+ break;
+ case 'X': /* Xit command */
+ mail_expunge (stream);
+ mail_close (stream);
+ stream = NIL;
+ break;
+ case '+':
+ mail_debug (stream); debug = T;
+ break;
+ case '-':
+ mail_nodebug (stream); debug = NIL;
+ break;
+ case '?': /* ? command */
+ puts ("Body, Check, Delete, Expunge, Find, GC, Headers, Literal,");
+ puts (" MailboxStatus, New Mailbox, Overview, Ping, Quit, Send, Type,");
+ puts ("Undelete, Xit, +, -, or <RETURN> for next message");
+ break;
+ default: /* bogus command */
+ printf ("?Unrecognized command: %s\n",cmd);
+ break;
+ }
+ }
+}
+
+/* MM display header
+ * Accepts: IMAP2 stream
+ * message number
+ */
+
+void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno)
+{
+ if (ov) {
+ unsigned long i;
+ char *t,tmp[MAILTMPLEN];
+ ADDRESS *adr;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ MESSAGECACHE selt;
+ tmp[0] = elt->recent ? (elt->seen ? 'R': 'N') : ' ';
+ tmp[1] = (elt->recent | elt->seen) ? ' ' : 'U';
+ tmp[2] = elt->flagged ? 'F' : ' ';
+ tmp[3] = elt->answered ? 'A' : ' ';
+ tmp[4] = elt->deleted ? 'D' : ' ';
+ mail_parse_date (&selt,ov->date);
+ sprintf (tmp+5,"%4lu) ",elt->msgno);
+ mail_date (tmp+11,&selt);
+ tmp[17] = ' ';
+ tmp[18] = '\0';
+ memset (tmp+18,' ',(size_t) 20);
+ tmp[38] = '\0'; /* tie off with null */
+ /* get first from address from envelope */
+ for (adr = ov->from; adr && !adr->host; adr = adr->next);
+ if (adr) { /* if a personal name exists use it */
+ if (!(t = adr->personal))
+ sprintf (t = tmp+400,"%s@%s",adr->mailbox,adr->host);
+ memcpy (tmp+18,t,(size_t) min (20,(long) strlen (t)));
+ }
+ strcat (tmp," ");
+ if (i = elt->user_flags) {
+ strcat (tmp,"{");
+ while (i) {
+ strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
+ if (i) strcat (tmp," ");
+ }
+ strcat (tmp,"} ");
+ }
+ sprintf (tmp + strlen (tmp),"%.25s (%lu chars)",
+ ov->subject ? ov->subject : " ",ov->optional.octets);
+ puts (tmp);
+ }
+ else printf ("%%No overview for UID %lu\n",uid);
+}
+
+/* MM display header
+ * Accepts: IMAP2 stream
+ * message number
+ */
+
+void header (MAILSTREAM *stream,long msgno)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ char *t;
+ MESSAGECACHE *cache = mail_elt (stream,msgno);
+ mail_fetchstructure (stream,msgno,NIL);
+ tmp[0] = cache->recent ? (cache->seen ? 'R': 'N') : ' ';
+ tmp[1] = (cache->recent | cache->seen) ? ' ' : 'U';
+ tmp[2] = cache->flagged ? 'F' : ' ';
+ tmp[3] = cache->answered ? 'A' : ' ';
+ tmp[4] = cache->deleted ? 'D' : ' ';
+ sprintf (tmp+5,"%4lu) ",cache->msgno);
+ mail_date (tmp+11,cache);
+ tmp[17] = ' ';
+ tmp[18] = '\0';
+ mail_fetchfrom (tmp+18,stream,msgno,(long) 20);
+ strcat (tmp," ");
+ if (i = cache->user_flags) {
+ strcat (tmp,"{");
+ while (i) {
+ strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
+ if (i) strcat (tmp," ");
+ }
+ strcat (tmp,"} ");
+ }
+ mail_fetchsubject (t = tmp + strlen (tmp),stream,msgno,(long) 25);
+ sprintf (t += strlen (t)," (%lu chars)",cache->rfc822_size);
+ puts (tmp);
+}
+
+/* MM display body
+ * Accepts: BODY structure pointer
+ * prefix string
+ * index
+ */
+
+void display_body (BODY *body,char *pfx,long i)
+{
+ char tmp[MAILTMPLEN];
+ char *s = tmp;
+ PARAMETER *par;
+ PART *part; /* multipart doesn't have a row to itself */
+ if (body->type == TYPEMULTIPART) {
+ /* if not first time, extend prefix */
+ if (pfx) sprintf (tmp,"%s%ld.",pfx,++i);
+ else tmp[0] = '\0';
+ for (i = 0,part = body->nested.part; part; part = part->next)
+ display_body (&part->body,tmp,i++);
+ }
+ else { /* non-multipart, output oneline descriptor */
+ if (!pfx) pfx = ""; /* dummy prefix if top level */
+ sprintf (s," %s%ld %s",pfx,++i,body_types[body->type]);
+ if (body->subtype) sprintf (s += strlen (s),"/%s",body->subtype);
+ if (body->description) sprintf (s += strlen (s)," (%s)",body->description);
+ if (par = body->parameter) do
+ sprintf (s += strlen (s),";%s=%s",par->attribute,par->value);
+ while (par = par->next);
+ if (body->id) sprintf (s += strlen (s),", id = %s",body->id);
+ switch (body->type) { /* bytes or lines depending upon body type */
+ case TYPEMESSAGE: /* encapsulated message */
+ case TYPETEXT: /* plain text */
+ sprintf (s += strlen (s)," (%lu lines)",body->size.lines);
+ break;
+ default:
+ sprintf (s += strlen (s)," (%lu bytes)",body->size.bytes);
+ break;
+ }
+ puts (tmp); /* output this line */
+ /* encapsulated message? */
+ if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") &&
+ (body = body->nested.msg->body)) {
+ if (body->type == TYPEMULTIPART) display_body (body,pfx,i-1);
+ else { /* build encapsulation prefix */
+ sprintf (tmp,"%s%ld.",pfx,i);
+ display_body (body,tmp,(long) 0);
+ }
+ }
+ }
+}
+
+/* MM status report
+ * Accepts: MAIL stream
+ */
+
+void status (MAILSTREAM *stream)
+{
+ unsigned long i;
+ char *s,date[MAILTMPLEN];
+ THREADER *thr;
+ AUTHENTICATOR *auth;
+ rfc822_date (date);
+ puts (date);
+ if (stream) {
+ if (stream->mailbox)
+ printf (" %s mailbox: %s, %lu messages, %lu recent\n",
+ stream->dtb->name,stream->mailbox,stream->nmsgs,stream->recent);
+ else puts ("%No mailbox is open on this stream");
+ if (stream->user_flags[0]) {
+ printf ("Keywords: %s",stream->user_flags[0]);
+ for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ printf (", %s",stream->user_flags[i]);
+ puts ("");
+ }
+ if (!strcmp (stream->dtb->name,"imap")) {
+ if (LEVELIMAP4rev1 (stream)) s = "IMAP4rev1 (RFC 3501)";
+ else if (LEVEL1730 (stream)) s = "IMAP4 (RFC 1730)";
+ else if (LEVELIMAP2bis (stream)) s = "IMAP2bis";
+ else if (LEVEL1176 (stream)) s = "IMAP2 (RFC 1176)";
+ else s = "IMAP2 (RFC 1064)";
+ printf ("%s server %s\n",s,imap_host (stream));
+ if (LEVELIMAP4 (stream)) {
+ if (i = imap_cap (stream)->auth) {
+ s = "";
+ printf ("Mutually-supported SASL mechanisms:");
+ while (auth = mail_lookup_auth (find_rightmost_bit (&i) + 1)) {
+ printf (" %s",auth->name);
+ if (!strcmp (auth->name,"PLAIN"))
+ s = "\n [LOGIN will not be listed here if PLAIN is supported]";
+ }
+ puts (s);
+ }
+ printf ("Supported standard extensions:\n");
+ if (LEVELACL (stream)) puts (" Access Control lists (RFC 2086)");
+ if (LEVELQUOTA (stream)) puts (" Quotas (RFC 2087)");
+ if (LEVELLITERALPLUS (stream))
+ puts (" Non-synchronizing literals (RFC 2088)");
+ if (LEVELIDLE (stream)) puts (" IDLE unsolicited update (RFC 2177)");
+ if (LEVELMBX_REF (stream)) puts (" Mailbox referrals (RFC 2193)");
+ if (LEVELLOG_REF (stream)) puts (" Login referrals (RFC 2221)");
+ if (LEVELANONYMOUS (stream)) puts (" Anonymous access (RFC 2245)");
+ if (LEVELNAMESPACE (stream)) puts (" Multiple namespaces (RFC 2342)");
+ if (LEVELUIDPLUS (stream)) puts (" Extended UID behavior (RFC 2359)");
+ if (LEVELSTARTTLS (stream))
+ puts (" Transport Layer Security (RFC 2595)");
+ if (LEVELLOGINDISABLED (stream))
+ puts (" LOGIN command disabled (RFC 2595)");
+ if (LEVELID (stream))
+ puts (" Implementation identity negotiation (RFC 2971)");
+ if (LEVELCHILDREN (stream))
+ puts (" LIST children announcement (RFC 3348)");
+ if (LEVELMULTIAPPEND (stream))
+ puts (" Atomic multiple APPEND (RFC 3502)");
+ if (LEVELBINARY (stream))
+ puts (" Binary body content (RFC 3516)");
+ if (LEVELUNSELECT (stream)) puts (" Mailbox unselect (RFC 3691)");
+ if (LEVELURLAUTH (stream))
+ puts (" URL authenticated fetch (RFC 4467)");
+ if (LEVELCATENATE (stream)) puts (" Catenation (RFC 4469)");
+ if (LEVELCONDSTORE (stream)) puts (" Conditional STORE (RFC 4551)");
+ if (LEVELESEARCH (stream)) puts (" Extended SEARCH (RFC 4731)");
+ puts ("Supported draft extensions:");
+ if (LEVELSASLIR (stream)) puts (" SASL initial client response");
+ if (LEVELSORT (stream)) puts (" Server-based sorting");
+ if (LEVELTHREAD (stream)) {
+ printf (" Server-based threading:");
+ for (thr = imap_cap (stream)->threader; thr; thr = thr->next)
+ printf (" %s",thr->name);
+ putchar ('\n');
+ }
+ if (LEVELSCAN (stream)) puts (" Mailbox text scan");
+ if (i = imap_cap (stream)->extlevel) {
+ printf ("Supported BODYSTRUCTURE extensions:");
+ switch (i) {
+ case BODYEXTLOC: printf (" location");
+ case BODYEXTLANG: printf (" language");
+ case BODYEXTDSP: printf (" disposition");
+ case BODYEXTMD5: printf (" MD5\n");
+ }
+ }
+ }
+ else putchar ('\n');
+ }
+ }
+}
+
+
+/* Prompt user for input
+ * Accepts: pointer to prompt message
+ * pointer to input buffer
+ */
+
+void prompt (char *msg,char *txt)
+{
+ printf ("%s",msg);
+ gets (txt);
+}
+
+/* Interfaces to C-client */
+
+
+void mm_searched (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg);
+}
+
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+{
+ putchar (' ');
+ if (delimiter) putchar (delimiter);
+ else fputs ("NIL",stdout);
+ putchar (' ');
+ fputs (mailbox,stdout);
+ if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout);
+ if (attributes & LATT_NOSELECT) fputs (", no select",stdout);
+ if (attributes & LATT_MARKED) fputs (", marked",stdout);
+ if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout);
+ putchar ('\n');
+}
+
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+{
+ putchar (' ');
+ if (delimiter) putchar (delimiter);
+ else fputs ("NIL",stdout);
+ putchar (' ');
+ fputs (mailbox,stdout);
+ if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout);
+ if (attributes & LATT_NOSELECT) fputs (", no select",stdout);
+ if (attributes & LATT_MARKED) fputs (", marked",stdout);
+ if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout);
+ putchar ('\n');
+}
+
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ printf (" Mailbox %s",mailbox);
+ if (status->flags & SA_MESSAGES) printf (", %lu messages",status->messages);
+ if (status->flags & SA_RECENT) printf (", %lu recent",status->recent);
+ if (status->flags & SA_UNSEEN) printf (", %lu unseen",status->unseen);
+ if (status->flags & SA_UIDVALIDITY) printf (", %lu UID validity",
+ status->uidvalidity);
+ if (status->flags & SA_UIDNEXT) printf (", %lu next UID",status->uidnext);
+ printf ("\n");
+}
+
+
+void mm_log (char *string,long errflg)
+{
+ switch ((short) errflg) {
+ case NIL:
+ printf ("[%s]\n",string);
+ break;
+ case PARSE:
+ case WARN:
+ printf ("%%%s\n",string);
+ break;
+ case ERROR:
+ printf ("?%s\n",string);
+ break;
+ }
+}
+
+
+void mm_dlog (char *string)
+{
+ puts (string);
+}
+
+
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+{
+ char *s,tmp[MAILTMPLEN];
+ if (curhst) fs_give ((void **) &curhst);
+ curhst = (char *) fs_get (1+strlen (mb->host));
+ strcpy (curhst,mb->host);
+ sprintf (s = tmp,"{%s/%s",mb->host,mb->service);
+ if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s",strcpy (user,mb->user));
+ if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser);
+ if (*mb->user) strcat (s = tmp,"} password:");
+ else {
+ printf ("%s} username: ",tmp);
+ fgets (user,NETMAXUSER-1,stdin);
+ user[NETMAXUSER-1] = '\0';
+ if (s = strchr (user,'\n')) *s = '\0';
+ s = "password: ";
+ }
+ if (curusr) fs_give ((void **) &curusr);
+ curusr = cpystr (user);
+ strcpy (pwd,getpass (s));
+}
+
+
+void mm_critical (MAILSTREAM *stream)
+{
+}
+
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+}
+
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+#if UNIXLIKE
+ kill (getpid (),SIGSTOP);
+#else
+ abort ();
+#endif
+ return NIL;
+}
+
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string);
+}
+
+/* SMTP tester */
+
+void smtptest (long debug)
+{
+ SENDSTREAM *stream = NIL;
+ char line[MAILTMPLEN];
+ char *text = (char *) fs_get (8*MAILTMPLEN);
+ ENVELOPE *msg = mail_newenvelope ();
+ BODY *body = mail_newbody ();
+ msg->from = mail_newaddr ();
+ msg->from->personal = cpystr (personalname);
+ msg->from->mailbox = cpystr (curusr);
+ msg->from->host = cpystr (curhst);
+ msg->return_path = mail_newaddr ();
+ msg->return_path->mailbox = cpystr (curusr);
+ msg->return_path->host = cpystr (curhst);
+ prompt ("To: ",line);
+ rfc822_parse_adrlist (&msg->to,line,curhst);
+ if (msg->to) {
+ prompt ("cc: ",line);
+ rfc822_parse_adrlist (&msg->cc,line,curhst);
+ }
+ else {
+ prompt ("Newsgroups: ",line);
+ if (*line) msg->newsgroups = cpystr (line);
+ else {
+ mail_free_body (&body);
+ mail_free_envelope (&msg);
+ fs_give ((void **) &text);
+ return;
+ }
+ }
+ prompt ("Subject: ",line);
+ msg->subject = cpystr (line);
+ puts (" Msg (end with a line with only a '.'):");
+ body->type = TYPETEXT;
+ *text = '\0';
+ while (gets (line)) {
+ if (line[0] == '.') {
+ if (line[1] == '\0') break;
+ else strcat (text,".");
+ }
+ strcat (text,line);
+ strcat (text,"\015\012");
+ }
+ body->contents.text.data = (unsigned char *) text;
+ body->contents.text.size = strlen (text);
+ rfc822_date (line);
+ msg->date = (char *) fs_get (1+strlen (line));
+ strcpy (msg->date,line);
+ if (msg->to) {
+ puts ("Sending...");
+ if (stream = smtp_open (hostlist,debug)) {
+ if (smtp_mail (stream,"MAIL",msg,body)) puts ("[Ok]");
+ else printf ("[Failed - %s]\n",stream->reply);
+ }
+ }
+ else {
+ puts ("Posting...");
+ if (stream = nntp_open (newslist,debug)) {
+ if (nntp_mail (stream,msg,body)) puts ("[Ok]");
+ else printf ("[Failed - %s]\n",stream->reply);
+ }
+ }
+ if (stream) smtp_close (stream);
+ else puts ("[Can't open connection to any server]");
+ mail_free_envelope (&msg);
+ mail_free_body (&body);
+}
diff --git a/imap/src/osdep/amiga/Makefile b/imap/src/osdep/amiga/Makefile
new file mode 100644
index 00000000..1f08e97e
--- /dev/null
+++ b/imap/src/osdep/amiga/Makefile
@@ -0,0 +1,231 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: C client makefile for Amiga
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 5 November 2006
+
+
+# Command line build parameters
+
+EXTRAAUTHENTICATORS=
+EXTRADRIVERS=mbox
+PASSWDTYPE=std
+
+
+# Build parameters normally set by the individual port
+
+AMICFLAGS=-O -DNO_INLINE_STDARG -Dunix
+AMILDFLAGS=/pine/libc.a -lamiga -lauto
+CHECKPW=std
+LOGINPW=std
+ACTIVEFILE=/UULib/News/Active
+SPOOLDIR=/usr/spool
+MAILSPOOL=/AmiTCP/Mail
+NEWSSPOOL=/UUNews
+MD5PWD="/etc/cram-md5.pwd"
+
+
+# Default formats for creating new mailboxes and for empty mailboxes in the
+# default namespace; must be set to the associated driver's prototype.
+#
+# The CREATEPROTO is the default format for new mailbox creation.
+# The EMPTYPROTO is the default format for handling zero-byte files.
+#
+# Normally, this is set by the individual port.
+#
+# NOTE: namespace formats (e.g. mh and news) can not be set as a default format
+# since they do not exist in the default namespace. Also, it is meaningless to
+# set certain other formats (e.g. mbx, mx, and mix) as the EMPTYPROTO since
+# these formats can never be empty files.
+
+CREATEPROTO=unixproto
+EMPTYPROTO=unixproto
+
+
+# Commands possibly overriden by the individual port
+
+ARRC=ar rc
+CC=cc
+LN=cp
+RANLIB=ranlib
+RM=rm -f
+
+
+# Standard distribution build parameters
+
+DEFAULTAUTHENTICATORS=ext md5 pla log
+DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile
+CHUNKSIZE=65536
+
+
+# Normally no need to change any of these
+
+ARCHIVE=c-client.a
+BINARIES=mail.o misc.o newsrc.o smanager.o osdep.o utf8.o utf8aux.o \
+ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \
+ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \
+ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o
+CFLAGS=$(BASECFLAGS) $(EXTRACFLAGS)
+MAKE=make
+MV=mv
+SHELL=/bin/sh
+
+
+# Primary build command
+
+BUILDOPTIONS= EXTRACFLAGS=$(EXTRACFLAGS) EXTRALDFLAGS=$(EXTRALDFLAGS)\
+ EXTRADRIVERS=$(EXTRADRIVERS) EXTRAAUTHENTICATORS=$(EXTRAAUTHENTICATORS)\
+ PASSWDTYPE=$(PASSWDTYPE)
+BUILD=$(MAKE) build $(BUILDOPTIONS) $(SPECIALS)
+
+
+# Here if no make argument established
+
+missing: osdep.h
+ $(MAKE) $(ARCHIVE) CC=`cat CCTYPE` CFLAGS="`cat CFLAGS`"
+
+osdep.h:
+ @echo You must specify what type of system
+ @false
+
+
+# Current ports
+
+ami: # AmigaDOS
+ $(BUILD) OS=$@ \
+ BASECFLAGS="-DOLD $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -lamitcp000" \
+ CC=gcc
+
+am2: # AmigaDOS with a 68020+
+ $(BUILD) OS=ami \
+ BASECFLAGS="-DOLD -m68020 $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -lamitcp" \
+ CC=gcc
+
+amn: # AmigaDOS with a 680x0 using "new" socket library
+ $(BUILD) OS=ami \
+ BASELDFLAGS="$(AMILDFLAGS) -lnewamitcp000" \
+ CC=gcc
+
+ama: # AmigaDOS using AS225R2
+ $(BUILD) OS=ami \
+ MAILSPOOL=/INet/Mail \
+ BASECFLAGS="-m68020 $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -las225r2" \
+ CC=gcc
+
+# Build it!
+
+build: clean once ckp$(PASSWDTYPE) $(EXTRAAUTHENTICATORS) $(ARCHIVE)
+
+$(ARCHIVE): $(BINARIES)
+ $(RM) $(ARCHIVE) || true
+ $(ARRC) $(ARCHIVE) $(BINARIES)
+ $(RANLIB) $(ARCHIVE)
+
+# Cleanup
+
+clean:
+ $(RM) *.o linkage.[ch] auths.c $(ARCHIVE) osdep.* *TYPE *FLAGS || true
+
+
+# Dependencies
+
+dummy.o: mail.h misc.h osdep.h dummy.h
+fdstring.o: mail.h misc.h osdep.h fdstring.h
+flstring.o: mail.h misc.h osdep.h flstring.h
+imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h
+mail.o: mail.h misc.h osdep.h rfc822.h linkage.h
+mbx.o: mail.h misc.h osdep.h dummy.h
+mh.o: mail.h misc.h osdep.h mh.h dummy.h
+mix.o: mail.h misc.h osdep.h dummy.h
+mx.o: mail.h misc.h osdep.h mx.h dummy.h
+misc.o: mail.h misc.h osdep.h
+mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h
+mtx.o: mail.h misc.h osdep.h dummy.h
+netmsg.o: mail.h misc.h osdep.h netmsg.h
+news.o: mail.h misc.h osdep.h
+newsrc.o: mail.h misc.h osdep.h newsrc.h
+nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h
+phile.o: mail.h misc.h osdep.h rfc822.h dummy.h
+pseudo.o: pseudo.h
+pop3.o: mail.h misc.h osdep.h pop3.h rfc822.h
+smanager.o: mail.h misc.h osdep.h
+smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h
+rfc822.o: mail.h misc.h osdep.h rfc822.h
+tenex.o: mail.h misc.h osdep.h dummy.h
+unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h
+utf8.o: mail.h misc.h osdep.h utf8.h
+utf8aux.o: mail.h misc.h osdep.h utf8.h
+
+
+# OS-dependent
+
+osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \
+ osdep.h env_ami.h tcp_ami.h \
+ osdep.c env_ami.c fs_ami.c ftl_ami.c nl_ami.c tcp_ami.c \
+ auths.c gethstid.c \
+ gr_waitp.c \
+ auth_log.c auth_md5.c auth_pla.c \
+ pmatch.c scandir.c \
+ tz_bsd.c \
+ write.c \
+ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \
+ OSCFLAGS
+ $(CC) $(CFLAGS) `cat OSCFLAGS` -c osdep.c
+
+osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c
+ $(RM) osdep.c || true
+ cat osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c
+
+
+# Once-only environment setup
+
+once:
+ @echo Once-only environment setup...
+ ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy
+ ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ echo $(CC) > CCTYPE
+ echo $(CFLAGS) -DCHUNKSIZE=$(CHUNKSIZE) > CFLAGS
+ echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \
+ -DMD5ENABLE=\"$(MD5PWD)\" -DMAILSPOOL=\"$(MAILSPOOL)\" \
+ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \
+ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" > OSCFLAGS
+ echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS
+ $(LN) os_$(OS).h osdep.h
+ $(LN) os_$(OS).c osdepbas.c
+ $(LN) log_$(LOGINPW).c osdeplog.c
+ $(LN) ssl_none.c osdepssl.c
+
+
+# Password checkers
+
+ckpstd: # Port standard
+ $(LN) ckp_$(CHECKPW).c osdepckp.c
+
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/amiga/ckp_std.c b/imap/src/osdep/amiga/ckp_std.c
new file mode 100644
index 00000000..36255cc1
--- /dev/null
+++ b/imap/src/osdep/amiga/ckp_std.c
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ?
+ pw : NIL;
+}
diff --git a/imap/src/osdep/amiga/drivers b/imap/src/osdep/amiga/drivers
new file mode 100755
index 00000000..bd49365f
--- /dev/null
+++ b/imap/src/osdep/amiga/drivers
@@ -0,0 +1,36 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Driver Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 October 1989
+# Last Edited: 30 August 2006
+
+
+# Erase old driver linkage
+rm -f linkage.[ch]
+
+# Now define the new list
+for driver
+ do
+ echo "extern DRIVER "$driver"driver;" >> linkage.h
+ echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c
+done
diff --git a/imap/src/osdep/amiga/dummy.c b/imap/src/osdep/amiga/dummy.c
new file mode 100644
index 00000000..b003a0ba
--- /dev/null
+++ b/imap/src/osdep/amiga/dummy.c
@@ -0,0 +1,809 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate clearbox INBOX */
+ if (!*s) return &dummydriver;
+ else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ return &dummydriver;
+ }
+ /* blackbox INBOX does not exist yet */
+ else if (!compare_cstring (name,"INBOX")) return &dummydriver;
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ DRIVER *drivers;
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'/',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,i = s - test);
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ if (s = strrchr (file,'/')){/* find directory name */
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if ((file[0] == '~') || (file[0] == '#')) s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ /* always an INBOX */
+ if (pmatch ("INBOX",ucase (test))) {
+ /* done if have a dirfmt INBOX */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ drivers && !(!(drivers->flags & DR_DISABLE) &&
+ (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) ("INBOX")); drivers = drivers->next);
+ /* list INBOX appropriately */
+ dummy_listed (stream,drivers ? '/' : NIL,"INBOX",
+ drivers ? NIL : LATT_NOINFERIORS,contents);
+ }
+ }
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (!compare_cstring (s,"INBOX") &&
+ pmatch ("INBOX",ucase (strcpy (tmp,test))))
+ mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ else while (showuppers && (t = strrchr (s,'/'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf))
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* allow but snarl */
+ sprintf (tmp,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
+ mailbox);
+ MM_NOTIFY (stream,tmp,WARN);
+ case S_IFREG:
+ return sm_subscribe (mailbox);
+ }
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ DRIVER *drivers;
+ dirfmttest_t dt;
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN],path[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ if (dp = opendir (tmp)) { /* do nothing if can't open directory */
+ /* see if a non-namespace directory format */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ dir && !dt && drivers; drivers = drivers->next)
+ if (!(drivers->flags & DR_DISABLE) && (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) (dir))
+ dt = mail_parameters ((*drivers->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'/') && !pmatch (dir,"INBOX"))
+ dummy_listed (stream,'/',dir,dt ? NIL : LATT_NOSELECT,contents);
+
+ /* scan directory, ignore . and .. */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '/') while (d = readdir (dp))
+ if ((!(dt && (*dt) (d->d_name))) &&
+ ((d->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]))))) &&
+ ((len + strlen (d->d_name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,d->d_name);
+ else strcpy (tmp,d->d_name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (strcpy (path,tmp),pat,'/') ||
+ pmatch_full (strcat (path,"/"),pat,'/') ||
+ dmatch (path,pat,'/')) &&
+ mailboxdir (path,dir,"x") && (len = strlen (path)) &&
+ strcpy (path+len-1,d->d_name) && !stat (path,&sbuf)) {
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ /* form with trailing / */
+ sprintf (path,"%s/",tmp);
+ /* skip listing if INBOX */
+ if (!pmatch (tmp,"INBOX")) {
+ if (pmatch_full (tmp,pat,'/')) {
+ if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents))
+ break;
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (path,pat,'/') &&
+ !dummy_listed (stream,'/',path,LATT_NOSELECT,contents))
+ break;
+ }
+ if (dmatch (path,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,path,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ /* Must use ctime for systems that don't update mtime properly */
+ if (pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX"))
+ dummy_listed (stream,'/',tmp,LATT_NOINFERIORS +
+ ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))?
+ LATT_MARKED : LATT_UNMARKED),contents);
+ break;
+ }
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* Scan file for contents
+ * Accepts: driver to use
+ * file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ scancontents_t sc = dtb ?
+ (scancontents_t) (*dtb->parameters) (GET_SCANCONTENTS,NIL) : NIL;
+ return (*(sc ? sc : dummy_scan_contents)) (name,contents,csiz,fsiz);
+}
+
+
+/* Scan file for contents
+ * Accepts: file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ int fd;
+ unsigned long ssiz,bsiz;
+ char *buf;
+ /* forget it if can't select or open */
+ if ((fd = open (name,O_RDONLY,NIL)) >= 0) {
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (fsiz) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ fsiz -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (fsiz) return T; /* found */
+ }
+ return NIL; /* not found */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: NIL if should abort hierarchy search, else T (currently always)
+ */
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ DRIVER *d;
+ DIR *dp;
+ struct direct *dr;
+ dirfmttest_t dt;
+ unsigned long csiz;
+ struct stat sbuf;
+ int nochild;
+ char *s,tmp[MAILTMPLEN];
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ (dp = opendir (tmp))) { /* if not \NoInferiors */
+ /* locate dirfmttest if any */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ !dt && d; d = d->next)
+ if (!(d->flags & DR_DISABLE) && (d->flags & DR_DIRFMT) &&
+ (*d->valid) (name))
+ dt = mail_parameters ((*d->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* scan directory for children */
+ for (nochild = T; nochild && (dr = readdir (dp)); )
+ if ((!(dt && (*dt) (dr->d_name))) &&
+ ((dr->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (dr->d_name[1] && ((dr->d_name[1] != '.') || dr->d_name[2])))))
+ nochild = NIL;
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ closedir (dp); /* all done, flush directory */
+ }
+ d = NIL; /* don't \NoSelect dir if it has a driver */
+ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) &&
+ (d != &dummydriver)) attributes &= ~LATT_NOSELECT;
+ if (!contents || /* notify main program */
+ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) &&
+ (s = mailboxfile (tmp,name)) &&
+ (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) &&
+ !stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) &&
+ SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size)))
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = NIL;
+ /* validate name */
+ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create the name, done if made directory */
+ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&&
+ (s = strrchr (s,'/')) && !s[1]) return T;
+ return ret ? set_mbx_protections (mailbox,tmp) : NIL;
+}
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'/');
+ int wantdir = t && !t[1];
+ int mask = umask (0);
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ if (s = strrchr (path,'/')) { /* found superior to this name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) {
+ umask (mask); /* restore mask */
+ return NIL;
+ }
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path,(int) dirmode);
+ *t = '/'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >=0)
+ ret = !close (fd);
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ umask (mask); /* restore mask */
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ }
+ /* no trailing / (workaround BSD kernel bug) */
+ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ /* no trailing / allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ MM_LOG (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ else { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,mbx)) return NIL;
+ *s = c; /* restore full name */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+ sprintf (err,"Can't open %.80s: not a selectable mailbox",
+ stream->mailbox);
+ else if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ MM_LOG (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ if (time (0) >= /* time to do another test? */
+ ((time_t) (stream->gensym +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ /* append to INBOX? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ /* yes, if no empty proto try creating */
+ if (!ts && !(*(ts = default_proto (NIL))->dtb->create) (ts,"INBOX"))
+ ts = NIL;
+ }
+ else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ MM_LOG (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ else if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s;
+ if (ref) { /* preliminary reference check */
+ if (*ref == '{') return NIL;/* remote reference not allowed */
+ else if (!*ref) ref = NIL; /* treat empty reference as no reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace name */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '/': /* rooted name */
+ case '~': /* home directory name */
+ if (!ref || (*ref != '#')) {/* non-namespace reference? */
+ strcpy (tmp,pat); /* yes, ignore */
+ break;
+ }
+ /* fall through */
+ default: /* apply reference for all other names */
+ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */
+ else if ((*ref != '#') || mailboxfile (tmp,ref)) {
+ /* wants root of name? */
+ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat);
+ /* otherwise just append */
+ else sprintf (tmp,"%s%s",ref,pat);
+ }
+ else return NIL; /* unknown namespace */
+ }
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/amiga/dummy.h b/imap/src/osdep/amiga/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/amiga/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/amiga/env_ami.c b/imap/src/osdep/amiga/env_ami.c
new file mode 100644
index 00000000..843757ed
--- /dev/null
+++ b/imap/src/osdep/amiga/env_ami.c
@@ -0,0 +1,1282 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Amiga environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 May 2008
+ */
+
+#include <grp.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myMailboxDir = NIL;/* mailbox directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *mailsubdir = NIL; /* mail subdirectory name */
+static char *sysInbox = NIL; /* system inbox name */
+static char *newsActive = NIL; /* news active file */
+static char *newsSpool = NIL; /* news spool */
+ /* anonymous home directory */
+static char *anonymousHome = NIL;
+static char *ftpHome = NIL; /* ftp export home directory */
+static char *publicHome = NIL; /* public home directory */
+static char *sharedHome = NIL; /* shared home directory */
+static short anonymous = NIL; /* is anonymous */
+static short restrictBox = NIL; /* is a restricted box */
+static short has_no_life = NIL; /* is a cretin with no life */
+ /* block environment init */
+static short block_env_init = NIL;
+static short hideDotFiles = NIL;/* hide files whose names start with . */
+ /* advertise filesystem root */
+static short advertisetheworld = NIL;
+ /* disable automatic shared namespaces */
+static short noautomaticsharedns = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+static short netfsstatbug = NIL;/* compensate for broken stat() on network
+ * filesystems (AFS and old NFS). Don't do
+ * this unless you really have to!
+ */
+ /* 1 = disable plaintext, 2 = if not SSL */
+static long disablePlaintext = NIL;
+static long list_max_level = 20;/* maximum level of list recursion */
+ /* default file protection */
+static long mbx_protection = 0600;
+ /* default directory protection */
+static long dir_protection = 0700;
+ /* default lock file protection */
+static long lock_protection = MANDATORYLOCKPROT;
+ /* default ftp file protection */
+static long ftp_protection = 0644;
+static long ftp_dir_protection = 0755;
+ /* default public file protection */
+static long public_protection = 0666;
+static long public_dir_protection = 0777;
+ /* default shared file protection */
+static long shared_protection = 0660;
+static long shared_dir_protection = 0770;
+static long locktimeout = 5; /* default lock timeout */
+ /* default prototypes */
+static MAILSTREAM *createProto = NIL;
+static MAILSTREAM *appendProto = NIL;
+ /* default user flags */
+static char *userFlags[NUSERFLAGS] = {NIL};
+static NAMESPACE *nslist[3]; /* namespace list */
+static int logtry = 3; /* number of server login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+
+/* Amiga namespaces */
+
+ /* personal mh namespace */
+static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
+static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
+ /* home namespace */
+static NAMESPACE nshome = {"",'/',NIL,&nsmh};
+ /* Amiga other user namespace */
+static NAMESPACE nsamigaother = {"~",'/',NIL,NIL};
+ /* public (anonymous OK) namespace */
+static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
+ /* netnews namespace */
+static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
+ /* FTP export namespace */
+static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
+ /* shared (no anonymous) namespace */
+static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
+ /* world namespace */
+static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_NEWSACTIVE:
+ if (newsActive) fs_give ((void **) &newsActive);
+ newsActive = cpystr ((char *) value);
+ case GET_NEWSACTIVE:
+ ret = (void *) newsActive;
+ break;
+ case SET_NEWSSPOOL:
+ if (newsSpool) fs_give ((void **) &newsSpool);
+ newsSpool = cpystr ((char *) value);
+ case GET_NEWSSPOOL:
+ ret = (void *) newsSpool;
+ break;
+
+ case SET_ANONYMOUSHOME:
+ if (anonymousHome) fs_give ((void **) &anonymousHome);
+ anonymousHome = cpystr ((char *) value);
+ case GET_ANONYMOUSHOME:
+ if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
+ ret = (void *) anonymousHome;
+ break;
+ case SET_FTPHOME:
+ if (ftpHome) fs_give ((void **) &ftpHome);
+ ftpHome = cpystr ((char *) value);
+ case GET_FTPHOME:
+ ret = (void *) ftpHome;
+ break;
+ case SET_PUBLICHOME:
+ if (publicHome) fs_give ((void **) &publicHome);
+ publicHome = cpystr ((char *) value);
+ case GET_PUBLICHOME:
+ ret = (void *) publicHome;
+ break;
+ case SET_SHAREDHOME:
+ if (sharedHome) fs_give ((void **) &sharedHome);
+ sharedHome = cpystr ((char *) value);
+ case GET_SHAREDHOME:
+ ret = (void *) sharedHome;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+
+ case SET_MBXPROTECTION:
+ mbx_protection = (long) value;
+ case GET_MBXPROTECTION:
+ ret = (void *) mbx_protection;
+ break;
+ case SET_DIRPROTECTION:
+ dir_protection = (long) value;
+ case GET_DIRPROTECTION:
+ ret = (void *) dir_protection;
+ break;
+ case SET_LOCKPROTECTION:
+ lock_protection = (long) value;
+ case GET_LOCKPROTECTION:
+ ret = (void *) lock_protection;
+ break;
+ case SET_FTPPROTECTION:
+ ftp_protection = (long) value;
+ case GET_FTPPROTECTION:
+ ret = (void *) ftp_protection;
+ break;
+ case SET_PUBLICPROTECTION:
+ public_protection = (long) value;
+ case GET_PUBLICPROTECTION:
+ ret = (void *) public_protection;
+ break;
+ case SET_SHAREDPROTECTION:
+ shared_protection = (long) value;
+ case GET_SHAREDPROTECTION:
+ ret = (void *) shared_protection;
+ break;
+ case SET_FTPDIRPROTECTION:
+ ftp_dir_protection = (long) value;
+ case GET_FTPDIRPROTECTION:
+ ret = (void *) ftp_dir_protection;
+ break;
+ case SET_PUBLICDIRPROTECTION:
+ public_dir_protection = (long) value;
+ case GET_PUBLICDIRPROTECTION:
+ ret = (void *) public_dir_protection;
+ break;
+ case SET_SHAREDDIRPROTECTION:
+ shared_dir_protection = (long) value;
+ case GET_SHAREDDIRPROTECTION:
+ ret = (void *) shared_dir_protection;
+ break;
+
+ case SET_LOCKTIMEOUT:
+ locktimeout = (long) value;
+ case GET_LOCKTIMEOUT:
+ ret = (void *) locktimeout;
+ break;
+ case SET_HIDEDOTFILES:
+ hideDotFiles = value ? T : NIL;
+ case GET_HIDEDOTFILES:
+ ret = (void *) (hideDotFiles ? VOIDT : NIL);
+ break;
+ case SET_DISABLEPLAINTEXT:
+ disablePlaintext = (long) value;
+ case GET_DISABLEPLAINTEXT:
+ ret = (void *) disablePlaintext;
+ break;
+ case SET_ADVERTISETHEWORLD:
+ advertisetheworld = value ? T : NIL;
+ case GET_ADVERTISETHEWORLD:
+ ret = (void *) (advertisetheworld ? VOIDT : NIL);
+ break;
+ case SET_DISABLEAUTOSHAREDNS:
+ noautomaticsharedns = value ? T : NIL;
+ case GET_DISABLEAUTOSHAREDNS:
+ ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ case SET_USERHASNOLIFE:
+ has_no_life = value ? T : NIL;
+ case GET_USERHASNOLIFE:
+ ret = (void *) (has_no_life ? VOIDT : NIL);
+ break;
+ case SET_NETFSSTATBUG:
+ netfsstatbug = value ? T : NIL;
+ case GET_NETFSSTATBUG:
+ ret = (void *) (netfsstatbug ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ /* append timezone suffix if desired */
+ if (suffix) rfc822_timezone (date,(void *) t);
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ int onceonly = server && service && sslservice;
+ if (onceonly) { /* set server name in syslog */
+ int mask;
+ openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
+ fclose (stderr); /* possibly save a process ID */
+
+ switch (mask = umask (022)){/* check old umask */
+ case 0: /* definitely unreasonable */
+ case 022: /* don't need to change it */
+ break;
+ default: /* already was a reasonable value */
+ umask (mask); /* so change it back */
+ }
+ }
+ arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
+ arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
+ arm_signal (SIGHUP,hupint); /* prepare for hangup */
+ arm_signal (SIGPIPE,hupint); /* alternative hangup */
+ arm_signal (SIGTERM,trmint); /* prepare for termination */
+ /* status dump */
+ if (staint) arm_signal (SIGUSR1,staint);
+ if (onceonly) { /* set up network and maybe SSL */
+ long port;
+ struct servent *sv;
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ }
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Return Amiga password entry for user name
+ * Accepts: user name string
+ * Returns: password entry
+ *
+ * Tries all-lowercase form of user name if given user name fails
+ */
+
+static struct passwd *pwuser (unsigned char *user)
+{
+ unsigned char *s;
+ struct passwd *pw = getpwnam (user);
+ if (!pw) { /* failed, see if any uppercase characters */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ if (*s) { /* yes, try all lowercase form */
+ pw = getpwnam (s = lcase (cpystr (user)));
+ fs_give ((void **) &s);
+ }
+ }
+ return pw;
+}
+
+
+/* Validate password for user name
+ * Accepts: user name string
+ * password string
+ * argument count
+ * argument vector
+ * Returns: password entry if validated
+ *
+ * Tries password+1 if password fails and starts with space
+ */
+
+static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
+{
+ char *s;
+ struct passwd *pw;
+ struct passwd *ret = NIL;
+ if (auth_md5.server) { /* using CRAM-MD5 authentication? */
+ if (s = auth_md5_pwd (user)) {
+ if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
+ ret = pwuser (user); /* validated, get passwd entry for user */
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s);
+ }
+ }
+ else if (pw = pwuser (user)) {/* can get user? */
+ s = cpystr (pw->pw_name); /* copy returned name in case we need it */
+ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
+ (*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
+ ret = checkpw (pw,pwd+1,argc,argv);
+ fs_give ((void **) &s); /* don't need copy of name any more */
+ }
+ return ret;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
+{
+ struct passwd *pw = NIL;
+ int level = LOG_NOTICE;
+ char *err = "failed";
+ /* cretins still haven't given up */
+ if ((strlen (user) >= NETMAXUSER) ||
+ (authuser && (strlen (authuser) >= NETMAXUSER))) {
+ level = LOG_ALERT; /* escalate this alert */
+ err = "SYSTEM BREAK-IN ATTEMPT";
+ logtry = 0; /* render this session useless */
+ }
+ else if (logtry-- <= 0) err = "excessive login failures";
+ else if (disablePlaintext) err = "disabled";
+ else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
+ else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
+ if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
+ syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
+ user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ /* log in Mr. A. N. Onymous */
+ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
+ (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
+ argc,argv);
+}
+
+/* Finish log in and environment initialization
+ * Accepts: passwd struct for loginpw()
+ * optional authentication user name
+ * user name (NIL for anonymous)
+ * home directory (NIL to use directory from passwd struct)
+ * argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[])
+{
+ struct group *gr;
+ char **t;
+ long ret = NIL;
+ if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */
+ /* make safe copies of user and home */
+ if (user) user = cpystr (pw->pw_name);
+ home = cpystr (home ? home : pw->pw_dir);
+ /* authorization ID .NE. authentication ID? */
+ if (user && auser && *auser && compare_cstring (auser,user)) {
+ /* scan list of mail administrators */
+ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
+ if (!compare_cstring (auser,*t++))
+ ret = pw_login (pw,NIL,user,home,argc,argv);
+ syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
+ ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
+ }
+ /* normal login */
+ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
+ (ret = env_init (user,home))) chdir (myhomedir ());
+ fs_give ((void **) &home); /* clean up */
+ if (user) fs_give ((void **) &user);
+ }
+ return ret; /* return status */
+}
+
+/* Initialize environment
+ * Accepts: user name (NIL for anonymous)
+ * home directory name
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ struct passwd *pw;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ /* set up user name */
+ myUserName = cpystr (user ? user : ANONYMOUSUSER);
+ if (user) { /* remember user name and home directory */
+ nslist[0] = &nshome; /* home namespace */
+ nslist[1] = &nsamigaother;
+ nslist[2] = advertisetheworld ? &nsworld : &nsshared;
+ }
+ else { /* anonymous user */
+ nslist[0] = nslist[1] = NIL,nslist[2] = &nsftp;
+ sprintf (tmp,"%s/INBOX",
+ home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
+ sysInbox = cpystr (tmp); /* make system INBOX */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (home); /* set home directory */
+ if (!noautomaticsharedns) {
+ /* #ftp namespace */
+ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
+ /* #public namespace */
+ if (!publicHome && (pw = getpwnam ("imappublic")))
+ publicHome = cpystr (pw->pw_dir);
+ /* #shared namespace */
+ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
+ sharedHome = cpystr (pw->pw_dir);
+ }
+ if (!myLocalHost) mylocalhost ();
+ if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
+ if (!newsActive) newsActive = cpystr (ACTIVEFILE);
+ if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
+ /* force default prototype to be set */
+ if (!createProto) createProto = &CREATEPROTO;
+ if (!appendProto) appendProto = &EMPTYPROTO;
+ /* re-do open action to get flags */
+ (*createProto->dtb->open) (NIL);
+ endpwent (); /* close pw database */
+ return T;
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ struct passwd *pw;
+ struct stat sbuf;
+ char *s;
+ unsigned long euid;
+ char *ret = UNLOGGEDUSER;
+ /* no user name yet and not root? */
+ if (!myUserName && (euid = geteuid ())) {
+ /* yes, look up getlogin() user name or EUID */
+ if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
+ (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
+ (pw = getpwuid (euid))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return pw->pw_name;
+ }
+ env_init (pw->pw_name,
+ ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
+ !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
+ s : pw->pw_dir);
+ }
+ else fatal ("Unable to look up user name");
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *host_name;
+ if (!myLocalHost) myLocalHost = cpystr (gethostname (tmp,MAILTMPLEN-1) ?
+ "random-pc" : tcp_canonical (tmp));
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return my home mailbox name
+ * Returns: my home directory name
+ */
+
+static char *mymailboxdir ()
+{
+ char *home = myhomedir ();
+ if (!myMailboxDir && home) { /* initialize if first time */
+ if (mailsubdir) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s/%s",home,mailsubdir);
+ myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
+ }
+ else myMailboxDir = cpystr (home);
+ }
+ return myMailboxDir ? myMailboxDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ /* no arguments, wants mailbox directory */
+ else strcpy (dst,mymailboxdir ());
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ struct passwd *pw;
+ char *s;
+ if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
+ ((anonymous || restrictBox || (*name == '#')) &&
+ (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
+ dst = NIL; /* invalid name */
+ else switch (*name) { /* determine mailbox type based upon name */
+ case '#': /* namespace name */
+ /* #ftp/ namespace */
+ if (((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) &&
+ (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
+ /* #public/ and #shared/ namespaces */
+ else if ((((name[1] == 'p') || (name[1] == 'P')) &&
+ ((name[2] == 'u') || (name[2] == 'U')) &&
+ ((name[3] == 'b') || (name[3] == 'B')) &&
+ ((name[4] == 'l') || (name[4] == 'L')) &&
+ ((name[5] == 'i') || (name[5] == 'I')) &&
+ ((name[6] == 'c') || (name[6] == 'C')) &&
+ (name[7] == '/') && (s = publicHome)) ||
+ (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) &&
+ ((name[3] == 'a') || (name[3] == 'A')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) &&
+ ((name[5] == 'e') || (name[5] == 'E')) &&
+ ((name[6] == 'd') || (name[6] == 'D')) &&
+ (name[7] == '/') && (s = sharedHome)))
+ sprintf (dst,"%s/%s",s,compare_cstring (name,"INBOX") ? name : "INBOX");
+ else dst = NIL; /* unknown namespace */
+ break;
+
+ case '/': /* root access */
+ if (anonymous) dst = NIL; /* anonymous forbidden to do this */
+ else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))
+ dst = NIL; /* restricted and not access to sysinbox */
+ else strcpy (dst,name); /* unrestricted, copy root name */
+ break;
+ case '~': /* other user access */
+ /* bad syntax or anonymous can't win */
+ if (!*++name || anonymous) dst = NIL;
+ /* ~/ equivalent to ordinary name */
+ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
+ /* other user forbidden if restricted */
+ else if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
+ else { /* clear box other user */
+ /* copy user name */
+ for (s = dst; *name && (*name != '/'); *s++ = *name++);
+ *s++ = '\0'; /* tie off user name, look up in passwd file */
+ if ((pw = getpwnam (dst)) && pw->pw_dir) {
+ if (*name) name++; /* skip past the slash */
+ /* canonicalize case of INBOX */
+ if (!compare_cstring (name,"INBOX")) name = "INBOX";
+ /* remove trailing / from directory */
+ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
+ /* don't allow ~root/ if restricted root */
+ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
+ /* build final name w/ subdir if needed */
+ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
+ else sprintf (dst,"%s/%s",pw->pw_dir,name);
+ }
+ else dst = NIL; /* no such user */
+ }
+ break;
+ case 'I': case 'i': /* possible INBOX */
+ if (!compare_cstring (name+1,"NBOX")) {
+ /* if anonymous, use INBOX in mailbox dir */
+ if (anonymous) sprintf (dst,"%s/INBOX",mymailboxdir ());
+ else *dst = '\0'; /* otherwise driver selects the name */
+ break;
+ }
+ /* drop into to ordinary name case */
+ default: /* ordinary name is easy */
+ sprintf (dst,"%s/%s",mymailboxdir (),name);
+ break;
+ }
+ return dst; /* return final name */
+}
+
+/* Dot-lock file locker
+ * Accepts: file name to lock
+ * destination buffer for lock file name
+ * open file description on file name to lock
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_lock (char *file,DOTLOCK *base,int fd)
+{
+ int i = locktimeout * 60;
+ int j,mask,retry,pi[2],po[2];
+ char *s,tmp[MAILTMPLEN];
+ struct stat sb;
+ /* flush absurd file name */
+ if (strlen (file) > 512) return NIL;
+ /* build lock filename */
+ sprintf (base->lock,"%s.lock",file);
+ /* assume no pipe */
+ base->pipei = base->pipeo = -1;
+ do { /* make sure not symlink */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ /* time out if file older than 5 minutes */
+ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
+ /* try to create the lock */
+ if ((j = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) lock_protection)) >= 0) {
+ close (i); /* make the file, now close it */
+ chmod (base->lock,(int) lock_protection);
+ return LONGT;
+ }
+ if (errno == EEXIST) { /* already locked? */
+ retry = -1; /* can try again */
+ if (!(i%15)) { /* time to notify? */
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait 1 second before next try */
+ }
+ else retry = i = 0; /* hard failure, no more retries */
+ } while (i--); /* until out of retries */
+ if (retry < 0) { /* still returning retry after locktimeout? */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
+ sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
+ (long) (time (0) - sb.st_ctime));
+ mm_log (tmp,WARN);
+ }
+ mask = umask (0);
+ unlink (base->lock); /* try to remove the old file */
+ /* seize the lock */
+ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) lock_protection)) >= 0) {
+ close (i); /* don't need descriptor any more */
+ sprintf (tmp,"Mailbox %.80s lock overridden",file);
+ mm_log (tmp,NIL);
+ chmod (base->lock,(int) lock_protection);
+ umask (mask) /* restore old umask */
+ return LONGT;
+ }
+ umask (mask) /* restore old umask */
+ }
+
+ if (fd >= 0) switch (errno) {
+ case EACCES: /* protection failure? */
+ /* make command pipes */
+ if (!stat (LOCKPGM,&sb) && (pipe (pi) >= 0)) {
+ /* if input pipes usable create output pipes */
+ if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
+ /* make sure output pipes are usable */
+ if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
+ /* all is good, make inferior process */
+ else if (!(j = fork ())) {
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ long cf; /* don't change caller vars in case vfork() */
+ char *argv[4],arg[20];
+ /* prepare argument vector */
+ sprintf (arg,"%d",fd);
+ argv[0] = LOCKPGM; argv[1] = arg;
+ argv[2] = file; argv[3] = NIL;
+ /* set parent's I/O to my O/I */
+ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
+ /* close all unnecessary descriptors */
+ for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
+ cf >= 3; --cf) if (cf != fd) close (cf);
+ /* be our own process group */
+ setpgrp (0,getpid ());
+ /* now run it */
+ _exit (execv (argv[0],argv));
+ }
+ _exit (1); /* child is done */
+ }
+ else if (j > 0) { /* parent process */
+ fd_set rfd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_SET (pi[0],&rfd);
+ tmo.tv_sec = locktimeout * 60;
+ grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
+ /* read response from locking program */
+ if (select (pi[0]+1,&rfd,0,0,&tmo) &&
+ (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
+ /* success, record pipes */
+ base->pipei = pi[0]; base->pipeo = po[1];
+ /* close child's side of the pipes */
+ close (pi[1]); close (po[0]);
+ return LONGT;
+ }
+ }
+ close (po[0]); close (po[1]);
+ }
+ close (pi[0]); close (pi[1]);
+ }
+ /* find directory/file delimiter */
+ if (s = strrchr (base->lock,'/')) {
+ *s = '\0'; /* tie off at directory */
+ sprintf(tmp, /* generate default message */
+ "Mailbox vulnerable - directory %.80s must have 1777 protection",
+ base->lock);
+ /* definitely not 1777 if can't stat */
+ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
+ *s = '/'; /* restore lock name */
+ if (mask != 1777) { /* default warning if not 1777 */
+ MM_LOG (tmp,WARN);
+ break;
+ }
+ }
+ default:
+ sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
+ base->lock,strerror (errno));
+ mm_log (tmp,WARN); /* this is probably not good */
+ break;
+ }
+ base->lock[0] = '\0'; /* don't use lock files */
+ return NIL;
+}
+
+/* Dot-lock file unlocker
+ * Accepts: lock file name
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_unlock (DOTLOCK *base)
+{
+ long ret = LONGT;
+ if (base && base->lock[0]) {
+ if (base->pipei >= 0) { /* if running through a pipe unlocker */
+ ret = (write (base->pipeo,"+",1) == 1);
+ /* nuke the pipes */
+ close (base->pipei); close (base->pipeo);
+ }
+ else ret = !unlink (base->lock);
+ }
+ return ret;
+}
+
+/* Lock file name
+ * Accepts: scratch buffer
+ * file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockname (char *lock,char *fname,int op,long *pid)
+{
+ struct stat sbuf;
+ *pid = 0; /* no locker PID */
+ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
+}
+
+
+/* Lock file descriptor
+ * Accepts: file descriptor
+ * lock file name buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockfd (int fd,char *lock,int op)
+{
+ struct stat sbuf;
+ return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
+}
+
+/* Lock file name worker
+ * Accepts: lock file name
+ * pointer to stat() buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lock_work (char *lock,void *sb,int op,long *pid)
+{
+ struct stat lsb,fsb;
+ struct stat *sbuf = (struct stat *) sb;
+ char tmp[MAILTMPLEN];
+ long i;
+ int fd;
+ int mask = umask (0);
+ if (pid) *pid = 0; /* initialize return PID */
+ /* make temporary lock file name */
+ sprintf (lock,"%s/.%lx.%lx","/tmp",
+ (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
+ while (T) { /* until get a good lock */
+ do switch ((int) chk_notsymlink (lock,&lsb)) {
+ case 1: /* exists just once */
+ if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) ||
+ (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
+ case -1: /* name doesn't exist */
+ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection);
+ break;
+ default: /* multiple hard links */
+ mm_log ("hard link to lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
+ case 0: /* symlink (already did syslog) */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: no lock file */
+ } while ((fd < 0) && (errno == EEXIST));
+ if (fd < 0) { /* failed to get file descriptor */
+ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
+ strerror (errno));
+ if (stat ("/tmp",&lsb))
+ syslog (LOG_CRIT,"SYSTEM ERROR: no /tmp: %s",strerror (errno));
+ else if ((lsb.st_mode & 01777) != 01777)
+ mm_log ("Can't lock for write: /tmp must have 1777 protection",WARN);
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't open lock file */
+ }
+
+ /* non-blocking form */
+ if (op & LOCK_NB) i = flock (fd,op);
+ else { /* blocking form */
+ (*mailblocknotify) (BLOCK_FILELOCK,NIL);
+ i = flock (fd,op);
+ (*mailblocknotify) (BLOCK_NONE,NIL);
+ }
+ if (i) { /* failed, get other process' PID */
+ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
+ (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
+ *pid = i;
+ close (fd); /* failed, give up on lock */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't lock */
+ }
+ /* make sure this lock is good for us */
+ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
+ !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
+ (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
+ close (fd); /* lock not right, drop fd and try again */
+ }
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (lock,(int) lock_protection);
+ umask (mask); /* restore old mask */
+ return fd; /* success */
+}
+
+/* Check to make sure not a symlink
+ * Accepts: file name
+ * stat buffer
+ * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
+ */
+
+long chk_notsymlink (char *name,void *sb)
+{
+ struct stat *sbuf = (struct stat *) sb;
+ /* name exists? */
+ if (lstat (name,sbuf)) return -1;
+ /* forbid symbolic link */
+ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
+ mm_log ("symbolic link on lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
+ name);
+ return NIL;
+ }
+ return (long) sbuf->st_nlink; /* return number of hard links */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ /* delete the file if no sharers */
+ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+/* Set proper file protection for mailbox
+ * Accepts: mailbox name
+ * actual file path name
+ * Returns: T, always
+ */
+
+long set_mbx_protections (char *mailbox,char *path)
+{
+ struct stat sbuf;
+ int mode = (int) mbx_protection;
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) mode = (int) ftp_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) mode = (int) public_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) mode = (int) shared_protection;
+ }
+ /* if a directory */
+ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* set owner search if allow read or write */
+ if (mode & 0600) mode |= 0100;
+ if (mode & 060) mode |= 010;/* set group search if allow read or write */
+ if (mode & 06) mode |= 01; /* set world search if allow read or write */
+ /* preserve directory SGID bit */
+ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
+ }
+ chmod (path,mode); /* set the new protection, ignore failure */
+ return LONGT;
+}
+
+/* Get proper directory protection
+ * Accepts: mailbox name
+ * Returns: directory mode, always
+ */
+
+long get_dir_protection (char *mailbox)
+{
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) return ftp_dir_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) return public_dir_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) return shared_dir_protection;
+ }
+ return dir_protection;
+}
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ myusername (); /* make sure initialized */
+ /* return default driver's prototype */
+ return type ? appendProto : createProto;
+}
+
+
+/* Set up user flags for stream
+ * Accepts: MAIL stream
+ * Returns: MAIL stream with user flags set up
+ */
+
+MAILSTREAM *user_flags (MAILSTREAM *stream)
+{
+ int i;
+ myusername (); /* make sure initialized */
+ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
+ return stream;
+}
+
+
+/* Return nth user flag
+ * Accepts: user flag number
+ * Returns: flag
+ */
+
+char *default_user_flag (unsigned long i)
+{
+ myusername (); /* make sure initialized */
+ return userFlags[i];
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned int) data) alarm ((unsigned int) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/env_ami.h b/imap/src/osdep/amiga/env_ami.h
new file mode 100644
index 00000000..365e5128
--- /dev/null
+++ b/imap/src/osdep/amiga/env_ami.h
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+typedef struct dotlock_base {
+ char lock[MAILTMPLEN];
+ int pipei;
+ int pipeo;
+} DOTLOCK;
+
+
+/* Bits that can be set in restrictBox */
+
+#define RESTRICTROOT 0x1 /* restricted box doesn't allow root */
+#define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */
+
+/* Subscription definitions for UNIX */
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ())
+
+
+/* dorc() options */
+
+#define SYSCONFIG "/etc/c-client.cf"
+
+
+/* Special users */
+
+#define ANONYMOUSUSER "nobody" /* anonymous user */
+#define UNLOGGEDUSER "root" /* unlogged-in user */
+#define ADMINGROUP "mailadm" /* mail administrator group */
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+long dotlock_lock (char *file,DOTLOCK *base,int fd);
+long dotlock_unlock (DOTLOCK *base);
+int lockname (char *lock,char *fname,int op,long *pid);
+int lockfd (int fd,char *lock,int op);
+int lock_work (char *lock,void *sbuf,int op,long *pid);
+long chk_notsymlink (char *name,void *sbuf);
+void unlockfd (int fd,char *lock);
+long set_mbx_protections (char *mailbox,char *path);
+long get_dir_protection (char *mailbox);
+MAILSTREAM *user_flags (MAILSTREAM *stream);
+char *default_user_flag (unsigned long i);
+void dorc (char *file,long flag);
+long path_create (MAILSTREAM *stream,char *mailbox);
+void grim_pid_reap_status (int pid,int killreq,void *status);
+#define grim_pid_reap(pid,killreq) \
+ grim_pid_reap_status (pid,killreq,NIL)
+long safe_write (int fd,char *buf,long nbytes);
+void *arm_signal (int sig,void *action);
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]);
+long loginpw (struct passwd *pw,int argc,char *argv[]);
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[]);
+void *mm_blocknotify (int reason,void *data);
diff --git a/imap/src/osdep/amiga/fdstring.c b/imap/src/osdep/amiga/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/amiga/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/amiga/fdstring.h b/imap/src/osdep/amiga/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/amiga/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/amiga/fs_ami.c b/imap/src/osdep/amiga/fs_ami.c
new file mode 100644
index 00000000..b433e1b4
--- /dev/null
+++ b/imap/src/osdep/amiga/fs_ami.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ free (*block);
+ *block = NIL;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
diff --git a/imap/src/osdep/amiga/ftl_ami.c b/imap/src/osdep/amiga/ftl_ami.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/amiga/ftl_ami.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/amiga/gethstid.c b/imap/src/osdep/amiga/gethstid.c
new file mode 100644
index 00000000..01516976
--- /dev/null
+++ b/imap/src/osdep/amiga/gethstid.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Get host ID emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ /* No gethostid() here, so just fake it and hope things turn out okay. */
+ return 0xdeadface;
+}
diff --git a/imap/src/osdep/amiga/gr_waitp.c b/imap/src/osdep/amiga/gr_waitp.c
new file mode 100644
index 00000000..8b230f9d
--- /dev/null
+++ b/imap/src/osdep/amiga/gr_waitp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- waitpid() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/amiga/log_std.c b/imap/src/osdep/amiga/log_std.c
new file mode 100644
index 00000000..a36fa88f
--- /dev/null
+++ b/imap/src/osdep/amiga/log_std.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) ||
+ setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mbx.c b/imap/src/osdep/amiga/mbx.c
new file mode 100644
index 00000000..1ece5d8d
--- /dev/null
+++ b/imap/src/osdep/amiga/mbx.c
@@ -0,0 +1,1855 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+
+/* Kludge to make Cygwin happy */
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned long lastpid; /* PID of last writer */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mbx_file (char *dst,char *name);
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mbx_status, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * scratch buffer
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ time_t tp[2];
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
+ ((fd = open (tmp,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* bogus format */
+ /* I love cretinous C compilers -- don't you? */
+ if (read (fd,hdr,HDRSIZE) == HDRSIZE)
+ if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
+ (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') &&
+ (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]))
+ if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
+ isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
+ isxdigit (c = hdr[15]) && isxdigit (hdr[16]))
+ if (isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
+ isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
+ (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockfd (fd,lock,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr,HDRSIZE) != HDRSIZE)) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),
+ 0,sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) &&
+ (j = strtol (s+1,&s,10)) && (*s == ';') &&
+ (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) &&
+ (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (tmp,tp); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mbx_file ((char *) value,"INBOX");
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = mbx_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if made directory */
+ if ((s = strrchr (s,'/')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i) {
+ t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ ((t = default_user_flag (i)) ? t : "");
+ sprintf (s += strlen (s),"%s\015\012",t);
+ }
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mbx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (tmp,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* superior name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT)))
+ return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mbxproto);
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!mbx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ unsigned long oldpid = LOCAL->lastpid;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ /* we are the last flag updater */
+ LOCAL->lastpid = (unsigned long) getpid ();
+ /* update header if needed */
+ if (((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid))
+ mbx_update_header (stream);
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ int snarf = stream->inbox && !stream->rdonly;
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs || snarf) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ /* reparse header if not flagchecking */
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ /* always reparse header at least */
+ if (ret && snarf) { /* snarf new messages if still OK */
+ mbx_snarf (stream);
+ /* parse snarfed messages */
+ ret = mbx_parse (stream);
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail snarf messages from system inbox
+ * Accepts: MAIL stream, already locked
+ */
+
+void mbx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* build header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;00000000%04x-00000000\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) +
+ (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) +
+ (fANSWERED * elt->answered) + (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_setflag (sysibx,tmp,"\\Deleted");
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) >= 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ MM_CRITICAL (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message))
+ ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ /* set atime to now-1 if successful copy */
+ if (ret) tp[0] = time (0) - 1;
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ MM_NOCRITICAL (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mbx_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s;
+}
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ /* get current last flag updater PID */
+ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) &&
+ (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))?
+ strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0;
+ /* set flagcheck if lastpid changed */
+ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T;
+ LOCAL->lastpid = i; /* set as last PID */
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ MM_LOG (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#ifndef CYGKLUDGEOFFSET
+#define CYGKLUDGEOFFSET 0
+#endif
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid);
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET,
+ HDRSIZE - CYGKLUDGEOFFSET) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock mailbox for rewrite",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ MM_CRITICAL (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ MM_NOCRITICAL (stream); /* release critical */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ }
+
+ else { /* can't get exclusive */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ (*bn) (BLOCK_NONE,NIL);
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/amiga/mh.c b/imap/src/osdep/amiga/mh.c
new file mode 100644
index 00000000..0226b7af
--- /dev/null
+++ b/imap/src/osdep/amiga/mh.c
@@ -0,0 +1,1283 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MH mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 February 1992
+ * Last Edited: 11 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */
+#define MHINBOXDIR "inbox"
+#define MHPROFILE ".mh_profile"
+#define MHCOMMA ','
+#define MHSEQUENCE ".mh_sequence"
+#define MHSEQUENCES ".mh_sequences"
+#define MHPATH "Mail"
+
+
+/* mh_load_message() flags */
+
+#define MLM_HEADER 0x1 /* load message text */
+#define MLM_TEXT 0x2 /* load message text */
+
+/* MH I/O stream local data */
+
+typedef struct mh_local {
+ char *dir; /* spool directory name */
+ unsigned char buf[CHUNKSIZE]; /* temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MHLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MHLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mh_valid (char *name);
+int mh_isvalid (char *name,char *tmp,long synonly);
+int mh_namevalid (char *name);
+char *mh_path (char *tmp);
+void *mh_parameters (long function,void *value);
+long mh_dirfmttest (char *name);
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mh_list (MAILSTREAM *stream,char *ref,char *pat);
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
+long mh_subscribe (MAILSTREAM *stream,char *mailbox);
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mh_create (MAILSTREAM *stream,char *mailbox);
+long mh_delete (MAILSTREAM *stream,char *mailbox);
+long mh_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mh_open (MAILSTREAM *stream);
+void mh_close (MAILSTREAM *stream,long options);
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long mh_ping (MAILSTREAM *stream);
+void mh_check (MAILSTREAM *stream);
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+int mh_select (struct direct *name);
+int mh_numsort (const void *d1,const void *d2);
+char *mh_file (char *dst,char *name);
+long mh_canonicalize (char *pattern,char *ref,char *pat);
+void mh_setdate (char *file,MESSAGECACHE *elt);
+
+/* MH mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mhdriver = {
+ "mh", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mh_valid, /* mailbox is valid for us */
+ mh_parameters, /* manipulate parameters */
+ mh_scan, /* scan mailboxes */
+ mh_list, /* find mailboxes */
+ mh_lsub, /* find subscribed mailboxes */
+ mh_subscribe, /* subscribe to mailbox */
+ mh_unsubscribe, /* unsubscribe from mailbox */
+ mh_create, /* create mailbox */
+ mh_delete, /* delete mailbox */
+ mh_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mh_open, /* open mailbox */
+ mh_close, /* close mailbox */
+ mh_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mh_header, /* fetch message header */
+ mh_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mh_ping, /* ping mailbox to see if still alive */
+ mh_check, /* check for new messages */
+ mh_expunge, /* expunge deleted messages */
+ mh_copy, /* copy messages to another mailbox */
+ mh_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mhproto = {&mhdriver};
+
+
+static char *mh_profile = NIL; /* holds MH profile */
+static char *mh_pathname = NIL; /* holds MH path name */
+static long mh_once = 0; /* already snarled once */
+static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */
+
+/* MH mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mh_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;
+}
+
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * syntax only test flag
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_isvalid (char *name,char *tmp,long synonly)
+{
+ struct stat sbuf;
+ char *s,*t,altname[MAILTMPLEN];
+ unsigned long i;
+ int ret = NIL;
+ errno = NIL; /* zap any error condition */
+ /* mh name? */
+ if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) ||
+ !compare_cstring (name,MHINBOX) ||
+ ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){
+ if (mh_path (tmp)) /* validate name if INBOX or not synonly */
+ ret = (synonly && compare_cstring (name,"INBOX")) ?
+ T : ((stat (mh_file (tmp,name),&sbuf) == 0) &&
+ (sbuf.st_mode & S_IFMT) == S_IFDIR);
+ else if (!mh_once++) { /* only report error once */
+ sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile);
+ mm_log (tmp,WARN);
+ }
+ }
+ /* see if non-NS name within mh hierarchy */
+ else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) &&
+ (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) &&
+ (tmp[i] == '/') && tmp[i+1]) {
+ sprintf (altname,"#mh%.900s",tmp+i);
+ /* can't do synonly here! */
+ ret = mh_isvalid (altname,tmp,NIL);
+ }
+ else errno = EINVAL; /* bogus name */
+ return ret;
+}
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_namevalid (char *name)
+{
+ char *s;
+ if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') &&
+ (name[2] == 'h' || name[2] == 'H') && name[3] == '/')
+ for (s = name; s && *s;) { /* make sure no all-digit nodes */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break;/* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* Return MH path
+ * Accepts: temporary buffer
+ * Returns: MH path or NIL if MH disabled
+ */
+
+char *mh_path (char *tmp)
+{
+ char *s,*t,*v,*r;
+ int fd;
+ struct stat sbuf;
+ if (!mh_profile) { /* build mh_profile and mh_pathname now */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE);
+ if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) {
+ fstat (fd,&sbuf); /* yes, get size and read file */
+ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size);
+ close (fd); /* don't need the file any more */
+ t[sbuf.st_size] = '\0'; /* tie it off */
+ /* parse profile file */
+ for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) {
+ /* found space in line? */
+ if (v = strpbrk (s," \t")) {
+ *v++ = '\0'; /* tie off, is keyword "Path:"? */
+ if (!compare_cstring (s,"Path:")) {
+ /* skip whitespace */
+ while ((*v == ' ') || (*v == '\t')) ++v;
+ /* absolute path? */
+ if (*v == '/') s = v;
+ else sprintf (s = tmp,"%s/%s",myhomedir (),v);
+ /* copy name */
+ mh_pathname = cpystr (s);
+ break; /* don't need to look at rest of file */
+ }
+ }
+ }
+ fs_give ((void **) &t); /* flush profile text */
+ if (!mh_pathname) { /* default path if not in the profile */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPATH);
+ mh_pathname = cpystr (tmp);
+ }
+ }
+ }
+ return mh_pathname;
+}
+
+/* MH manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mh_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mh_file ((char *) value,"INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mh_dirfmttest;
+ break;
+ case SET_MHPROFILE:
+ if (mh_profile) fs_give ((void **) &mh_profile);
+ mh_profile = cpystr ((char *) value);
+ case GET_MHPROFILE:
+ ret = (void *) mh_profile;
+ break;
+ case SET_MHPATH:
+ if (mh_pathname) fs_give ((void **) &mh_pathname);
+ mh_pathname = cpystr ((char *) value);
+ case GET_MHPATH:
+ ret = (void *) mh_pathname;
+ break;
+ case SET_MHALLOWINBOX:
+ mh_allow_inbox = value ? T : NIL;
+ case GET_MHALLOWINBOX:
+ ret = (void *) (mh_allow_inbox ? VOIDT : NIL);
+ }
+ return ret;
+}
+
+
+/* MH test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mh_dirfmttest (char *s)
+{
+ int c;
+ /* sequence(s) file is an internal name */
+ if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) {
+ if (*s == MHCOMMA) ++s; /* else comma + all numeric name */
+ /* success if all-numeric */
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ }
+ return LONGT;
+}
+
+/* MH scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (mh_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ mm_list (stream,'/',test,LATT_NOSELECT);
+ }
+ }
+ /* get canonical form of name */
+ else if (mh_canonicalize (test,ref,pat)) {
+ if (contents) { /* maybe I'll implement this someday */
+ mm_log ("Scan not valid for mh mailboxes",ERROR);
+ return;
+ }
+ if (test[3] == '/') { /* looking down levels? */
+ /* yes, found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test+4,i = s - (test+4));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test+4);/* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'/')) {
+ *s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* do the work */
+ mh_list_work (stream,s,test,0);
+ }
+ /* always an INBOX */
+ if (!compare_cstring (test,MHINBOX))
+ mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS);
+ }
+}
+
+/* MH list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ mh_scan (stream,ref,pat,NIL);
+}
+
+
+/* MH list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,test[MAILTMPLEN];
+ /* get canonical form of name */
+ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) {
+ do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+ }
+}
+
+/* MH list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * search level
+ */
+
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
+{
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
+ /* build MH name to search */
+ if (dir) sprintf (name,"#mh/%s/",dir);
+ else strcpy (name,"#mh/");
+ /* make directory name, punt if bogus */
+ if (!mh_file (curdir,name)) return;
+ cp = curdir + strlen (curdir);/* end of directory name */
+ np = name + strlen (name); /* end of MH name */
+ if (dp = opendir (curdir)) { /* open directory */
+ while (d = readdir (dp)) /* scan, ignore . and numeric names */
+ if ((d->d_name[0] != '.') && !mh_select (d)) {
+ strcpy (cp,d->d_name); /* make directory name */
+ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ strcpy (np,d->d_name);/* make mh name of directory name */
+ /* yes, an MH name if full match */
+ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL);
+ /* check if should recurse */
+ if (dmatch (name,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ mh_list_work (stream,name+4,pat,level+1);
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* MH mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MH mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MH mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!mh_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox);
+ /* must not already exist */
+ else if (mh_isvalid (mailbox,tmp,NIL))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ else if (!mh_path (tmp)) return NIL;
+ /* try to make it */
+ else if (!(mh_file (tmp,mailbox) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else return LONGT; /* success */
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* MH mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int i;
+ char tmp[MAILTMPLEN];
+ /* is mailbox valid? */
+ if (!mh_isvalid (mailbox,tmp,NIL)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get name of directory */
+ i = strlen (mh_file (tmp,mailbox));
+ if (dirp = opendir (tmp)) { /* open directory */
+ tmp[i++] = '/'; /* now apply trailing delimiter */
+ /* massacre all mh owned files */
+ while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) {
+ strcpy (tmp + i,d->d_name);
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ }
+ /* try to remove the directory */
+ if (rmdir (mh_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
+ mm_log (tmp,WARN);
+ }
+ return T; /* return success */
+}
+
+/* MH mail rename mailbox
+ * Accepts: MH mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ /* old mailbox name must be valid */
+ if (!mh_isvalid (old,tmp,NIL))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mh_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mh_isvalid (newname,tmp,NIL))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ /* success if can rename the directory */
+ else { /* found superior to destination name? */
+ if (s = strrchr (mh_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (mh_file (tmp,old),tmp1)) return T;
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ mm_log (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MH mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mh_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mh recycle stream");
+ stream->local = fs_get (sizeof (MHLOCAL));
+ /* INBOXness is one of the following:
+ * #mhinbox (case-independent)
+ * #mh/inbox (mh is case-independent, inbox is case-dependent)
+ * INBOX (case-independent
+ */
+ stream->inbox = /* note if an INBOX or not */
+ (!compare_cstring (stream->mailbox,MHINBOX) ||
+ ((stream->mailbox[0] == '#') &&
+ ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) &&
+ ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) &&
+ (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) ||
+ !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL;
+ mh_file (tmp,stream->mailbox);/* get directory name */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mh_ping (stream)) return NIL;
+ if (!(stream->nmsgs || stream->silent))
+ mm_log ("Mailbox is empty",(long) NIL);
+ return stream; /* return stream to caller */
+}
+
+/* MH mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mh_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL);
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MH mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL);
+}
+
+/* MH load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ --j;
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+ /* need to load cache with message data? */
+ if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+
+ if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i < elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.header.text.data) !=
+ elt->private.msg.header.text.size) fatal ("mh hdr size mismatch");
+ }
+ if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.special.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i < elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.text.text.data) !=
+ elt->private.msg.text.text.size) fatal ("mh txt size mismatch");
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* MH mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ mh_load_message (stream,msgno,MLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* MH mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mh_load_message (stream,msgno,MLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MH mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mh_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r;
+ unsigned long old = stream->uid_last;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */
+ if (stream->inbox && /* no, create if INBOX */
+ dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"),
+ get_dir_protection ("INBOX"))) return T;
+ sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ else { /* see if already read */
+ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name);
+ if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime))
+ elt->seen = T;
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+
+ /* if INBOX, snarf from system INBOX */
+ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ mm_critical (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mh_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MH mailbox failed: %.80s",
+ s,strerror (errno));
+ mm_log (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ mm_nocritical (stream); /* release critical */
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MH mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mh_check (MAILSTREAM *stream)
+{
+ /* Perhaps in the future this will preserve flags */
+ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MH mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ mm_critical (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ mm_log (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MH mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char flags[MAILTMPLEN],date[MAILTMPLEN];
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ long ret = NIL;
+ /* copy the messages */
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
+ fstat (fd,&sbuf); /* get size of message */
+ if (!elt->day) { /* set internaldate to file date if needed */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ /* kludge; mh_append would just strip CRs */
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->deleted) strcat (flags," \\Deleted");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '('; /* open list */
+ strcat (flags,")"); /* close list */
+ mail_date (date,elt); /* generate internal date */
+ if (au) mail_parameters (NIL,SET_APPENDUID,NIL);
+ if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) &&
+ (options & CP_MOVE)) elt->deleted = T;
+ if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ close (fd);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret; /* return success */
+}
+
+/* MH mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct direct **names = NIL;
+ int fd;
+ char c,*flags,*date,*s,tmp[MAILTMPLEN];
+ STRING *message;
+ MESSAGECACHE elt;
+ FILE *df;
+ long i,size,last,nfiles;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mhproto;
+ /* make sure valid mailbox */
+ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!((!compare_cstring (mailbox,MHINBOX) ||
+ !compare_cstring (mailbox,"INBOX")) &&
+ (mh_file (tmp,MHINBOX) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) {
+ /* largest number */
+ last = atoi (names[nfiles-1]->d_name);
+ for (i = 0; i < nfiles; ++i) /* free directory */
+ fs_give ((void **) &names[i]);
+ }
+ else last = 0; /* no messages here yet */
+ if (s = (void *) names) fs_give ((void **) &s);
+
+ mm_critical (stream); /* go critical */
+ do {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ if (date) { /* want to preserve date? */
+ /* yes, parse date into an elt */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ }
+ mh_file (tmp,mailbox); /* build file name we will use */
+ sprintf (tmp + strlen (tmp),"/%ld",++last);
+ if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ /* copy the data w/o CR's */
+ for (size = 0,i = SIZE (message); i && ret; --i)
+ if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL;
+ /* close the file */
+ if (!ret || fclose (df)) {
+ unlink (tmp); /* delete message */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ if (ret) { /* set the date for this message */
+ if (date) mh_setdate (tmp,&elt);
+ /* get next message */
+ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MH file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mh_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MH file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mh_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MH mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mh_file (char *dst,char *name)
+{
+ char *s;
+ char *path = mh_path (dst);
+ if (!path) fatal ("No mh path in mh_file()!");
+ /* INBOX becomes "inbox" in the MH path */
+ if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX"))
+ sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR);
+ /* #mh names skip past prefix */
+ else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4);
+ else mailboxfile (dst,name); /* all other names */
+ /* tie off unnecessary trailing / */
+ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0';
+ return dst;
+}
+
+/* MH canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long mh_canonicalize (char *pattern,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with / */
+ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if (mh_isvalid (pattern,tmp,T)) {
+ /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ mm_log ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mh_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/amiga/mix.c b/imap/src/osdep/amiga/mix.c
new file mode 100644
index 00000000..fbf4a023
--- /dev/null
+++ b/imap/src/osdep/amiga/mix.c
@@ -0,0 +1,2834 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIX mail routines
+ *
+ * Author(s): Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 March 2006
+ * Last Edited: 7 May 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MIX definitions */
+
+#define MEGABYTE (1024*1024)
+
+#define MIXDATAROLL MEGABYTE /* size at which we roll to a new file */
+
+
+/* MIX files */
+
+#define MIXNAME ".mix" /* prefix for all MIX file names */
+#define MIXMETA "meta" /* suffix for metadata */
+#define MIXINDEX "index" /* suffix for index */
+#define MIXSTATUS "status" /* suffix for status */
+#define MIXSORTCACHE "sortcache"/* suffix for sortcache */
+#define METAMAX (MEGABYTE-1) /* maximum metadata file size (sanity check) */
+
+
+/* MIX file formats */
+
+ /* sequence format (all but msg files) */
+#define SEQFMT "S%08lx\015\012"
+ /* metadata file format */
+#define MTAFMT "V%08lx\015\012L%08lx\015\012N%08lx\015\012"
+ /* index file record format */
+#define IXRFMT ":%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:%08lx:%08lx:%08lx:%08lx:\015\012"
+ /* status file record format */
+#define STRFMT ":%08lx:%08lx:%04x:%08lx:\015\012"
+ /* message file header format */
+#define MSRFMT "%s%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:\015\012"
+#define MSGTOK ":msg:"
+#define MSGTSZ (sizeof(MSGTOK)-1)
+ /* sortcache file record format */
+#define SCRFMT ":%08lx:%08lx:%08lx:%08lx:%08lx:%c%08lx:%08lx:%08lx:\015\012"
+
+/* MIX I/O stream local data */
+
+typedef struct mix_local {
+ unsigned long curmsg; /* current message file number */
+ unsigned long newmsg; /* current new message file number */
+ time_t lastsnarf; /* last snarf time */
+ int msgfd; /* file description of current msg file */
+ int mfd; /* file descriptor of open metadata */
+ unsigned long metaseq; /* metadata sequence */
+ char *index; /* mailbox index name */
+ unsigned long indexseq; /* index sequence */
+ char *status; /* mailbox status name */
+ unsigned long statusseq; /* status sequence */
+ char *sortcache; /* mailbox sortcache name */
+ unsigned long sortcacheseq; /* sortcache sequence */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned int expok : 1; /* non-zero if expunge reports OK */
+ unsigned int internal : 1; /* internally opened, do not validate */
+} MIXLOCAL;
+
+
+#define MIXBURP struct mix_burp
+
+MIXBURP {
+ unsigned long fileno; /* message file number */
+ char *name; /* message file name */
+ SEARCHSET *tail; /* tail of ranges */
+ SEARCHSET set; /* set of retained ranges */
+ MIXBURP *next; /* next file to burp */
+};
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MIXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mix_valid (char *name);
+long mix_isvalid (char *name,char *meta);
+void *mix_parameters (long function,void *value);
+long mix_dirfmttest (char *name);
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mix_list (MAILSTREAM *stream,char *ref,char *pat);
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mix_subscribe (MAILSTREAM *stream,char *mailbox);
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mix_create (MAILSTREAM *stream,char *mailbox);
+long mix_delete (MAILSTREAM *stream,char *mailbox);
+long mix_rename (MAILSTREAM *stream,char *old,char *newname);
+int mix_rselect (struct direct *name);
+MAILSTREAM *mix_open (MAILSTREAM *stream);
+void mix_close (MAILSTREAM *stream,long options);
+void mix_abort (MAILSTREAM *stream);
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long mix_ping (MAILSTREAM *stream);
+void mix_check (MAILSTREAM *stream);
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options);
+int mix_select (struct direct *name);
+int mix_msgfsort (const void *d1,const void *d2);
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size);
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed);
+long mix_burp_check (SEARCHSET *set,size_t size,char *file);
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq);
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags);
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq);
+long mix_meta_update (MAILSTREAM *stream);
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag);
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag);
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize);
+FILE *mix_sortcache_open (MAILSTREAM *stream);
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache);
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type);
+unsigned long mix_read_sequence (FILE *f);
+char *mix_dir (char *dst,char *name);
+char *mix_file (char *dst,char *dir,char *name);
+char *mix_file_data (char *dst,char *dir,unsigned long data);
+unsigned long mix_modseq (unsigned long oldseq);
+
+/* MIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mixdriver = {
+ "mix", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT|DR_MODSEQ,
+ (DRIVER *) NIL, /* next driver */
+ mix_valid, /* mailbox is valid for us */
+ mix_parameters, /* manipulate parameters */
+ mix_scan, /* scan mailboxes */
+ mix_list, /* find mailboxes */
+ mix_lsub, /* find subscribed mailboxes */
+ mix_subscribe, /* subscribe to mailbox */
+ mix_unsubscribe, /* unsubscribe from mailbox */
+ mix_create, /* create mailbox */
+ mix_delete, /* delete mailbox */
+ mix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mix_open, /* open mailbox */
+ mix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mix_header, /* fetch message header only */
+ mix_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mix_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ mix_sort, /* sort messages */
+ mix_thread, /* thread messages */
+ mix_ping, /* ping mailbox to see if still alive */
+ mix_check, /* check for new messages */
+ mix_expunge, /* expunge deleted messages */
+ mix_copy, /* copy messages to another mailbox */
+ mix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mixproto = {&mixdriver};
+
+/* MIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mix_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mix_isvalid (name,tmp) ? &mixdriver : NIL;
+}
+
+
+/* MIX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return meta name
+ * Returns: T if valid, NIL otherwise, metadata name written in both cases
+ */
+
+long mix_isvalid (char *name,char *meta)
+{
+ char dir[MAILTMPLEN];
+ struct stat sbuf;
+ /* validate name as directory */
+ if (!(errno = ((strlen (name) > NETMAXMBX) ? ENAMETOOLONG : NIL)) &&
+ *mix_dir (dir,name) && mix_file (meta,dir,MIXMETA) &&
+ !stat (dir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mix? */
+ if (!stat (meta,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
+ return LONGT;
+ else errno = NIL; /* directory but not mix */
+ }
+ return NIL;
+}
+
+/* MIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mix_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mix_scan_contents;
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MIX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mix_dirfmttest (char *name)
+{
+ /* belongs to MIX if starts with .mix */
+ return strncmp (name,MIXNAME,sizeof (MIXNAME) - 1) ? NIL : LONGT;
+}
+
+/* MIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MIX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mix_select,mix_msgfsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MIX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MIX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MIX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MIX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MIX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ FILE *f;
+ int c,i;
+ char *t,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = strrchr (mailbox,'/');
+ unsigned long now = time (NIL);
+ long ret = NIL;
+ /* always create \NoSelect if trailing / */
+ if (s && !s[1]) return dummy_create (stream,mailbox);
+ /* validate name */
+ if (mix_dirfmttest (s ? s + 1 : mailbox))
+ sprintf(tmp,"Can't create mailbox %.80s: invalid MIX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory and metadata */
+ else if (!dummy_create_path (stream,
+ mix_file (file,mix_dir (tmp,mailbox),MIXMETA),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %.80s",mailbox,strerror (errno));
+ else if (!(f = fopen (file,"w")))
+ sprintf (tmp,"Can't re-open metadata %.80s: %.80s",mailbox,
+ strerror (errno));
+ else { /* success, write initial metadata */
+ fprintf (f,SEQFMT,now);
+ fprintf (f,MTAFMT,now,0,now);
+ for (i = 0, c = 'K'; (i < NUSERFLAGS) &&
+ (t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ default_user_flag (i)) && *t; ++i) {
+ putc (c,f); /* write another keyword */
+ fputs (t,f);
+ c = ' '; /* delimiter is now space */
+ }
+ fclose (f);
+ set_mbx_protections (mailbox,file);
+ /* point to suffix */
+ s = file + strlen (file) - (sizeof (MIXMETA) - 1);
+ strcpy (s,MIXINDEX); /* create index */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox index: %.80s",strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ strcpy (s,MIXSTATUS); /* create status */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox status: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ sprintf (s,"%08lx",now);/* message file */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox data: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ ret = LONGT; /* declare success at this point */
+ }
+ }
+ }
+ }
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MIX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int fd = -1;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for delete: %.80s",mailbox);
+ /* delete metadata */
+ else if (unlink (tmp)) sprintf (tmp,"Can't delete mailbox %.80s index: %80s",
+ mailbox,strerror (errno));
+ else {
+ close (fd); /* close descriptor on deleted metadata */
+ /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mix_dirfmttest (d->d_name)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %.80s",
+ mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MIX mail rename mailbox
+ * Accepts: MIX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ if (!mix_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for rename: %.80s",old);
+ else if (mix_dirfmttest ((s = strrchr (newname,'/')) ? s + 1 : newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MIX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mix_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mix_dir (tmp,old); /* build old directory name */
+ mix_dir (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (tmp1,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) {
+ close (fd); /* close descriptor on metadata */
+ return LONGT;
+ }
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ char *src,*dst;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mix file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mix_rselect,alphasort);
+ i < n; ++i) {
+ size_t len = strlen (names[i]->d_name);
+ sprintf (src = (char *) fs_get (srcl + len + 2),"%s/%s",
+ tmp,names[i]->d_name);
+ sprintf (dst = (char *) fs_get (dstl + len + 1),"%s%s",
+ tmp1,names[i]->d_name);
+ if (rename (src,dst)) lasterror = errno;
+ fs_give ((void **) &src);
+ fs_give ((void **) &dst);
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror) errno = lasterror;
+ else {
+ close (fd); /* close descriptor on metadata */
+ return mix_create (NIL,"INBOX");
+ }
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",
+ old,newname,strerror (errno));
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MIX test for mix name
+ * Accepts: candidate directory name
+ * Returns: T if mix file name, NIL otherwise
+ */
+
+int mix_rselect (struct direct *name)
+{
+ return mix_dirfmttest (name->d_name);
+}
+
+/* MIX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mix_open (MAILSTREAM *stream)
+{
+ short silent;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mixproto);
+ if (stream->local) fatal ("mix recycle stream");
+ stream->local = memset (fs_get (sizeof (MIXLOCAL)),0,sizeof (MIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* set stream->mailbox to be directory name */
+ mix_dir (LOCAL->buf,stream->mailbox);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (LOCAL->buf);
+ LOCAL->msgfd = -1; /* currently no file open */
+ if (!(((!stream->rdonly && /* open metadata file */
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDWR,NIL)) >= 0)) ||
+ ((stream->rdonly = T) &&
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDONLY,NIL)) >= 0))) &&
+ !flock (LOCAL->mfd,LOCK_SH))) {
+ MM_LOG ("Error opening mix metadata file",ERROR);
+ mix_abort (stream);
+ stream = NIL; /* open fails */
+ }
+ else { /* metadata open, complete open */
+ LOCAL->index = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXINDEX));
+ LOCAL->status = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXSTATUS));
+ LOCAL->sortcache = cpystr (mix_file (LOCAL->buf,stream->mailbox,
+ MIXSORTCACHE));
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (silent = stream->silent) LOCAL->internal = T;
+ stream->silent = T;
+ if (mix_ping (stream)) { /* do initial ping */
+ /* try burping in case we are exclusive */
+ if (!stream->rdonly) mix_expunge (stream,"",NIL);
+ if (!(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = /* can we create new user flags? */
+ (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T;
+ }
+ else { /* got murdelyzed in ping */
+ mix_abort (stream);
+ stream = NIL;
+ }
+ }
+ return stream; /* return stream to caller */
+}
+
+/* MIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mix_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* burp-only or expunge */
+ mix_expunge (stream,(options & CL_EXPUNGE) ? NIL : "",NIL);
+ mix_abort (stream);
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ /* close current message file if open */
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ /* close current metadata file if open */
+ if (LOCAL->mfd >= 0) close (LOCAL->mfd);
+ if (LOCAL->index) fs_give ((void **) &LOCAL->index);
+ if (LOCAL->status) fs_give ((void **) &LOCAL->status);
+ if (LOCAL->sortcache) fs_give ((void **) &LOCAL->sortcache);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i,j,k;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (length) *length = 0; /* default return */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return "";
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ lseek (LOCAL->msgfd,elt->private.special.offset,L_SET);
+ /* size of special data and header */
+ j = elt->private.msg.header.offset + elt->private.msg.header.text.size;
+ if (j > LOCAL->buflen) { /* is buffer big enough? */
+ /* no, make one that is */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
+ }
+ /* Maybe someday validate internaldate too */
+ /* slurp special data + header, validate */
+ if ((read (LOCAL->msgfd,LOCAL->buf,j) == j) &&
+ !strncmp (LOCAL->buf,MSGTOK,MSGTSZ) &&
+ (elt->private.uid == strtoul ((char *) LOCAL->buf + MSGTSZ,&s,16)) &&
+ (*s++ == ':') && (s = strchr (s,':')) &&
+ (k = strtoul (s+1,&s,16)) && (*s++ == ':') &&
+ (s < (char *) (LOCAL->buf + elt->private.msg.header.offset))) {
+ /* won, set offset and size of message */
+ i = elt->private.msg.header.offset;
+ *length = elt->private.msg.header.text.size;
+ if (k != elt->rfc822_size) {
+ sprintf (tmp,"Inconsistency in mix message size, uid=%lx (%lu != %lu)",
+ elt->private.uid,elt->rfc822_size,k);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* document the problem */
+ LOCAL->buf[100] = '\0'; /* tie off buffer at no more than 100 octets */
+ /* or at newline, whichever is first */
+ if (s = strpbrk (LOCAL->buf,"\015\012")) *s = '\0';
+ sprintf (tmp,"Error reading mix message header, uid=%lx, s=%.0lx, h=%s",
+ elt->private.uid,elt->rfc822_size,LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ *length = i = j = 0; /* default to empty */
+ }
+ LOCAL->buf[j] = '\0'; /* tie off buffer at the end */
+ return (char *) LOCAL->buf + i;
+}
+
+/* MIX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return NIL;
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ /* doing non-peek fetch? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ FILE *idxf; /* yes, process metadata/index/status */
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ elt->seen = T; /* mark as seen */
+ MM_FLAGS (stream,elt->msgno);
+ /* update status file if possible */
+ if (statf && !stream->rdonly) {
+ elt->private.mod = LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,NIL);
+ }
+ if (idxf) fclose (idxf); /* release index and status file */
+ if (statf) fclose (statf);
+ }
+ d.fd = LOCAL->msgfd; /* set up file descriptor */
+ /* offset of message text */
+ d.pos = elt->private.special.offset + elt->private.msg.header.offset +
+ elt->private.msg.header.text.size;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* chunk size */
+ INIT (bs,fd_string,&d,elt->rfc822_size - elt->private.msg.header.text.size);
+ return T;
+}
+
+/* MIX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf,ffkey;
+ long f;
+ short nf;
+ FILE *idxf;
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ unsigned long seq = mix_modseq (LOCAL->statusseq);
+ /* find first free key */
+ for (ffkey = 0; (ffkey < NUSERFLAGS) && stream->user_flags[ffkey]; ++ffkey);
+ /* parse sequence and flags */
+ if (((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf)) {
+ /* alter flags */
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) {
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ }
+ /* update status file after change */
+ if (statf && (seq == LOCAL->statusseq))
+ mix_status_update (stream,statf,NIL);
+ /* update metadata if created a keyword */
+ if ((ffkey < NUSERFLAGS) && stream->user_flags[ffkey] &&
+ !mix_meta_update (stream))
+ MM_LOG ("Error updating mix metadata after keyword creation",ERROR);
+ }
+ if (statf) fclose (statf); /* release status file if still open */
+ if (idxf) fclose (idxf); /* release index file */
+}
+
+/* MIX mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+
+/* MIX mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+/* MIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+static int snarfing = 0; /* lock against recursive snarfing */
+
+long mix_ping (MAILSTREAM *stream)
+{
+ FILE *idxf,*statf;
+ struct stat sbuf;
+ STRING msg;
+ MESSAGECACHE *elt;
+ int mfd,ifd,sfd;
+ unsigned long i,msglen;
+ char *message,date[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *sysibx = NIL;
+ long ret = NIL;
+ long snarfok = LONGT;
+ /* time to snarf? */
+ if (stream->inbox && !stream->rdonly && !snarfing &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (time_t) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ MM_CRITICAL (stream); /* go critical */
+ snarfing = T; /* don't recursively snarf */
+ /* disable APPENDUID/COPYUID callbacks */
+ mail_parameters (NIL,SET_APPENDUID,NIL);
+ mail_parameters (NIL,SET_COPYUID,NIL);
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && sysibx->nmsgs) {
+ /* for each message in sysibx mailbox */
+ for (i = 1; snarfok && (i <= sysibx->nmsgs); ++i)
+ if (!(elt = mail_elt (sysibx,i))->deleted &&
+ (message = mail_fetch_message (sysibx,i,&msglen,FT_PEEK)) &&
+ msglen) {
+ mail_date (date,elt); /* make internal date string */
+ /* make flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '(';
+ strcat (flags,")");
+ INIT (&msg,mail_string,message,msglen);
+ if (snarfok = mail_append_full (stream,"INBOX",flags,date,&msg)) {
+ char sequence[15];
+ sprintf (sequence,"%lu",i);
+ mail_flag (sysibx,sequence,"\\Deleted",ST_SET);
+ }
+ }
+
+ /* now expunge all those messages */
+ if (snarfok) mail_expunge (sysibx);
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail at message: %lu",i - 1);
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ if (sysibx) mail_close (sysibx);
+ /* reenable APPENDUID/COPYUID */
+ mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ mail_parameters (NIL,SET_COPYUID,(void *) cu);
+ snarfing = NIL; /* no longer snarfing */
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ /* expunging OK if global flag set */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* process metadata/index/status */
+ if (statf = mix_parse (stream,&idxf,LONGT,
+ (LOCAL->internal ? NIL : LONGT))) {
+ fclose (statf); /* just close the status file */
+ ret = LONGT; /* declare success */
+ }
+ if (idxf) fclose (idxf); /* release index file */
+ LOCAL->expok = NIL; /* expunge no longer OK */
+ if (!ret) mix_abort (stream); /* murdelyze stream if ping fails */
+ return ret;
+}
+
+
+/* MIX mail checkpoint mailbox (burp only)
+ * Accepts: MAIL stream
+ */
+
+void mix_check (MAILSTREAM *stream)
+{
+ if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Checkpoint ignored on readonly mailbox",NIL);
+ /* do burp-only expunge action */
+ if (mix_expunge (stream,"",NIL)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL, empty string for burp only
+ * expunge options
+ * Returns: T on success, NIL if failure
+ */
+
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ FILE *idxf = NIL;
+ FILE *statf = NIL;
+ MESSAGECACHE *elt;
+ int ifd,sfd;
+ long ret;
+ unsigned long i;
+ unsigned long nexp = 0;
+ unsigned long reclaimed = 0;
+ int burponly = (sequence && !*sequence);
+ LOCAL->expok = T; /* expunge during ping is OK */
+ if (!(ret = burponly || !sequence ||
+ ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) || stream->rdonly);
+ /* read index and open status exclusive */
+ else if (statf = mix_parse (stream,&idxf,LONGT,
+ LOCAL->internal ? NIL : LONGT)) {
+ /* expunge unless just burping */
+ if (!burponly) for (i = 1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* need to expunge this message? */
+ if (sequence ? elt->sequence : elt->deleted) {
+ ++nexp; /* yes, make it so */
+ mail_expunged (stream,i);
+ }
+ else ++i; /* otherwise advance to next message */
+ }
+
+ /* burp if can get exclusive access */
+ if (!flock (LOCAL->mfd,LOCK_EX|LOCK_NB)) {
+ void *a;
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mix_select,mix_msgfsort);
+ if (nfiles > 0) { /* if have message files */
+ MIXBURP *burp,*cur;
+ /* initialize burp list */
+ for (i = 0, burp = cur = NIL; i < nfiles; ++i) {
+ MIXBURP *nxt = (MIXBURP *) memset (fs_get (sizeof (MIXBURP)),0,
+ sizeof (MIXBURP));
+ /* another file found */
+ if (cur) cur = cur->next = nxt;
+ else cur = burp = nxt;
+ cur->name = names[i]->d_name;
+ cur->fileno = strtoul (cur->name + sizeof (MIXNAME) - 1,NIL,16);
+ cur->tail = &cur->set;
+ fs_give ((void **) &names[i]);
+ }
+ /* now load ranges */
+ for (i = 1, cur = burp; ret && (i <= stream->nmsgs); i++) {
+ /* is this message in current set? */
+ elt = mail_elt (stream,i);
+ if (cur && (elt->private.spare.data != cur->fileno)) {
+ /* restart if necessary */
+ if (elt->private.spare.data < cur->fileno) cur = burp;
+ /* hunt for appropriate mailbox */
+ while (cur && (elt->private.spare.data > cur->fileno))
+ cur = cur->next;
+ /* ought to have found it now... */
+ if (cur && (elt->private.spare.data != cur->fileno)) cur = NIL;
+ }
+ /* if found, add to set */
+ if (cur) ret = mix_addset (&cur->tail,elt->private.special.offset,
+ elt->private.msg.header.offset +
+ elt->rfc822_size);
+ else { /* uh-oh */
+ sprintf (LOCAL->buf,"Can't locate mix message file %.08lx",
+ elt->private.spare.data);
+ MM_LOG (LOCAL->buf,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret) /* if no errors, burp all files */
+ for (cur = burp; ret && cur; cur = cur->next) {
+ /* if non-empty, burp it */
+ if (cur->set.last) ret = mix_burp (stream,cur,&reclaimed);
+ /* empty, delete it unless new msg file */
+ else if (mix_file_data (LOCAL->buf,stream->mailbox,cur->fileno) &&
+ ((cur->fileno == LOCAL->newmsg) ?
+ truncate (LOCAL->buf,0) : unlink (LOCAL->buf))) {
+ sprintf (LOCAL->buf,
+ "Can't delete empty message file %.80s: %.80s",
+ cur->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ }
+ else MM_LOG ("No mix message files found during expunge",WARN);
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ }
+
+ /* either way, re-acquire shared lock */
+ if (flock (LOCAL->mfd,LOCK_SH|LOCK_NB))
+ fatal ("Unable to re-acquire metadata shared lock!");
+ /* Do this step even if ret is NIL (meaning some burp problem)! */
+ if (nexp || reclaimed) { /* rewrite index and status if changed */
+ LOCAL->indexseq = mix_modseq (LOCAL->indexseq);
+ if (mix_index_update (stream,idxf,NIL)) {
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ /* set failure if update fails */
+ ret = mix_status_update (stream,statf,NIL);
+ }
+ }
+ }
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ LOCAL->expok = NIL; /* cancel expok */
+ if (ret) { /* only if success */
+ char *s = NIL;
+ if (nexp) sprintf (s = LOCAL->buf,"Expunged %lu messages",nexp);
+ else if (reclaimed)
+ sprintf (s=LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ else if (!burponly)
+ s = stream->rdonly ? "Expunge ignored on readonly mailbox" :
+ "No messages deleted, so no update needed";
+ if (s) MM_LOG (s,(long) NIL);
+ }
+ return ret;
+}
+
+/* MIX test for message file name
+ * Accepts: candidate directory name
+ * Returns: T if message file name, NIL otherwise
+ *
+ * ".mix" with no suffix was used by experimental versions
+ */
+
+int mix_select (struct direct *name)
+{
+ char c,*s;
+ /* make sure name has prefix */
+ if (mix_dirfmttest (name->d_name)) {
+ for (c = *(s = name->d_name + sizeof (MIXNAME) - 1); c && isxdigit (c);
+ c = *s++);
+ if (!c) return T; /* all-hex or no suffix */
+ }
+ return NIL; /* not suffix or non-hex */
+}
+
+
+/* MIX msg file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: -1 if d1 < d2, 0 if d1 == d2, 1 d1 > d2
+ */
+
+int mix_msgfsort (const void *d1,const void *d2)
+{
+ char *n1 = (*(struct direct **) d1)->d_name + sizeof (MIXNAME) - 1;
+ char *n2 = (*(struct direct **) d2)->d_name + sizeof (MIXNAME) - 1;
+ return compare_ulong (*n1 ? strtoul (n1,NIL,16) : 0,
+ *n2 ? strtoul (n2,NIL,16) : 0);
+}
+
+
+/* MIX add a range to a set
+ * Accepts: pointer to set to add
+ * start of set
+ * size of set
+ * Returns: T if success, set updated, NIL otherwise
+ */
+
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size)
+{
+ SEARCHSET *s = *set;
+ if (start < s->last) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Backwards-running mix index %lu < %lu",start,s->last);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* range initially empty? */
+ if (!s->last) s->first = start;
+ else if (start > s->last) /* no, start new range if can't append */
+ (*set = s = s->next = mail_newsearchset ())->first = start;
+ s->last = start + size; /* end of current range */
+ return LONGT;
+}
+
+/* MIX burp message file
+ * Accepts: MAIL stream
+ * current burp block for this message
+ * Returns: T if successful, NIL if failed
+ */
+
+static char *staterr = "Error in stat of mix message file %.80s: %.80s";
+static char *truncerr = "Error truncating mix message file %.80s: %.80s";
+
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed)
+{
+ MESSAGECACHE *elt;
+ SEARCHSET *set;
+ struct stat sbuf;
+ off_t rpos,wpos;
+ size_t size,wsize,wpending,written;
+ int fd;
+ FILE *f;
+ void *s;
+ unsigned long i;
+ long ret = NIL;
+ /* build file name */
+ mix_file_data (LOCAL->buf,stream->mailbox,burp->fileno);
+ /* need to burp at start or multiple ranges? */
+ if (!burp->set.first && !burp->set.next) {
+ /* easy case, single range at start of file */
+ if (stat (LOCAL->buf,&sbuf)) {
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* is this range sane? */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* if matches range then no burp needed! */
+ if (burp->set.last == sbuf.st_size) ret = LONGT;
+ /* just need to remove cruft at end */
+ else if (ret = !truncate (LOCAL->buf,burp->set.last))
+ *reclaimed += sbuf.st_size - burp->set.last;
+ else {
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ }
+ }
+ /* have to do more work, get the file */
+ else if (((fd = open (LOCAL->buf,O_RDWR,NIL)) < 0) ||
+ !(f = fdopen (fd,"r+b"))) {
+ sprintf (LOCAL->buf,"Error opening mix message file %.80s: %.80s",
+ burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ if (fd >= 0) close (fd); /* in case fdopen() failure */
+ }
+ else if (fstat (fd,&sbuf)) { /* get file size */
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ }
+
+ /* only if sane */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* make sure each range starts with token */
+ for (set = &burp->set; set; set = set->next)
+ if (fseek (f,set->first,SEEK_SET) ||
+ (fread (LOCAL->buf,1,MSGTSZ,f) != MSGTSZ) ||
+ strncmp (LOCAL->buf,MSGTOK,MSGTSZ)) {
+ sprintf (LOCAL->buf,"Bad message token in mix message file at %lu",
+ set->first);
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ return NIL; /* burp fails for this file */
+ }
+ /* burp out each old message */
+ for (set = &burp->set, wpos = 0; set; set = set->next) {
+ /* move down this range */
+ for (rpos = set->first, size = set->last - set->first;
+ size; size -= wsize) {
+ if (rpos != wpos) { /* data to skip at start? */
+ /* no, slide this buffer down */
+ wsize = min (size,LOCAL->buflen);
+ /* failure is not an option here */
+ while (fseek (f,rpos,SEEK_SET) ||
+ (fread (LOCAL->buf,1,wsize,f) != wsize)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* nor here */
+ while (fseek (f,wpos,SEEK_SET)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* and especially not here */
+ for (s = LOCAL->buf, wpending = wsize; wpending; wpending -= written)
+ if (!(written = fwrite (LOCAL->buf,1,wpending,f))) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+ else wsize = size; /* nothing to skip, say we wrote it all */
+ rpos += wsize; wpos += wsize;
+ }
+ }
+
+ while (fflush (f)) { /* failure also not an option here... */
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ if (ftruncate (fd,wpos)) { /* flush cruft at end of file */
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ else *reclaimed += rpos - wpos;
+ ret = !fclose (f); /* close file */
+ /* slide down message positions in index */
+ for (i = 1,rpos = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->private.spare.data == burp->fileno) {
+ elt->private.special.offset = rpos;
+ rpos += elt->private.msg.header.offset + elt->rfc822_size;
+ }
+ /* debugging */
+ if (rpos != wpos) fatal ("burp size consistency check!");
+ }
+ return ret;
+}
+
+
+/* MIX burp sanity check to make sure not burping off end of file
+ * Accepts: burp set
+ * file size
+ * file name
+ * Returns: T if sane, NIL if insane
+ */
+
+long mix_burp_check (SEARCHSET *set,size_t size,char *file)
+{
+ do if (set->last > size) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unexpected short mix message file %.80s %lu < %lu",
+ file,size,set->last);
+ MM_LOG (tmp,ERROR);
+ return NIL; /* don't burp this file at all */
+ } while (set = set->next);
+ return LONGT;
+}
+
+/* MIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ char tmp[2*MAILTMPLEN];
+ long ret = mix_isvalid (mailbox,LOCAL->buf);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ MAILSTREAM *astream = NIL;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (!ret) switch (errno) { /* make sure valid mailbox */
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ break;
+ }
+ /* get sequence to copy */
+ else if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append */
+ else if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ unsigned long newsize,hdrsize,size;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+
+ MM_CRITICAL (stream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* calculate size that will be added */
+ for (i = 1, newsize = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence)
+ newsize += hdrsize + elt->rfc822_size;
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,newsize)) {
+ char *t;
+ unsigned long j,uid,uidv;
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); ++i)
+ if (((elt = mail_elt (stream,i))->sequence) && elt->rfc822_size) {
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) ||
+ (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,
+ stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) >= 0)
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ if (LOCAL->msgfd < 0) ret = NIL;
+ else { /* got file */
+ d.fd = LOCAL->msgfd;/* set up file descriptor */
+ /* start of message */
+ d.pos = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,elt->rfc822_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if ((t = stream->user_flags[find_rightmost_bit (&j)]) && *t)
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* wrap list */
+ strcat (tmp,")");
+ /* if append OK, add to source set */
+ if ((ret = mix_append_msg (astream,msgf,tmp,elt,&st,dest,
+ seq)) && source)
+ mail_append_set (source,mail_uid (stream,i));
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if (ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT))) {
+ /* success, delete if doing a move */
+ if (options & CP_MOVE)
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T;
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ /* done with status file now */
+ mix_status_update (astream,statf,LONGT);
+ /* return sets if doing COPYUID */
+ if (cu) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ source = dest = NIL; /* don't free these sets now */
+ }
+ }
+ else { /* error */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message copy failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert file */
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any sets remaining */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening copy message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (stream);
+ }
+ else MM_LOG ("Can't open copy mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ /* finished with append stream */
+ if (astream) mail_close (astream);
+ return ret; /* return state */
+}
+
+/* MIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ STRING *message;
+ char *flags,*date,tmp[MAILTMPLEN];
+ /* N.B.: can't use LOCAL->buf for tmp */
+ long ret = mix_isvalid (mailbox,tmp);
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mixproto);
+ if (!ret) switch (errno) { /* if not valid mailbox */
+ case ENOENT: /* no such file? */
+ if (ret = compare_cstring (mailbox,"INBOX") ?
+ NIL : mix_create (NIL,"INBOX"))
+ break;
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ break;
+ default:
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ }
+
+ /* get first message */
+ if (ret && MM_APPEND (af) (stream,data,&flags,&date,&message)) {
+ MAILSTREAM *astream;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long size,hdrsize;
+ MESSAGECACHE elt;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+ MM_CRITICAL (astream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,hdrsize + SIZE (message))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ while (ret && message) {/* while good to go and have messages */
+ errno = NIL; /* in case one of these causes failure */
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else {
+ if (!date) { /* if date not specified, use now */
+ internal_date (tmp);
+ mail_parse_date (&elt,tmp);
+ }
+ ret = mix_append_msg (astream,msgf,flags,&elt,message,dst,seq) &&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if ((ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT) &&
+ mix_status_update (astream,statf,LONGT))) && au) {
+ (*au) (mailbox,astream->uid_validity,dst);
+ dst = NIL; /* don't free this set now */
+ }
+ }
+ else { /* failure */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message append failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert all writes to file*/
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any set remaining */
+ mail_free_searchset (&dst);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening append message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ }
+ else MM_LOG ("Can't open append mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ if (astream) mail_close (astream);
+ }
+ return ret;
+}
+
+/* MIX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * modseq of message
+ * Returns: T if success, NIL if failure
+ */
+
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq)
+{
+ MESSAGECACHE *elt;
+ int c,cs;
+ unsigned long i,j,k,uf,hoff;
+ long sf;
+ stream->kwd_create = NIL; /* don't copy unknown keywords */
+ sf = mail_parse_flags (stream,flags,&uf);
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* assign new UID from metadata */
+ (elt = mail_elt (stream,stream->nmsgs))->private.uid = ++stream->uid_last;
+ elt->private.mod = seq; /* set requested modseq in status */
+ elt->rfc822_size = SIZE (msg);/* copy message size and date to index */
+ elt->year = delt->year; elt->month = delt->month; elt->day = delt->day;
+ elt->hours = delt->hours; elt->minutes = delt->minutes;
+ elt->seconds = delt->seconds; elt->zoccident = delt->zoccident;
+ elt->zhours = delt->zhours; elt->zminutes = delt->zminutes;
+ /*
+ * Do NOT set elt->valid here! mix_status_update() uses it to determine
+ * whether a message should be marked as old.
+ */
+ if (sf&fSEEN) elt->seen = T; /* copy flags to status */
+ if (sf&fDELETED) elt->deleted = T;
+ if (sf&fFLAGGED) elt->flagged = T;
+ if (sf&fANSWERED) elt->answered = T;
+ if (sf&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ /* message is in new message file */
+ elt->private.spare.data = LOCAL->newmsg;
+
+ /* offset to message internal header */
+ elt->private.special.offset = ftell (f);
+ /* build header for message */
+ fprintf (f,MSRFMT,MSGTOK,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size);
+ /* offset to header from internal header */
+ elt->private.msg.header.offset = ftell (f) - elt->private.special.offset;
+ for (cs = 0; SIZE (msg); ) { /* copy message */
+ if (elt->private.msg.header.text.size) {
+ if (msg->cursize) /* blat entire chunk if have it */
+ for (j = msg->cursize; j; j -= k)
+ if (!(k = fwrite (msg->curpos,1,j,f))) return NIL;
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ }
+ else { /* still searching for delimiter */
+ c = 0xff & SNX (msg); /* get source character */
+ if (putc (c,f) == EOF) return NIL;
+ switch (cs) { /* decide what to do based on state */
+ case 0: /* previous char ordinary */
+ if (c == '\015') cs = 1;/* advance if CR */
+ break;
+ case 1: /* previous CR, advance if LF */
+ cs = (c == '\012') ? 2 : 0;
+ break;
+ case 2: /* previous CRLF, advance if CR */
+ cs = (c == '\015') ? 3 : 0;
+ break;
+ case 3: /* previous CRLFCR, done if LF */
+ if (c == '\012') elt->private.msg.header.text.size =
+ elt->rfc822_size - SIZE (msg);
+ cs = 0; /* reset mechanism */
+ break;
+ }
+ }
+ }
+ /* if no delimiter, header is entire msg */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* add this message to set */
+ mail_append_set (set,elt->private.uid);
+ return LONGT; /* success */
+}
+
+/* MIX mail read metadata, index, and status
+ * Accepts: MAIL stream
+ * returned index file
+ * index file flags (non-NIL if want to add/remove messages)
+ * status file flags (non-NIL if want to update elt->valid and old)
+ * Returns: open status file, or NIL if failure
+ *
+ * Note that this routine can return an open index file even if it fails!
+ */
+
+static char *shortmsg =
+ "message %lu (UID=%.08lx) truncated by %lu byte(s) (%lu < %lu)";
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags)
+{
+ int fd;
+ unsigned long i;
+ char *s,*t;
+ struct stat sbuf;
+ FILE *statf = NIL;
+ short metarepairneeded = 0;
+ short indexrepairneeded = 0;
+ short silent = stream->silent;
+ *idxf = NIL; /* in case error */
+ /* readonly means no updates */
+ if (stream->rdonly) iflags = sflags = NIL;
+ /* open index file */
+ if ((fd = open (LOCAL->index,iflags ? O_RDWR : O_RDONLY,NIL)) < 0)
+ MM_LOG ("Error opening mix index file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,iflags ? LOCK_EX : LOCK_SH) &&
+ !(*idxf = fdopen (fd,iflags ? "r+b" : "rb"))) {
+ MM_LOG ("Error obtaining stream on mix index file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+
+ /* slurp metadata */
+ else if (s = mix_meta_slurp (stream,&i)) {
+ unsigned long j = 0; /* non-zero if UIDVALIDITY/UIDLAST changed */
+ if (i != LOCAL->metaseq) { /* metadata changed? */
+ char *t,*k;
+ LOCAL->metaseq = i; /* note new metadata sequence */
+ while (s && *s) { /* parse entire metadata file */
+ /* locate end of line */
+ if (s = strstr (t = s,"\015\012")) {
+ *s = '\0'; /* tie off line */
+ s += 2; /* skip past CRLF */
+ switch (*t++) { /* parse line */
+ case 'V': /* UIDVALIDITY */
+ if (!isxdigit (*t) || !(i = strtoul (t,&t,16))) {
+ MM_LOG ("Error in mix metadata file UIDVALIDITY record",ERROR);
+ return NIL; /* give up */
+ }
+ if (i != stream->uid_validity) j = stream->uid_validity = i;
+ break;
+ case 'L': /* new UIDLAST */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file UIDLAST record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ j = stream->uid_last = i;
+ break;
+ case 'N': /* new message file */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file new msg record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ LOCAL->newmsg = i;
+ break;
+ case 'K': /* new keyword list */
+ for (i = 0; t && *t && (i < NUSERFLAGS); ++i) {
+ if (t = strchr (k = t,' ')) *t++ = '\0';
+ /* make sure keyword non-empty */
+ if (*k && (strlen (k) <= MAXUSERFLAG)) {
+ /* in case value changes (shouldn't happen) */
+ if (stream->user_flags[i] && strcmp (stream->user_flags[i],k)){
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"flag rename old=%.80s new=%.80s",
+ stream->user_flags[i],k);
+ MM_LOG (tmp,WARN);
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (k);
+ }
+ else break; /* empty keyword */
+ }
+ if ((i < NUSERFLAGS) && stream->user_flags[i]) {
+ MM_LOG ("Error in mix metadata file keyword record",ERROR);
+ return NIL; /* give up */
+ }
+ else if (i == NUSERFLAGS) stream->kwd_create = NIL;
+ break;
+ }
+ }
+ if (t && *t) { /* junk in line */
+ MM_LOG ("Error in mix metadata record",ERROR);
+ return NIL; /* give up */
+ }
+ }
+ }
+
+ /* get sequence */
+ if (!(i = mix_read_sequence (*idxf)) || (i < LOCAL->indexseq)) {
+ MM_LOG ("Error in mix index file sequence record",ERROR);
+ return NIL; /* give up */
+ }
+ /* sequence changed from last time? */
+ else if (j || (i > LOCAL->indexseq)) {
+ unsigned long uid,nmsgs,curfile,curfilesize,curpos;
+ char *t,*msg,tmp[MAILTMPLEN];
+ /* start with no messages */
+ curfile = curfilesize = curpos = nmsgs = 0;
+ /* update sequence iff expunging OK */
+ if (LOCAL->expok) LOCAL->indexseq = i;
+ /* get first elt */
+ while ((s = mix_read_record (*idxf,LOCAL->buf,LOCAL->buflen,"index")) &&
+ *s)
+ switch (*s) {
+ case ':': /* message record */
+ if (!(isxdigit (*++s) && (uid = strtoul (s,&t,16)))) msg = "UID";
+ else if (!((*t++ == ':') && isdigit (*t) && isdigit (t[1]) &&
+ isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) &&
+ isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) &&
+ isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) &&
+ isdigit (t[11]) && isdigit (t[12]) && isdigit (t[13]) &&
+ ((t[14] == '+') || (t[14] == '-')) &&
+ isdigit (t[15]) && isdigit (t[16]) && isdigit (t[17]) &&
+ isdigit (t[18]))) msg = "internaldate";
+ else if ((*(s = t+19) != ':') || !isxdigit (*++s)) msg = "size";
+ else {
+ unsigned int y = (((*t - '0') * 1000) + ((t[1] - '0') * 100) +
+ ((t[2] - '0') * 10) + t[3] - '0') - BASEYEAR;
+ unsigned int m = ((t[4] - '0') * 10) + t[5] - '0';
+ unsigned int d = ((t[6] - '0') * 10) + t[7] - '0';
+ unsigned int hh = ((t[8] - '0') * 10) + t[9] - '0';
+ unsigned int mm = ((t[10] - '0') * 10) + t[11] - '0';
+ unsigned int ss = ((t[12] - '0') * 10) + t[13] - '0';
+ unsigned int z = (t[14] == '-') ? 1 : 0;
+ unsigned int zh = ((t[15] - '0') * 10) + t[16] - '0';
+ unsigned int zm = ((t[17] - '0') * 10) + t[18] - '0';
+ unsigned long size = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long file = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long pos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hpos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hsiz = strtoul (s,&s,16);
+ if (uid > stream->uid_last) {
+ sprintf (tmp,"mix index invalid UID (%08lx < %08lx)",
+ uid,stream->uid_last);
+ if (stream->rdonly) {
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ strcat (tmp,", repaired");
+ MM_LOG (tmp,WARN);
+ stream->uid_last = uid;
+ metarepairneeded = T;
+ }
+
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ MESSAGECACHE *elt;
+ ++nmsgs; /* this is another mesage */
+ /* within current known range of messages? */
+ while (nmsgs <= stream->nmsgs) {
+ /* yes, get corresponding elt */
+ elt = mail_elt (stream,nmsgs);
+ /* existing message with matching data? */
+ if (uid == elt->private.uid) {
+ /* beware of Dracula's resurrection */
+ if (elt->private.ghost) {
+ sprintf (tmp,"mix index data unexpunged UID: %lx",
+ uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* also of static data changing */
+ if ((size != elt->rfc822_size) ||
+ (file != elt->private.spare.data) ||
+ (pos != elt->private.special.offset) ||
+ (hpos != elt->private.msg.header.offset) ||
+ (hsiz != elt->private.msg.header.text.size) ||
+ (y != elt->year) || (m != elt->month) ||
+ (d != elt->day) || (hh != elt->hours) ||
+ (mm != elt->minutes) || (ss != elt->seconds) ||
+ (z != elt->zoccident) || (zh != elt->zhours) ||
+ (zm != elt->zminutes)) {
+ sprintf (tmp,"mix index data mismatch: %lx",uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ break;
+ }
+ /* existing msg with lower UID is expunged */
+ else if (uid > elt->private.uid) {
+ if (LOCAL->expok) mail_expunged (stream,nmsgs);
+ else {/* message expunged, but not yet for us */
+ ++nmsgs;
+ elt->private.ghost = T;
+ }
+ }
+ else { /* unexpected message record */
+ sprintf (tmp,"mix index UID mismatch (%lx < %lx)",
+ uid,elt->private.uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* time to create a new message? */
+ if (nmsgs > stream->nmsgs) {
+ /* defer announcing until later */
+ stream->silent = T;
+ mail_exists (stream,nmsgs);
+ stream->silent = silent;
+ (elt = mail_elt (stream,nmsgs))->recent = T;
+ elt->private.uid = uid; elt->rfc822_size = size;
+ elt->private.spare.data = file;
+ elt->private.special.offset = pos;
+ elt->private.msg.header.offset = hpos;
+ elt->private.msg.header.text.size = hsiz;
+ elt->year = y; elt->month = m; elt->day = d;
+ elt->hours = hh; elt->minutes = mm;
+ elt->seconds = ss; elt->zoccident = z;
+ elt->zhours = zh; elt->zminutes = zm;
+ /* message in same file? */
+ if (curfile == file) {
+ if (pos < curpos) {
+ MESSAGECACHE *plt = mail_elt (stream,elt->msgno-1);
+ /* uh-oh, calculate delta? */
+ i = curpos - pos;
+ sprintf (tmp,shortmsg,plt->msgno,plt->private.uid,
+ i,pos,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < plt->rfc822_size)) {
+ plt->rfc822_size -= i;
+ if (plt->rfc822_size <
+ plt->private.msg.header.text.size)
+ plt->private.msg.header.text.size =
+ plt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* new file, restart */
+ if (stat (mix_file_data (LOCAL->buf,stream->mailbox,
+ curfile = file),&sbuf)) {
+ sprintf (tmp,"Missing mix data file: %.500s",
+ LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ curfile = file;
+ curfilesize = sbuf.st_size;
+ }
+
+ /* position of message in file */
+ curpos = pos + elt->private.msg.header.offset +
+ elt->rfc822_size;
+ /* short file? */
+ if (curfilesize < curpos) {
+ /* uh-oh, calculate delta? */
+ i = curpos - curfilesize;
+ sprintf (tmp,shortmsg,elt->msgno,elt->private.uid,
+ i,curfilesize,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < elt->rfc822_size)) {
+ elt->rfc822_size -= i;
+ if (elt->rfc822_size <
+ elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size =
+ elt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ break;
+ }
+ else msg = "expansion";
+ }
+ else msg = "header size";
+ }
+ else msg = "header position";
+ }
+ else msg = "message position";
+ }
+ else msg = "file#";
+ }
+ sprintf (tmp,"Error in %s in mix index file: %.500s",msg,s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Unknown record in mix index file: %.500s",s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if (!s) return NIL; /* barfage from mix_read_record() */
+ /* expunge trailing messages not in index */
+ if (LOCAL->expok) while (nmsgs < stream->nmsgs)
+ mail_expunged (stream,stream->nmsgs);
+ }
+
+ /* repair metadata and index if needed */
+ if ((metarepairneeded ? mix_meta_update (stream) : T) &&
+ (indexrepairneeded ? mix_index_update (stream,*idxf,NIL) : T)) {
+ MESSAGECACHE *elt;
+ int fd;
+ unsigned long uid,uf,sf,mod;
+ char *s;
+ int updatep = NIL;
+ /* open status file */
+ if ((fd = open (LOCAL->status,
+ stream->rdonly ? O_RDONLY : O_RDWR,NIL)) < 0)
+ MM_LOG ("Error opening mix status file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,stream->rdonly ? LOCK_SH : LOCK_EX) &&
+ !(statf = fdopen (fd,stream->rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix status file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ /* get sequence */
+ else if (!(i = mix_read_sequence (statf)) ||
+ ((i < LOCAL->statusseq) && stream->nmsgs && (i != 1))) {
+ sprintf (LOCAL->buf,
+ "Error in mix status sequence record, i=%lx, seq=%lx",
+ i,LOCAL->statusseq);
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sequence changed from last time? */
+ else if (i != LOCAL->statusseq) {
+ /* update sequence, get first elt */
+ if (i > LOCAL->statusseq) LOCAL->statusseq = i;
+ if (stream->nmsgs) {
+ elt = mail_elt (stream,i = 1);
+
+ /* read message records */
+ while ((t = s = mix_read_record (statf,LOCAL->buf,LOCAL->buflen,
+ "status")) && *s && (*s++ == ':') &&
+ isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ uf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ mod = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ /* need to move ahead to next elt? */
+ while ((uid > elt->private.uid) && (i < stream->nmsgs))
+ elt = mail_elt (stream,++i);
+ /* update elt if altered */
+ if ((uid == elt->private.uid) &&
+ (!elt->valid || (mod != elt->private.mod))) {
+ elt->user_flags = uf;
+ elt->private.mod = mod;
+ elt->seen = (sf & fSEEN) ? T : NIL;
+ elt->deleted = (sf & fDELETED) ? T : NIL;
+ elt->flagged = (sf & fFLAGGED) ? T : NIL;
+ elt->answered = (sf & fANSWERED) ? T : NIL;
+ elt->draft = (sf & fDRAFT) ? T : NIL;
+ /* announce if altered existing message */
+ if (elt->valid) MM_FLAGS (stream,elt->msgno);
+ /* first time, is old message? */
+ else if (sf & fOLD) {
+ /* yes, clear recent and set valid */
+ elt->recent = NIL;
+ elt->valid = T;
+ }
+ /* recent, allowed to update its status? */
+ else if (sflags) {
+ /* yes, set valid and check in status */
+ elt->valid = T;
+ elt->private.mod = mix_modseq (elt->private.mod);
+ updatep = T;
+ }
+ /* leave valid unset and recent if sflags not set */
+ }
+ continue; /* everything looks good */
+ }
+ }
+ }
+ }
+ break; /* error somewhere */
+ }
+
+ if (t && *t) { /* non-null means bogus record */
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Error in mix status file message record%s: %.80s",
+ stream->rdonly ? "" : ", fixing",t);
+ MM_LOG (msg,WARN);
+ /* update it if not readonly */
+ if (!stream->rdonly) updatep = T;
+ }
+ if (updatep) { /* need to update? */
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,LONGT);
+ }
+ }
+ }
+ }
+ }
+ if (statf) { /* still happy? */
+ unsigned long j;
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ for (i = 1, j = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->recent) ++j;
+ mail_recent (stream,j);
+ }
+ return statf;
+}
+
+/* MIX metadata file routines */
+
+/* MIX read metadata
+ * Accepts: MAIL stream
+ * return pointer for modseq
+ * Returns: pointer to metadata after modseq or NIL if failure
+ */
+
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq)
+{
+ struct stat sbuf;
+ char *s;
+ char *ret = NIL;
+ if (fstat (LOCAL->mfd,&sbuf))
+ MM_LOG ("Error obtaining size of mix metatdata file",ERROR);
+ if (sbuf.st_size > LOCAL->buflen) {
+ /* should be just a few dozen bytes */
+ if (sbuf.st_size > METAMAX) fatal ("absurd mix metadata file size");
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
+ }
+ /* read current metadata file */
+ LOCAL->buf[sbuf.st_size] = '\0';
+ if (lseek (LOCAL->mfd,0,L_SET) ||
+ (read (LOCAL->mfd,s = LOCAL->buf,sbuf.st_size) != sbuf.st_size))
+ MM_LOG ("Error reading mix metadata file",ERROR);
+ else if ((*s != 'S') || !isxdigit (s[1]) ||
+ ((*seq = strtoul (s+1,&s,16)) < LOCAL->metaseq) ||
+ (*s++ != '\015') || (*s++ != '\012'))
+ MM_LOG ("Error in mix metadata file sequence record",ERROR);
+ else ret = s;
+ return ret;
+}
+
+/* MIX update metadata
+ * Accepts: MAIL stream
+ * Returns: T on success, NIL if error
+ *
+ * Index MUST be locked!!
+ */
+
+long mix_meta_update (MAILSTREAM *stream)
+{
+ long ret;
+ /* do nothing if stream readonly */
+ if (stream->rdonly) ret = LONGT;
+ else {
+ unsigned char c,*s,*ss,*t;
+ unsigned long i;
+ /* The worst-case metadata is limited to:
+ * 4 * (1 + 8 + 2) + (NUSERFLAGS * (MAXUSERFLAG + 1))
+ * which comes out to 1994 octets. This is much smaller than the normal
+ * CHUNKSIZE definition of 64K, and CHUNKSIZE is the smallest size of
+ * LOCAL->buf.
+ *
+ * If more stuff gets added to the metadata, or if you change the value
+ * of NUSERFLAGS, MAXUSERFLAG or CHUNKSIZE, be sure to recalculate the
+ * above assertation.
+ */
+ sprintf (LOCAL->buf,SEQFMT,LOCAL->metaseq = mix_modseq (LOCAL->metaseq));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),MTAFMT,
+ stream->uid_validity,stream->uid_last,LOCAL->newmsg);
+ for (i = 0, c = 'K', s = ss = LOCAL->buf + strlen (LOCAL->buf);
+ (i < NUSERFLAGS) && (t = stream->user_flags[i]); ++i) {
+ if (!*t) fatal ("impossible empty keyword");
+ *s++ = c; /* write delimiter */
+ while (*t) *s++ = *t++; /* write keyword */
+ c = ' '; /* delimiter is now space */
+ }
+ if (s != ss) { /* tie off keywords line */
+ *s++ = '\015'; *s++ = '\012';
+ }
+ /* calculate length of metadata */
+ if ((i = s - LOCAL->buf) > LOCAL->buflen)
+ fatal ("impossible buffer overflow");
+ lseek (LOCAL->mfd,0,L_SET); /* rewind file */
+ /* write new metadata */
+ ret = (write (LOCAL->mfd,LOCAL->buf,i) == i) ? LONGT : NIL;
+ ftruncate (LOCAL->mfd,i); /* and tie off at that point */
+ }
+ return ret;
+}
+
+/* MIX index file routines */
+
+
+/* MIX update index
+ * Accepts: MAIL stream
+ * open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag)
+{
+ unsigned long i;
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* Winston Smith's first dairy entry */
+ sprintf (tmp,IXRFMT,0,14,4,4,13,0,0,'+',0,0,0,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ /* calculate file size we need */
+ sprintf (tmp,SEQFMT,LOCAL->indexseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (idxf),&sbuf)) {
+ MM_LOG ("Error getting size of mix index file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (idxf,0,SEEK_END) || (fwrite (buf,1,size,idxf) != size) ||
+ fflush (idxf)) {
+ fseek (idxf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (idxf),sbuf.st_size);
+ MM_LOG ("Error extending mix index file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (idxf); /* let's start at the very beginning */
+ /* write modseq first */
+ fprintf (idxf,SEQFMT,LOCAL->indexseq);
+ /* then write all messages */
+ for (i = 1; ret && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (idxf,IXRFMT,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size,elt->private.spare.data,
+ elt->private.special.offset,
+ elt->private.msg.header.offset,
+ elt->private.msg.header.text.size);
+ if (ferror (idxf)) {
+ MM_LOG ("Error updating mix index file",ERROR);
+ ret = NIL;
+ }
+ }
+ if (fflush (idxf)) {
+ MM_LOG ("Error flushing mix index file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (idxf),ftell (idxf));
+ }
+ }
+ return ret;
+}
+
+/* MIX status file routines */
+
+
+/* MIX update status
+ * Accepts: MAIL stream
+ * pointer to open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* number of living messages */
+ sprintf (tmp,STRFMT,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ sprintf (tmp,SEQFMT,LOCAL->statusseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (statf),&sbuf)) {
+ MM_LOG ("Error getting size of mix status file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (statf,0,SEEK_END) || (fwrite (buf,1,size,statf) != size) ||
+ fflush (statf)) {
+ fseek (statf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (statf),sbuf.st_size);
+ MM_LOG ("Error extending mix status file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (statf); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (statf,SEQFMT,LOCAL->statusseq);
+ /* write message status records */
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ /* make sure all messages have a modseq */
+ if (!elt->private.mod) elt->private.mod = LOCAL->statusseq;
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (statf,STRFMT,elt->private.uid,elt->user_flags,
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + (elt->valid ? fOLD : NIL),
+ elt->private.mod);
+ if (ferror (statf)) {
+ sprintf (tmp,"Error updating mix status file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (statf)) {
+ MM_LOG ("Error flushing mix status file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (statf),ftell (statf));
+ }
+ }
+ return ret;
+}
+
+/* MIX data file routines */
+
+
+/* MIX open data file
+ * Accepts: MAIL stream
+ * pointer to returned fd if success
+ * pointer to returned size if success
+ * size of new data to be added
+ * Returns: open FILE, or NIL if failure
+ *
+ * The curend test assumes that the last message of the mailbox is the furthest
+ * point that the current data file extends, and thus that is all that needs to
+ * be tested for short file prevention.
+ */
+
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize)
+{
+ FILE *msgf = NIL;
+ struct stat sbuf;
+ MESSAGECACHE *elt = stream->nmsgs ? mail_elt (stream,stream->nmsgs) : NIL;
+ unsigned long curend = (elt && (elt->private.spare.data == LOCAL->newmsg)) ?
+ elt->private.special.offset + elt->private.msg.header.offset +
+ elt->rfc822_size : 0;
+ /* allow create if curend 0 */
+ if ((*fd = open (mix_file_data (LOCAL->buf,stream->mailbox,LOCAL->newmsg),
+ O_RDWR | (curend ? NIL : O_CREAT),NIL)) >= 0) {
+ fstat (*fd,&sbuf); /* get current file size */
+ /* can we use this file? */
+ if ((curend <= sbuf.st_size) &&
+ (!sbuf.st_size || ((sbuf.st_size + newsize) <= MIXDATAROLL)))
+ *size = sbuf.st_size; /* yes, return current size */
+ else { /* short file or becoming too long */
+ if (curend > sbuf.st_size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"short mix message file %.08lx (%ld > %ld), rolling",
+ LOCAL->newmsg,curend,sbuf.st_size);
+ MM_LOG (tmp,WARN); /* shouldn't happen */
+ }
+ close (*fd); /* roll to a new file */
+ while ((*fd = open (mix_file_data
+ (LOCAL->buf,stream->mailbox,
+ LOCAL->newmsg = mix_modseq (LOCAL->newmsg)),
+ O_RDWR | O_CREAT | O_EXCL,sbuf.st_mode)) < 0);
+ *size = 0; /* brand new file */
+ fchmod (*fd,sbuf.st_mode);/* with same mode as previous file */
+ }
+ }
+ if (*fd >= 0) { /* have a data file? */
+ /* yes, get stdio and set position */
+ if (msgf = fdopen (*fd,"r+b")) fseek (msgf,*size,SEEK_SET);
+ else close (*fd); /* fdopen() failed? */
+ }
+ return msgf; /* return results */
+}
+
+/* MIX open sortcache
+ * Accepts: MAIL stream
+ * Returns: open FILE, or NIL if failure or could only get readonly sortcache
+ */
+
+FILE *mix_sortcache_open (MAILSTREAM *stream)
+{
+ int fd,refwd;
+ unsigned long i,uid,sentdate,fromlen,tolen,cclen,subjlen,msgidlen,reflen;
+ char *s,*t,*msg,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ SORTCACHE *sc;
+ STRINGLIST *sl;
+ struct stat sbuf;
+ int rdonly = NIL;
+ FILE *srtcf = NIL;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ fstat (LOCAL->mfd,&sbuf);
+ if (!stream->nmsgs); /* do nothing if mailbox empty */
+ /* open sortcache file */
+ else if (((fd = open (LOCAL->sortcache,O_RDWR|O_CREAT,sbuf.st_mode)) < 0) &&
+ !(rdonly = ((fd = open (LOCAL->sortcache,O_RDONLY,NIL)) >= 0)))
+ MM_LOG ("Error opening mix sortcache file",WARN);
+ /* acquire lock and FILE */
+ else if (!flock (fd,rdonly ? LOCK_SH : LOCK_EX) &&
+ !(srtcf = fdopen (fd,rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix sortcache file",WARN);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ else if (!(i = mix_read_sequence (srtcf)) || (i < LOCAL->sortcacheseq))
+ MM_LOG ("Error in mix sortcache file sequence record",WARN);
+ /* sequence changed from last time? */
+ else if (i > LOCAL->sortcacheseq) {
+ LOCAL->sortcacheseq = i; /* update sequence */
+ while ((s = t = mix_read_record (srtcf,LOCAL->buf,LOCAL->buflen,
+ "sortcache")) && *s &&
+ (msg = "uid") && (*s++ == ':') && isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sentdate = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ fromlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ tolen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ cclen = strtoul (s,&s,16);
+ if ((*s++ == ':') && ((*s == 'R') || (*s == ' ')) &&
+ isxdigit (s[1])) {
+ refwd = (*s++ == 'R') ? T : NIL;
+ subjlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ msgidlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ reflen = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+
+ if (i = mail_msgno (stream,uid)) {
+ sc = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ sc->size = (elt = mail_elt (stream,i))->rfc822_size;
+ sc->date = sentdate;
+ sc->arrival = elt->day ? mail_longdate (elt) : 1;
+ if (refwd) sc->refwd = T;
+ if (fromlen) {
+ if (sc->from) fseek (srtcf,fromlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'F') ||
+ (fread (sc->from = (char *) fs_get(fromlen),
+ 1,fromlen-1,srtcf) != (fromlen-1))||
+ (sc->from[fromlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "from data";
+ break;
+ }
+ }
+ if (tolen) {
+ if (sc->to) fseek (srtcf,tolen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'T') ||
+ (fread (sc->to = (char *) fs_get (tolen),
+ 1,tolen-1,srtcf) != (tolen - 1)) ||
+ (sc->to[tolen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "to data";
+ break;
+ }
+ }
+ if (cclen) {
+ if (sc->cc) fseek (srtcf,cclen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'C') ||
+ (fread (sc->cc = (char *) fs_get (cclen),
+ 1,cclen-1,srtcf) != (cclen - 1)) ||
+ (sc->cc[cclen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "cc data";
+ break;
+ }
+ }
+ if (subjlen) {
+ if (sc->subject) fseek (srtcf,subjlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'S') ||
+ (fread (sc->subject =
+ (char *) fs_get (subjlen),1,
+ subjlen-1,srtcf) != (subjlen-1))||
+ (sc->subject[subjlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "subject data";
+ break;
+ }
+ }
+
+ if (msgidlen) {
+ if (sc->message_id)
+ fseek (srtcf,msgidlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'M') ||
+ (fread (sc->message_id =
+ (char *) fs_get (msgidlen),1,
+ msgidlen-1,srtcf) != (msgidlen-1))||
+ (sc->message_id[msgidlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "message-id data";
+ break;
+ }
+ }
+ if (reflen) {
+ if (sc->references) fseek(srtcf,reflen + 2,SEEK_CUR);
+ /* make sure it fits */
+ else {
+ if (reflen >= LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *)
+ fs_get ((LOCAL->buflen = reflen) + 1);
+ }
+ if ((getc (srtcf) != 'R') ||
+ (fread (LOCAL->buf,1,reflen-1,srtcf) !=
+ (reflen - 1)) ||
+ (LOCAL->buf[reflen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "references data";
+ break;
+ }
+ for (s = LOCAL->buf,sl = NIL,
+ sc->references = mail_newstringlist ();
+ s && *s; s += i + 1) {
+ if ((i = strtoul (s,&s,16)) && (*s++ == ':') &&
+ (s[i] == ':')) {
+ if (sl) sl = sl->next = mail_newstringlist();
+ else sl = sc->references;
+ s[i] = '\0';
+ sl->text.data = cpystr (s);
+ sl->text.size = i;
+ }
+ else s = NIL;
+ }
+ if (!s || *s ||
+ (s != ((char *) LOCAL->buf + reflen - 1))) {
+ msg = "references length consistency check";
+ break;
+ }
+ }
+ }
+ }
+
+ /* UID not found, ignore this message */
+ else fseek (srtcf,((fromlen ? fromlen + 2 : 0) +
+ (tolen ? tolen + 2 : 0) +
+ (cclen ? cclen + 2 : 0) +
+ (subjlen ? subjlen + 2 : 0) +
+ (msgidlen ? msgidlen + 2 : 0) +
+ (reflen ? reflen + 2 : 0)),
+ SEEK_CUR);
+ continue;
+ }
+ else msg = "expansion";
+ }
+ else msg = "references";
+ }
+ else msg = "message-id";
+ }
+ else msg = "subject";
+ }
+ else msg = "cc";
+ }
+ else msg = "to";
+ }
+ else msg = "from";
+ }
+ else msg = "sentdate";
+ break; /* error somewhere */
+ }
+ if (!t || *t) { /* error detected? */
+ if (t) { /* non-null means bogus record */
+ sprintf (tmp,"Error in %s in mix sortcache record: %.500s",msg,t);
+ MM_LOG (tmp,WARN);
+ }
+ fclose (srtcf); /* either way, must punt */
+ srtcf = NIL;
+ }
+ }
+ if (rdonly && srtcf) { /* can't update if readonly */
+ unlink (LOCAL->sortcache); /* try deleting it */
+ fclose (srtcf); /* so close it and return as if error */
+ srtcf = NIL;
+ }
+ else fchmod (fd,sbuf.st_mode);
+ return srtcf;
+}
+
+/* MIX update and close sortcache
+ * Accepts: MAIL stream
+ * pointer to open FILE (if FILE is NIL, do nothing)
+ * Returns: T on success, NIL on error
+ */
+
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache)
+{
+ FILE *f = *sortcache;
+ long ret = LONGT;
+ if (f) { /* ignore if no file */
+ unsigned long i,j;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ for (i = 1; (i <= stream->nmsgs) &&
+ !((SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE))->dirty; ++i);
+ if (i <= stream->nmsgs) { /* only update if some entry is dirty */
+ rewind (f); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (f,SEQFMT,LOCAL->sortcacheseq = mix_modseq(LOCAL->sortcacheseq));
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ SORTCACHE *s = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ STRINGLIST *sl;
+ s->dirty = NIL; /* no longer dirty */
+ if (sl = s->references) /* count length of references */
+ for (j = 1; sl && sl->text.data; sl = sl->next)
+ j += 10 + sl->text.size;
+ else j = 0; /* no references yet */
+ fprintf (f,SCRFMT,elt->private.uid,s->date,
+ s->from ? strlen (s->from) + 1 : 0,
+ s->to ? strlen (s->to) + 1 : 0,s->cc ? strlen (s->cc) + 1 : 0,
+ s->refwd ? 'R' : ' ',s->subject ? strlen (s->subject) + 1: 0,
+ s->message_id ? strlen (s->message_id) + 1 : 0,j);
+ if (s->from) fprintf (f,"F%s\015\012",s->from);
+ if (s->to) fprintf (f,"T%s\015\012",s->to);
+ if (s->cc) fprintf (f,"C%s\015\012",s->cc);
+ if (s->subject) fprintf (f,"S%s\015\012",s->subject);
+ if (s->message_id) fprintf (f,"M%s\015\012",s->message_id);
+ if (j) { /* any references to write? */
+ fputc ('R',f); /* yes, do so */
+ for (sl = s->references; sl && sl->text.data; sl = sl->next)
+ fprintf (f,"%08lx:%s:",sl->text.size,sl->text.data);
+ fputs ("\015\012",f);
+ }
+ if (ferror (f)) {
+ MM_LOG ("Error updating mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (f)) {
+ MM_LOG ("Error flushing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (f),ftell (f));
+ }
+ if (fclose (f)) {
+ MM_LOG ("Error closing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ return ret;
+}
+
+/* MIX generic file routines */
+
+/* MIX read record
+ * Accepts: open FILE
+ * buffer
+ * buffer length
+ * record type
+ * Returns: buffer if success, else NIL (zero-length buffer means EOF)
+ */
+
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type)
+{
+ char *s,tmp[MAILTMPLEN];
+ /* ensure string tied off */
+ buf[buflen-2] = buf[buflen-1] = '\0';
+ while (fgets (buf,buflen-1,f)) {
+ if (s = strchr (buf,'\012')) {
+ if ((s != buf) && (s[-1] == '\015')) --s;
+ *s = '\0'; /* tie off buffer */
+ if (s != buf) return buf; /* return if non-empty buffer */
+ sprintf (tmp,"Empty mix %s record",type);
+ MM_LOG (tmp,WARN);
+ }
+ else if (buf[buflen-2]) { /* overlong record is bad news */
+ sprintf (tmp,"Oversize mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sprintf (tmp,"Truncated mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,WARN);
+ return buf; /* pass to caller anyway */
+ }
+ }
+ buf[0] = '\0'; /* return empty buffer on EOF */
+ return buf;
+}
+
+/* MIX read sequence record
+ * Accepts: open FILE
+ * Returns: sequence value, or NIL if failure
+ */
+
+unsigned long mix_read_sequence (FILE *f)
+{
+ unsigned long ret;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_read_record (f,tmp,MAILTMPLEN-1,"sequence")) return NIL;
+ switch (tmp[0]) { /* examine record */
+ case '\0': /* end of file */
+ ret = 1; /* start a new sequence regime */
+ break;
+ case 'S': /* sequence record */
+ if (isxdigit (tmp[1])) { /* must be followed by hex value */
+ ret = strtoul (tmp+1,&s,16);
+ if (!*s) break; /* and nothing more */
+ }
+ /* drop into default case */
+ default: /* anything else is an error */
+ return NIL; /* return error */
+ }
+ return ret;
+}
+
+/* MIX internal routines */
+
+
+/* MIX mail build directory name
+ * Accepts: destination string
+ * source
+ * Returns: destination or empty string if error
+ */
+
+char *mix_dir (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+
+/* MIX mail build file name
+ * Accepts: destination string
+ * directory name
+ * file name
+ * Returns: destination
+ */
+
+char *mix_file (char *dst,char *dir,char *name)
+{
+ sprintf (dst,"%.500s/%.80s%.80s",dir,MIXNAME,name);
+ return dst;
+}
+
+
+/* MIX mail build file name from data file number
+ * Accepts: destination string
+ * directory name
+ * data file number
+ * Returns: destination
+ */
+
+char *mix_file_data (char *dst,char *dir,unsigned long data)
+{
+ char tmp[MAILTMPLEN];
+ if (data) sprintf (tmp,"%08lx",data);
+ else tmp[0] = '\0'; /* compatibility with experimental version */
+ return mix_file (dst,dir,tmp);
+}
+
+/* MIX mail get new modseq
+ * Accepts: old modseq
+ * Returns: new modseq value
+ */
+
+unsigned long mix_modseq (unsigned long oldseq)
+{
+ /* normally time now */
+ unsigned long ret = (unsigned long) time (NIL);
+ /* ensure that modseq doesn't go backwards */
+ if (ret <= oldseq) ret = oldseq + 1;
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mkauths b/imap/src/osdep/amiga/mkauths
new file mode 100755
index 00000000..8e9cc0cf
--- /dev/null
+++ b/imap/src/osdep/amiga/mkauths
@@ -0,0 +1,40 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Authenticator Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 December 1995
+# Last Edited: 30 August 2006
+
+# Erase old authenticators list
+rm -f auths.c
+touch auths.c
+
+# Now define the new list
+for authenticator
+ do
+ if [ -f Makefile."$authenticator" ]; then
+ make -f Makefile."$authenticator" `cat SPECIALS`
+ fi
+ echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h
+ echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c
+ echo "#include \"auth_"$authenticator".c\"" >> auths.c
+done
diff --git a/imap/src/osdep/amiga/mmdf.c b/imap/src/osdep/amiga/mmdf.c
new file mode 100644
index 00000000..e962434e
--- /dev/null
+++ b/imap/src/osdep/amiga/mmdf.c
@@ -0,0 +1,2549 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MMDF mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Supposedly, this page has everything the MMDF driver needs to know about
+ * the MMDF delimiter. By changing these macros, the MMDF driver should
+ * change with it. Note that if you change the length of MMDFHDRTXT you
+ * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new
+ * size.
+ */
+
+
+/* Useful MMDF constants */
+
+#define MMDFCHR '\01' /* MMDF character */
+#define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */
+ /* MMDF header text */
+#define MMDFHDRTXT "\01\01\01\01\n"
+ /* length of MMDF header text */
+#define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1)
+
+
+/* Validate MMDF header
+ * Accepts: pointer to candidate string to validate as an MMDF header
+ * Returns: T if valid; else NIL
+ */
+
+#define ISMMDF(s) \
+ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \
+ ((s)[3] == MMDFCHR) && ((s)[4] == '\n'))
+
+
+/* Return if a 32-bit word has the start of an MMDF header
+ * Accepts: pointer to word of four bytes to validate as an MMDF header
+ * Returns: pointer to MMDF header, else proceeds
+ */
+
+#define RETIFMMDFWRD(s) { \
+ if (s[3] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \
+ (s[7] == '\n')) return s + 3; \
+ else if (s[2] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \
+ return s + 2; \
+ else if (s[1] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \
+ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \
+ } \
+ } \
+ } \
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\n'; x++); \
+ if (*x) { \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-5 Validates that there is an end of line and points x at it.
+ * Lines 6-13 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 14 Makes sure that there are at least 27 characters in the line.
+ * Lines 15-20 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 21-23 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 24-27 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 28-31 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+
+
+/* MMDF I/O stream local data */
+
+typedef struct mmdf_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} MMDFLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MMDFLOCAL *) stream->local)
+
+
+/* MMDF protected file structure */
+
+typedef struct mmdf_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} MMDFFILE;
+
+/* Function prototypes */
+
+DRIVER *mmdf_valid (char *name);
+long mmdf_isvalid (char *name,char *tmp);
+long mmdf_isvalid_fd (int fd,char *tmp);
+void *mmdf_parameters (long function,void *value);
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat);
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mmdf_create (MAILSTREAM *stream,char *mailbox);
+long mmdf_delete (MAILSTREAM *stream,char *mailbox);
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mmdf_open (MAILSTREAM *stream);
+void mmdf_close (MAILSTREAM *stream,long options);
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mmdf_ping (MAILSTREAM *stream);
+void mmdf_check (MAILSTREAM *stream);
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void mmdf_abort (MAILSTREAM *stream);
+char *mmdf_file (char *dst,char *name);
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long mmdf_extend (MAILSTREAM *stream,unsigned long size);
+void mmdf_write (MMDFFILE *f,char *s,unsigned long i);
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size);
+
+/* MMDF mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mmdfdriver = {
+ "mmdf", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ mmdf_valid, /* mailbox is valid for us */
+ mmdf_parameters, /* manipulate parameters */
+ mmdf_scan, /* scan mailboxes */
+ mmdf_list, /* list mailboxes */
+ mmdf_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mmdf_create, /* create mailbox */
+ mmdf_delete, /* delete mailbox */
+ mmdf_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mmdf_open, /* open mailbox */
+ mmdf_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mmdf_header, /* fetch message header */
+ mmdf_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mmdf_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mmdf_ping, /* ping mailbox to see if still alive */
+ mmdf_check, /* check for new messages */
+ mmdf_expunge, /* expunge deleted messages */
+ mmdf_copy, /* copy messages to another mailbox */
+ mmdf_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mmdfproto = {&mmdfdriver};
+
+char *mmdfhdr = MMDFHDRTXT; /* MMDF header */
+
+/* MMDF mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mmdf_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL;
+}
+
+
+/* MMDF mail test for valid mailbox name
+ * Accepts: mailbox name
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* error -1 for invalid format */
+ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1;
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* MMDF mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid_fd (int fd,char *tmp)
+{
+ int ret = NIL;
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL;
+ return ret; /* return what we should */
+}
+
+
+/* MMDF manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mmdf_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* MMDF mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MMDF mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MMDF mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MMDF mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti));
+ rfc822_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* MMDF mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mmdf_rename (stream,mailbox,NIL);
+}
+
+
+/* MMDF mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = mmdf_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ mmdf_unlock (fd,NIL,&lockx);
+ mmdf_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ mmdf_unlock (fd,NIL,&lockx);
+ }
+ mmdf_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* MMDF mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *mmdf_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mmdfproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("mmdf recycle stream");
+ stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ mmdf_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (mmdf_parse (stream,&lock,LOCK_SH)) {
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* MMDF mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mmdf_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) mmdf_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) mmdf_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ mmdf_abort (stream); /* now punt the file and local data */
+}
+
+/* MMDF mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *mmdf_hlines = NIL;
+
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!mmdf_hlines) { /* once only code */
+ STRINGLIST *lines = mmdf_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* MMDF mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = mmdf_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* MMDF mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* MMDF per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* MMDF mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mmdf_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) mmdf_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) mmdf_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* MMDF mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mmdf_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* MMDF mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (mmdf_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent)
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* MMDF mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(mmdf_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mmdf_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = mmdf_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ mmdf_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* MMDF mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ /* default stream to prototype */
+ if (!stream) { /* stream specified? */
+ stream = &mmdfproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!mmdf_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ mmdf_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!mmdf_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!mmdf_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df);
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"%sFrom %s@%s %sStatus: ",mmdfhdr,myusername(),
+ mylocalhost(),x) < 0) ||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("mmdf_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out ^A and CRs (note copies NUL) */
+ for (x = tmp; x = strpbrk (x,"\01\r"); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ /* make sure read entire msg & wrote trailer */
+ if (i || (fputs (mmdfhdr,df) == EOF)) return NIL;
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* MMDF mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mmdf_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MMDF open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* MMDF unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* MMDF mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int ti,zn,m;
+ unsigned long i,j,k;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ stream->silent = T; /* quell main program new message events */
+ do { /* read MMDF header */
+ if (!(i && ISMMDF (s))){/* see if valid MMDF header */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ /* see if we can back up to a line */
+ if (i && (j > MMDFHDRLEN)) {
+ SETPOS (&bs,j -= MMDFHDRLEN);
+ /* read previous line */
+ s = mmdf_mbxline (stream,&bs,&i);
+ /* kill the error if it looks good */
+ if (i && ISMMDF (s)) tmp[0] = '\0';
+ }
+ if (tmp[0]) {
+ MM_LOG (tmp,ERROR);
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ }
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.special.text.size = i;
+
+ s = mmdf_mbxline (stream,&bs,&i);
+ ti = 0; /* assume not a valid date */
+ zn = 0,t = NIL;
+ if (i) VALID (s,t,ti,zn);
+ if (ti) { /* generate plausible IMAPish date string */
+ /* this is also part of header */
+ elt->private.special.text.size += i;
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6]==':'){ /* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* make date from file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ t = NIL; /* suppress line read */
+ }
+ /* header starts here */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+
+ do { /* look for message body */
+ j = GETPOS (&bs); /* note position before line */
+ if (t) s = t = mmdf_mbxline (stream,&bs,&i);
+ else t = s; /* this line read was suppressed */
+ if (ISMMDF (s)) { /* found terminator in header? */
+ SETPOS (&bs,j); /* oops, back up before line */
+ /* must insert a newline */
+ elt->private.spare.data++;
+ break; /* punt */
+ }
+ /* this line is part of header */
+ elt->private.msg.header.text.size += i;
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = mmdf_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ if (ISMMDF (s)) break;
+ else { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (i) { /* get next header line */
+ /* remember first internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* MMDF read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ /* embedded MMDF header at end of line? */
+ if ((*size > sizeof (MMDFHDRTXT)) &&
+ (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) {
+ SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */
+ *size -= i; /* reduce length of line */
+ ret[*size - 1] = '\n'; /* force newline at end */
+ }
+ return ret;
+}
+
+/* MMDF make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ mmdfhdr,pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ return strlen (hdr);
+}
+
+/* MMDF make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ MMDFFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = mmdf_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = mmdf_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ mmdf_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ mmdf_write (&f,LOCAL->buf,
+ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset :
+ (f.curpos + j + MMDFHDRLEN);
+ mmdf_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ mmdf_write (&f,mmdfhdr,MMDFHDRLEN);
+ }
+ else { /* tie off header and status */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ }
+ }
+
+ mmdf_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend MMDF mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long mmdf_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void mmdf_write (MMDFFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ mmdf_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/amiga/mtx.c b/imap/src/osdep/amiga/mtx.c
new file mode 100644
index 00000000..8e6f76e8
--- /dev/null
+++ b/imap/src/osdep/amiga/mtx.c
@@ -0,0 +1,1371 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mtx_status, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mtx_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mtx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ return ret; /* return success */
+}
+
+/* Mtx Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mtxproto);
+ if (stream->local) fatal ("mtx recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!mtx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return (char *) LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ mtx_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MTX mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void mtx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (mtx_file (file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mtxproto);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mtx_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "INBOX.MTX") : s;
+}
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mx.c b/imap/src/osdep/amiga/mx.c
new file mode 100644
index 00000000..45495279
--- /dev/null
+++ b/imap/src/osdep/amiga/mx.c
@@ -0,0 +1,1287 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MX mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1996
+ * Last Edited: 6 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* Index file */
+
+#define MXINDEXNAME "/.mxindex"
+#define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME)
+
+
+/* MX I/O stream local data */
+
+typedef struct mx_local {
+ int fd; /* file descriptor of open index */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mx_valid (char *name);
+int mx_isvalid (char *name,char *tmp);
+int mx_namevalid (char *name);
+void *mx_parameters (long function,void *value);
+long mx_dirfmttest (char *name);
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mx_subscribe (MAILSTREAM *stream,char *mailbox);
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mx_create (MAILSTREAM *stream,char *mailbox);
+long mx_delete (MAILSTREAM *stream,char *mailbox);
+long mx_rename (MAILSTREAM *stream,char *old,char *newname);
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name);
+MAILSTREAM *mx_open (MAILSTREAM *stream);
+void mx_close (MAILSTREAM *stream,long options);
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags);
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt);
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mx_ping (MAILSTREAM *stream);
+void mx_check (MAILSTREAM *stream);
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set);
+
+int mx_select (struct direct *name);
+int mx_numsort (const void *d1,const void *d2);
+char *mx_file (char *dst,char *name);
+long mx_lockindex (MAILSTREAM *stream);
+void mx_unlockindex (MAILSTREAM *stream);
+void mx_setdate (char *file,MESSAGECACHE *elt);
+
+
+/* MX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mxdriver = {
+ "mx", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mx_valid, /* mailbox is valid for us */
+ mx_parameters, /* manipulate parameters */
+ mx_scan, /* scan mailboxes */
+ mx_list, /* find mailboxes */
+ mx_lsub, /* find subscribed mailboxes */
+ mx_subscribe, /* subscribe to mailbox */
+ mx_unsubscribe, /* unsubscribe from mailbox */
+ mx_create, /* create mailbox */
+ mx_delete, /* delete mailbox */
+ mx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mx_open, /* open mailbox */
+ mx_close, /* close mailbox */
+ mx_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mx_header, /* fetch message header only */
+ mx_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mx_flag, /* modify flags */
+ mx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mx_ping, /* ping mailbox to see if still alive */
+ mx_check, /* check for new messages */
+ mx_expunge, /* expunge deleted messages */
+ mx_copy, /* copy messages to another mailbox */
+ mx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mxproto = {&mxdriver};
+
+/* MX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mx_isvalid (name,tmp) ? &mxdriver : NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * Returns: T if valid, NIL otherwise with errno holding dir stat error
+ */
+
+int mx_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ errno = NIL; /* zap error */
+ if ((strlen (name) <= NETMAXMBX) && *mx_file (tmp,name) &&
+ !stat (tmp,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mx? */
+ if (!stat (MXINDEX (tmp,name),&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return T;
+ errno = NIL; /* directory but not mx */
+ }
+ else if (!compare_cstring (name,"INBOX")) errno = NIL;
+ return NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mx_namevalid (char *name)
+{
+ char *s = (*name == '/') ? name + 1 : name;
+ while (s && *s) { /* make sure valid name */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break; /* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* MX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mx_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mx_scan_contents;
+ break;
+ }
+ return ret;
+}
+
+
+/* MX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mx_dirfmttest (char *name)
+{
+ int c;
+ /* success if index name or all-numberic */
+ if (strcmp (name,MXINDEXNAME+1))
+ while (c = *name++) if (!isdigit (c)) return NIL;
+ return LONGT;
+}
+
+/* MX scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mx_select,mx_numsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ int mask = umask (0);
+ long ret = NIL;
+ if (!mx_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory */
+ else if (!dummy_create_path (stream,MXINDEX (tmp,mailbox),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else { /* success */
+ /* set index protection */
+ set_mbx_protections (mailbox,tmp);
+ /* tie off directory name */
+ *(s = strrchr (tmp,'/') + 1) = '\0';
+ /* set directory protection */
+ set_mbx_protections (mailbox,tmp);
+ ret = LONGT;
+ }
+ umask (mask); /* restore mask */
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ char *s;
+ char tmp[MAILTMPLEN];
+ if (!mx_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ /* delete index */
+ else if (unlink (MXINDEX (tmp,mailbox)))
+ sprintf (tmp,"Can't delete mailbox %.80s index: %s",
+ mailbox,strerror (errno));
+ else { /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mx_select (d)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MX mail rename mailbox
+ * Accepts: MX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ if (!mx_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mx_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mx_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mx_file (tmp,old); /* build old directory name */
+ mx_file (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (mx_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) return LONGT;
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mx file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mx_select,mx_numsort);
+ i < n; ++i) {
+ if (mx_rename_work (tmp,srcl,tmp1,dstl,names[i]->d_name))
+ lasterror = errno;
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror || mx_rename_work (tmp,srcl,tmp1,dstl,MXINDEXNAME+1))
+ errno = lasterror;
+ else return mx_create (NIL,"INBOX");
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MX rename worker routine
+ * Accepts: source directory name
+ * source directory name length
+ * destination directory name
+ * destination directory name length
+ * name of node to move
+ * Returns: zero if success, non-zero if error
+ */
+
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name)
+{
+ int ret;
+ size_t len = strlen (name);
+ char *s = (char *) fs_get (srcl + len + 2);
+ char *d = (char *) fs_get (dstl + len + 1);
+ sprintf (s,"%s/%s",src,name);
+ sprintf (d,"%s%s",dst,name);
+ ret = rename (s,d);
+ fs_give ((void **) &s);
+ fs_give ((void **) &d);
+ return ret;
+}
+
+/* MX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mx_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mxproto);
+ if (stream->local) fatal ("mx recycle stream");
+ stream->local = fs_get (sizeof (MXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ mx_file (tmp,stream->mailbox);/* get directory name */
+ /* canonicalize mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->fd = -1; /* no index yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mx_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mx_expunge (stream,NIL,NIL);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt);
+}
+
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * message cache element
+ * Returns: name of message file
+ */
+
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ struct tm *tm;
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ /* have size yet? */
+ if (!elt->rfc822_size && !stat (LOCAL->buf,&sbuf)) {
+ /* make plausible IMAPish date string */
+ tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
+ elt->rfc822_size = sbuf.st_size;
+ }
+ return (char *) LOCAL->buf; /* return file name */
+}
+
+/* MX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ int fd;
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ mail_gc (stream,GC_TEXTS);/* just can't keep that much */
+ LOCAL->cachedtexts = 0;
+ }
+ if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return "";
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
+ }
+ /* slurp message */
+ read (fd,LOCAL->buf,elt->rfc822_size);
+ /* tie off file */
+ LOCAL->buf[elt->rfc822_size] = '\0';
+ close (fd); /* flush message file */
+ /* find end of header */
+ if (elt->rfc822_size < 4) i = 0;
+ else for (i = 4; (i < elt->rfc822_size) &&
+ !((LOCAL->buf[i - 4] == '\015') &&
+ (LOCAL->buf[i - 3] == '\012') &&
+ (LOCAL->buf[i - 2] == '\015') &&
+ (LOCAL->buf[i - 1] == '\012')); i++);
+ /* copy header */
+ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i);
+ cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i);
+ /* add to cached size */
+ LOCAL->cachedtexts += elt->rfc822_size;
+ }
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+/* MX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mx_header (stream,msgno,&i,flags);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ /* mark as seen */
+ if (!(flags & FT_PEEK) && mx_lockindex (stream)) {
+ elt->seen = T;
+ mx_unlockindex (stream);
+ MM_FLAGS (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ mx_unlockindex (stream); /* finished with index */
+}
+
+
+/* MX per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ mx_lockindex (stream); /* lock index if not already locked */
+}
+
+/* MX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mx_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r,old;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (stream->mailbox,&sbuf)) return NIL;
+ stream->silent = T; /* don't pass up exists events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mx_select,mx_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ old = stream->uid_last;
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+ stream->nmsgs = nmsgs; /* don't upset mail_uid() */
+
+ /* if INBOX, snarf from system INBOX */
+ if (mx_lockindex (stream) && stream->inbox &&
+ !strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ MM_CRITICAL (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mx_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MX mailbox failed: %.80s",
+ s,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (stream->mailbox,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ }
+ mx_unlockindex (stream); /* done with index */
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mx_check (MAILSTREAM *stream)
+{
+ if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mx_lockindex (stream)) { /* lock the index */
+ MM_CRITICAL (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ MM_LOG (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ if(elt->recent)--recent;/* if recent, note one less recent message */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ MM_NOCRITICAL (stream); /* release critical */
+ mx_unlockindex (stream); /* finished with index */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ MAILSTREAM *astream;
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j,uid,uidv;
+ char *t,tmp[MAILTMPLEN];
+ long ret;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mx_valid (mailbox)) switch (errno) {
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ /* copy the messages */
+ if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append to */
+ else if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open copy mailbox",ERROR);
+ ret = NIL;
+ }
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message copy failed: unable to lock index",ERROR);
+ else {
+
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (ret = ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))
+ >= 0)) {
+ fstat (fd,&sbuf); /* get size of message */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if (t = stream->user_flags[find_rightmost_bit (&j)])
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* open list */
+ strcat (tmp,")"); /* close list */
+ if (ret = mx_append_msg (astream,tmp,elt,&st,dest)) {
+ /* add to source set if needed */
+ if (source) mail_append_set (source,mail_uid (stream,i));
+ /* delete if doing a move */
+ if (options & CP_MOVE) elt->deleted = T;
+ }
+ }
+ }
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (stream);
+ mail_close (astream); /* finished with append stream */
+ }
+ return ret; /* return success */
+}
+
+/* MX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MESSAGECACHE elt;
+ MAILSTREAM *astream;
+ char *flags,*date,tmp[MAILTMPLEN];
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mxproto);
+ /* N.B.: can't use LOCAL->buf for tmp */
+ /* make sure valid mailbox */
+ if (!mx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open append mailbox",ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (astream); /* go critical */
+ /* lock the index */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message append failed: unable to lock index",ERROR);
+ else {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ do {
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = mx_append_msg (astream,flags,date ? &elt : NIL,message,dst)&&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ } while (ret && message);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,astream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ mail_close (astream);
+ return ret;
+}
+
+/* MX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set)
+{
+ char tmp[MAILTMPLEN];
+ int fd;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* make message file name */
+ sprintf (tmp,"%s/%lu",stream->mailbox,++stream->uid_last);
+ if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't create append message: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ while (SIZE (st)) { /* copy the file */
+ if (st->cursize && (write (fd,st->curpos,st->cursize) < 0)) {
+ unlink (tmp); /* delete file */
+ close (fd); /* close the file */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ SETPOS (st,GETPOS (st) + st->cursize);
+ }
+ close (fd); /* close the file */
+ if (elt) mx_setdate (tmp,elt);/* set file date */
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* copy flags */
+ mail_append_set (set,(elt = mail_elt (stream,stream->nmsgs))->private.uid =
+ stream->uid_last);
+ if (f&fSEEN) elt->seen = T;
+ if (f&fDELETED) elt->deleted = T;
+ if (f&fFLAGGED) elt->flagged = T;
+ if (f&fANSWERED) elt->answered = T;
+ if (f&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ return LONGT;
+}
+
+/* Internal routines */
+
+
+/* MX file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mx_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MX file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mx_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MX mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mx_file (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+/* MX read and lock index
+ * Accepts: MAIL stream
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_lockindex (MAILSTREAM *stream)
+{
+ unsigned long uf,sf,uid;
+ int k = 0;
+ unsigned long msgno = 1;
+ struct stat sbuf;
+ char *s,*t,*idx,tmp[2*MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
+ (LOCAL->fd = open (strcat (strcpy (tmp,stream->mailbox),MXINDEXNAME),
+ O_RDWR|O_CREAT,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
+ (*bn) (BLOCK_NONE,NIL);
+ fstat (LOCAL->fd,&sbuf); /* get size of index */
+ /* slurp index */
+ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ idx[sbuf.st_size] = '\0'; /* tie off index */
+ /* parse index */
+ if (sbuf.st_size) while (s && *s) switch (*s) {
+ case 'V': /* UID validity record */
+ stream->uid_validity = strtoul (s+1,&s,16);
+ break;
+ case 'L': /* UID last record */
+ stream->uid_last = strtoul (s+1,&s,16);
+ break;
+ case 'K': /* keyword */
+ /* find end of keyword */
+ if (s = strchr (t = ++s,'\n')) {
+ *s++ = '\0'; /* tie off keyword */
+ /* copy keyword */
+ if ((k < NUSERFLAGS) && !stream->user_flags[k] &&
+ (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t);
+ k++; /* one more keyword */
+ }
+ break;
+
+ case 'M': /* message status record */
+ uid = strtoul (s+1,&s,16);/* get UID for this message */
+ if (*s == ';') { /* get user flags */
+ uf = strtoul (s+1,&s,16);
+ if (*s == '.') { /* get system flags */
+ sf = strtoul (s+1,&s,16);
+ while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid))
+ msgno++; /* find message number for this UID */
+ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) {
+ (elt = mail_elt (stream,msgno))->valid = T;
+ elt->user_flags=uf; /* set user and system flags in elt */
+ if (sf & fSEEN) elt->seen = T;
+ if (sf & fDELETED) elt->deleted = T;
+ if (sf & fFLAGGED) elt->flagged = T;
+ if (sf & fANSWERED) elt->answered = T;
+ if (sf & fDRAFT) elt->draft = T;
+ }
+ break;
+ }
+ }
+ default: /* bad news */
+ sprintf (tmp,"Error in index: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ *s = NIL; /* ignore remainder of index */
+ }
+ else { /* new index */
+ stream->uid_validity = time (0);
+ user_flags (stream); /* init stream with default user flags */
+ }
+ fs_give ((void **) &idx); /* flush index */
+ }
+ return (LOCAL->fd >= 0) ? T : NIL;
+}
+
+/* MX write and unlock index
+ * Accepts: MAIL stream
+ */
+
+#define MXIXBUFLEN 2048
+
+void mx_unlockindex (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ off_t size = 0;
+ char *s,tmp[MXIXBUFLEN + 64];
+ MESSAGECACHE *elt;
+ if (LOCAL->fd >= 0) {
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* write header */
+ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]);
+ /* write messages */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ /* filled buffer? */
+ if (((s += strlen (s)) - tmp) > MXIXBUFLEN) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ *(s = tmp) = '\0'; /* dump out and restart buffer */
+ }
+ elt = mail_elt (stream,i);
+ sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ }
+ /* write tail end of buffer */
+ if ((s += strlen (s)) != tmp) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ }
+ ftruncate (LOCAL->fd,size);
+ flock (LOCAL->fd,LOCK_UN); /* unlock the index */
+ close (LOCAL->fd); /* finished with file */
+ LOCAL->fd = -1; /* no index now */
+ }
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mx_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/amiga/news.c b/imap/src/osdep/amiga/news.c
new file mode 100644
index 00000000..4cf5bb70
--- /dev/null
+++ b/imap/src/osdep/amiga/news.c
@@ -0,0 +1,738 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: News routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 September 1991
+ * Last Edited: 30 January 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "newsrc.h"
+#include "fdstring.h"
+
+
+/* news_load_message() flags */
+
+#define NLM_HEADER 0x1 /* load message text */
+#define NLM_TEXT 0x2 /* load message text */
+
+/* NEWS I/O stream local data */
+
+typedef struct news_local {
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ char *dir; /* spool directory name */
+ char *name; /* local mailbox name */
+ unsigned char buf[CHUNKSIZE]; /* scratch buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+} NEWSLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NEWSLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *news_valid (char *name);
+DRIVER *news_isvalid (char *name,char *mbx);
+void *news_parameters (long function,void *value);
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void news_list (MAILSTREAM *stream,char *ref,char *pat);
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long news_canonicalize (char *ref,char *pat,char *pattern);
+long news_subscribe (MAILSTREAM *stream,char *mailbox);
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long news_create (MAILSTREAM *stream,char *mailbox);
+long news_delete (MAILSTREAM *stream,char *mailbox);
+long news_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *news_open (MAILSTREAM *stream);
+int news_select (struct direct *name);
+int news_numsort (const void *d1,const void *d2);
+void news_close (MAILSTREAM *stream,long options);
+void news_fast (MAILSTREAM *stream,char *sequence,long flags);
+void news_flags (MAILSTREAM *stream,char *sequence,long flags);
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long news_ping (MAILSTREAM *stream);
+void news_check (MAILSTREAM *stream);
+long news_expunge (MAILSTREAM *stream,char *sequence,long options);
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* News routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER newsdriver = {
+ "news", /* driver name */
+ /* driver flags */
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ news_valid, /* mailbox is valid for us */
+ news_parameters, /* manipulate parameters */
+ news_scan, /* scan mailboxes */
+ news_list, /* find mailboxes */
+ news_lsub, /* find subscribed mailboxes */
+ news_subscribe, /* subscribe to mailbox */
+ news_unsubscribe, /* unsubscribe from mailbox */
+ news_create, /* create mailbox */
+ news_delete, /* delete mailbox */
+ news_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ news_open, /* open mailbox */
+ news_close, /* close mailbox */
+ news_fast, /* fetch message "fast" attributes */
+ news_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ news_header, /* fetch message header */
+ news_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ news_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ news_ping, /* ping mailbox to see if still alive */
+ news_check, /* check for new messages */
+ news_expunge, /* expunge deleted messages */
+ news_copy, /* copy messages to another mailbox */
+ news_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM newsproto = {&newsdriver};
+
+/* News validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *news_valid (char *name)
+{
+ int fd;
+ char *s,*t,*u;
+ struct stat sbuf;
+ if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
+ (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
+ !strchr (name,'/') &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
+ NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get size of active file */
+ /* slurp in active file */
+ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
+ s[sbuf.st_size] = '\0'; /* tie off file */
+ close (fd); /* flush file */
+ while (*t && (u = strchr (t,' '))) {
+ *u++ = '\0'; /* tie off at end of name */
+ if (!strcmp (name+6,t)) {
+ fs_give ((void **) &s); /* flush data */
+ return &newsdriver;
+ }
+ t = 1 + strchr (u,'\n'); /* next line */
+ }
+ fs_give ((void **) &s); /* flush data */
+ }
+ return NIL; /* return status */
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *news_parameters (long function,void *value)
+{
+ return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
+}
+
+
+/* News scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (news_canonicalize (ref,pat,tmp))
+ mm_log ("Scan not valid for news mailboxes",ERROR);
+}
+
+/* News find list of newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int fd;
+ int i;
+ char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
+ struct stat sbuf;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (news_canonicalize (ref,"*",pattern)) {
+ /* tie off name at root */
+ if (s = strchr (pattern,'.')) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,LATT_NOSELECT);
+ }
+ }
+ else if (news_canonicalize (ref,pat,pattern) &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
+ O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file size and read data */
+ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ close (fd); /* close file */
+ s[sbuf.st_size] = '\0'; /* tie off string */
+ strcpy (name,"#news."); /* write initial prefix */
+ i = strlen (pattern); /* length of pattern */
+ if (pattern[--i] != '%') i = 0;
+ if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
+ *u = '\0'; /* tie off at end of name */
+ strcpy (name + 6,t); /* make full form of name */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else if (i && (u = strchr (name + i,'.'))) {
+ *u = '\0'; /* tie off at delimiter, see if matches */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ } while (t = strtok_r (NIL,"\n",&r));
+ fs_give ((void **) &s);
+ }
+}
+
+/* News find list of subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char pattern[MAILTMPLEN];
+ /* return data from newsrc */
+ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
+}
+
+
+/* News canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long news_canonicalize (char *ref,char *pat,char *pattern)
+{
+ unsigned long i;
+ char *s;
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
+ (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
+ !strchr (pattern,'/')) { /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* News subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
+}
+
+
+/* NEWS unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
+}
+
+/* News create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long news_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long news_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long news_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for News */
+}
+
+/* News open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *news_open (MAILSTREAM *stream)
+{
+ long i,nmsgs;
+ char *s,tmp[MAILTMPLEN];
+ struct direct **names = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &newsproto;
+ if (stream->local) fatal ("news recycle stream");
+ /* build directory name */
+ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
+ stream->mailbox + 6);
+ while (s = strchr (s,'.')) *s = '/';
+ /* scan directory */
+ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ stream->local = fs_get (sizeof (NEWSLOCAL));
+ LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->name = cpystr (stream->mailbox + 6);
+ for (i = 0; i < nmsgs; ++i) {
+ stream->uid_last = mail_elt (stream,i+1)->private.uid =
+ atoi (names[i]->d_name);
+ fs_give ((void **) &names[i]);
+ }
+ s = (void *) names; /* stupid language */
+ fs_give ((void **) &s); /* free directory */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (LOCAL->name,stream));
+ /* notify if empty newsgroup */
+ if (!(stream->nmsgs || stream->silent)) {
+ sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ else mm_log ("Unable to scan newsgroup spool directory",ERROR);
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* News file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int news_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* News file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int news_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* News close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void news_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ news_check (stream); /* dump final checkpoint */
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* News fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
+}
+
+
+/* News fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* News load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+
+ /* need to load cache with message data? */
+ if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+ if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i <= elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.msg.header.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i <= elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* News fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ news_load_message (stream,msgno,NLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* News fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ news_load_message (stream,msgno,NLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* News per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+
+/* News ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long news_ping (MAILSTREAM *stream)
+{
+ return T; /* always alive */
+}
+
+
+/* News check mailbox
+ * Accepts: MAIL stream
+ */
+
+void news_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* News expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long news_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* News copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for News",ERROR);
+ return NIL;
+}
+
+
+/* News append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for news",ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/nl_ami.c b/imap/src/osdep/amiga/nl_ami.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/amiga/nl_ami.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/amiga/os_ami.c b/imap/src/osdep/amiga/os_ami.c
new file mode 100644
index 00000000..1ba3ca5f
--- /dev/null
+++ b/imap/src/osdep/amiga/os_ami.c
@@ -0,0 +1,80 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define PINE /* to get full DIR description in <dirent.h> */
+#include "tcp_ami.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen /* for scandir.c */
+
+#include "fs_ami.c"
+#include "ftl_ami.c"
+#include "nl_ami.c"
+#include "env_ami.c"
+#include "tcp_ami.c"
+#include "log_std.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
+#include "scandir.c"
+#include "gethstid.c"
+
+#undef utime
+
+/* Amiga has its own wierd utime() with an incompatible struct utimbuf that
+ * does not match with the traditional time_t [2].
+ */
+
+/* Portable utime() that takes it args like real Unix systems
+ * Accepts: file path
+ * traditional utime() argument
+ * Returns: utime() results
+ */
+
+int portable_utime (char *file,time_t timep[2])
+{
+ struct utimbuf times;
+ times.actime = timep[0]; /* copy the portable values */
+ times.modtime = timep[1];
+ return utime (file,&times); /* now call Amiga's routine */
+}
diff --git a/imap/src/osdep/amiga/os_ami.h b/imap/src/osdep/amiga/os_ami.h
new file mode 100644
index 00000000..293005ca
--- /dev/null
+++ b/imap/src/osdep/amiga/os_ami.h
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 15 September 2006
+ */
+
+#include <proto/socket.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define direct dirent
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_ami.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
diff --git a/imap/src/osdep/amiga/phile.c b/imap/src/osdep/amiga/phile.c
new file mode 100644
index 00000000..ce72d0a9
--- /dev/null
+++ b/imap/src/osdep/amiga/phile.c
@@ -0,0 +1,553 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1993
+ * Last Edited: 9 May 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "rfc822.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Types returned from phile_type() */
+
+#define PTYPEBINARY 0 /* binary data */
+#define PTYPETEXT 1 /* textual data */
+#define PTYPECRTEXT 2 /* textual data with CR */
+#define PTYPE8 4 /* textual 8bit data */
+#define PTYPEISO2022JP 8 /* textual Japanese */
+#define PTYPEISO2022KR 16 /* textual Korean */
+#define PTYPEISO2022CN 32 /* textual Chinese */
+
+
+/* PHILE I/O stream local data */
+
+typedef struct phile_local {
+ ENVELOPE *env; /* file envelope */
+ BODY *body; /* file body */
+ char tmp[MAILTMPLEN]; /* temporary buffer */
+} PHILELOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((PHILELOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *phile_valid (char *name);
+int phile_isvalid (char *name,char *tmp);
+void *phile_parameters (long function,void *value);
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void phile_list (MAILSTREAM *stream,char *ref,char *pat);
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long phile_create (MAILSTREAM *stream,char *mailbox);
+long phile_delete (MAILSTREAM *stream,char *mailbox);
+long phile_rename (MAILSTREAM *stream,char *old,char *newname);
+long phile_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *phile_open (MAILSTREAM *stream);
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j);
+void phile_close (MAILSTREAM *stream,long options);
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long phile_ping (MAILSTREAM *stream);
+void phile_check (MAILSTREAM *stream);
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options);
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* File routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER philedriver = {
+ "phile", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_READONLY|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ phile_valid, /* mailbox is valid for us */
+ phile_parameters, /* manipulate parameters */
+ phile_scan, /* scan mailboxes */
+ phile_list, /* list mailboxes */
+ phile_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ phile_status, /* status of mailbox */
+ phile_open, /* open mailbox */
+ phile_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ phile_structure, /* fetch message envelopes */
+ phile_header, /* fetch message header only */
+ phile_text, /* fetch message body only */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ phile_ping, /* ping mailbox to see if still alive */
+ phile_check, /* check for new messages */
+ phile_expunge, /* expunge deleted messages */
+ phile_copy, /* copy messages to another mailbox */
+ phile_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM phileproto = {&philedriver};
+
+/* File validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *phile_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return phile_isvalid (name,tmp) ? &philedriver : NIL;
+}
+
+
+/* File test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int phile_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ char *s;
+ /* INBOX never accepted, any other name is */
+ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
+ !(sbuf.st_mode & S_IFDIR) &&
+ /* only allow empty files if no empty proto
+ or if #ftp */
+ (sbuf.st_size || !default_proto (T) ||
+ ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/'))));
+}
+
+/* File manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *phile_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* File mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* File list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* File list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+
+/* File status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long phile_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ char *s,tmp[MAILTMPLEN];
+ MAILSTATUS status;
+ struct stat sbuf;
+ long ret = NIL;
+ if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) {
+ status.flags = flags; /* return status values */
+ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1;
+ status.messages = status.recent = status.uidnext = 1;
+ status.uidvalidity = sbuf.st_mtime;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = LONGT; /* success */
+ }
+ return ret;
+}
+
+/* File open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *phile_open (MAILSTREAM *stream)
+{
+ int i,k,fd;
+ unsigned long j,m;
+ char *s,tmp[MAILTMPLEN];
+ struct passwd *pw;
+ struct stat sbuf;
+ struct tm *t;
+ MESSAGECACHE *elt;
+ SIZEDTEXT *buf;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &phileproto;
+ if (stream->local) fatal ("phile recycle stream");
+ /* open associated file */
+ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) ||
+ (fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Unable to open file %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ stream->local = fs_get (sizeof (PHILELOCAL));
+ mail_exists (stream,1); /* make sure upper level knows */
+ mail_recent (stream,1);
+ elt = mail_elt (stream,1); /* instantiate cache element */
+ elt->valid = elt->recent = T; /* mark valid flags */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = T; /* make sure upper level knows readonly */
+ /* instantiate a new envelope and body */
+ LOCAL->env = mail_newenvelope ();
+ LOCAL->body = mail_newbody ();
+
+ t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */
+ i = t->tm_hour * 60 + t->tm_min;
+ k = t->tm_yday;
+ t = localtime(&sbuf.st_mtime);/* get local time */
+ /* calculate time delta */
+ i = t->tm_hour * 60 + t->tm_min - i;
+ if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
+ k = abs (i); /* time from UTC either way */
+ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
+ elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
+ elt->year = t->tm_year - (BASEYEAR - 1900);
+ elt->zoccident = (k == i) ? 0 : 1;
+ elt->zhours = k/60;
+ elt->zminutes = k % 60;
+ sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
+ days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
+ elt->zhours,elt->zminutes);
+ /* set up Date field */
+ LOCAL->env->date = cpystr (tmp);
+
+ /* fill in From field from file owner */
+ LOCAL->env->from = mail_newaddr ();
+ if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
+ else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
+ LOCAL->env->from->mailbox = cpystr (tmp);
+ LOCAL->env->from->host = cpystr (mylocalhost ());
+ /* set subject to be mailbox name */
+ LOCAL->env->subject = cpystr (stream->mailbox);
+ /* slurp the data */
+ (buf = &elt->private.special.text)->size = sbuf.st_size;
+ read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size);
+ buf->data[buf->size] = '\0';
+ close (fd); /* close the file */
+ /* analyze data type */
+ if (i = phile_type (buf->data,buf->size,&j)) {
+ LOCAL->body->type = TYPETEXT;
+ LOCAL->body->subtype = cpystr ("PLAIN");
+ if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */
+ s = (char *) buf->data; /* make copy of UNIX-format string */
+ buf->data = NIL; /* zap the buffer */
+ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size);
+ fs_give ((void **) &s); /* flush original UNIX-format string */
+ }
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("charset");
+ LOCAL->body->parameter->value =
+ cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
+ (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
+ (i & PTYPEISO2022CN) ? "ISO-2022-CN" :
+ (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII");
+ LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
+ LOCAL->body->size.lines = j;
+ }
+ else { /* binary data */
+ LOCAL->body->type = TYPEAPPLICATION;
+ LOCAL->body->subtype = cpystr ("OCTET-STREAM");
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("name");
+ LOCAL->body->parameter->value =
+ cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
+ LOCAL->body->encoding = ENCBASE64;
+ buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size);
+ fs_give ((void **) &s); /* flush originary binary contents */
+ }
+ phile_header (stream,1,&j,NIL);
+ LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size;
+ elt->rfc822_size = j + buf->size;
+ /* only one message ever... */
+ stream->uid_validity = sbuf.st_mtime;
+ stream->uid_last = elt->private.uid = 1;
+ return stream; /* return stream alive to caller */
+}
+
+/* File determine data type
+ * Accepts: data to examine
+ * size of data
+ * pointer to line count return
+ * Returns: PTYPE mask of data type
+ */
+
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j)
+{
+ int ret = PTYPETEXT;
+ char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ *j = 0; /* no lines */
+ /* check type of every character */
+ while (i--) switch (charvec[*s++]) {
+ case 'A':
+ ret |= PTYPE8; /* 8bit character */
+ break;
+ case 'a':
+ break; /* ASCII character */
+ case 'b':
+ return PTYPEBINARY; /* binary byte seen, stop immediately */
+ case 'c':
+ ret |= PTYPECRTEXT; /* CR indicates Internet text */
+ break;
+ case 'e': /* ESC */
+ if (*s == '$') { /* ISO-2022 sequence? */
+ switch (s[1]) {
+ case 'B': case '@': ret |= PTYPEISO2022JP; break;
+ case ')':
+ switch (s[2]) {
+ case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break;
+ case 'C': ret |= PTYPEISO2022KR; break;
+ }
+ case '*':
+ switch (s[2]) {
+ case 'H': ret |= PTYPEISO2022CN; break;
+ }
+ case '+':
+ switch (s[2]) {
+ case 'I': case 'J': case 'K': case 'L': case 'M':
+ ret |= PTYPEISO2022CN; break;
+ }
+ }
+ }
+ break;
+ case 'l': /* newline */
+ (*j)++;
+ break;
+ }
+ return ret; /* return type of data */
+}
+
+/* File close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void phile_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* File fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ if (body) *body = LOCAL->body;
+ return LOCAL->env; /* return the envelope */
+}
+
+
+/* File fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body);
+ *length = strlen (LOCAL->tmp);
+ return LOCAL->tmp;
+}
+
+
+/* File fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text;
+ if (!(flags &FT_PEEK)) { /* mark message as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,buf->data,buf->size);
+ return T;
+}
+
+/* File ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long phile_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+/* File check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void phile_check (MAILSTREAM *stream)
+{
+ mm_log ("Check completed",NIL);
+}
+
+/* File expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* File copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format",
+ stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* File append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = mailboxfile (file,mailbox);
+ if (s && *s)
+ sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s);
+ else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/pmatch.c b/imap/src/osdep/amiga/pmatch.c
new file mode 100644
index 00000000..a7539e50
--- /dev/null
+++ b/imap/src/osdep/amiga/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-dependent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL;
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL;
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/pseudo.c b/imap/src/osdep/amiga/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/amiga/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/amiga/pseudo.h b/imap/src/osdep/amiga/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/amiga/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/amiga/scandir.c b/imap/src/osdep/amiga/scandir.c
new file mode 100644
index 00000000..878343e8
--- /dev/null
+++ b/imap/src/osdep/amiga/scandir.c
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Scan directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+/* Emulator for BSD scandir() call
+ * Accepts: directory name
+ * destination pointer of names array
+ * selection function
+ * comparison function
+ * Returns: number of elements in the array or -1 if error
+ */
+
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar)
+{
+ struct direct *p,*d,**names;
+ int nitems;
+ struct stat stb;
+ long nlmax;
+ DIR *dirp = opendir (dirname);/* open directory and get status poop */
+ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1;
+ nlmax = stb.st_size / 24; /* guesstimate at number of files */
+ names = (struct direct **) fs_get (nlmax * sizeof (struct direct *));
+ nitems = 0; /* initially none found */
+ while (d = readdir (dirp)) { /* read directory item */
+ /* matches select criterion? */
+ if (select && !(*select) (d)) continue;
+ /* get size of direct record for this file */
+ p = (struct direct *) fs_get (DIR_SIZE (d));
+ p->d_ino = d->d_ino; /* copy the poop */
+ strcpy (p->d_name,d->d_name);
+ if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */
+ void *s = (void *) names; /* stupid language */
+ nlmax *= 2; /* double it */
+ fs_resize ((void **) &s,nlmax * sizeof (struct direct *));
+ names = (struct direct **) s;
+ }
+ names[nitems - 1] = p; /* store this file there */
+ }
+ closedir (dirp); /* done with directory */
+ /* sort if necessary */
+ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar);
+ *namelist = names; /* return directory */
+ return nitems; /* and size */
+}
+
+/* Alphabetic file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int alphasort (void *d1,void *d2)
+{
+ return strcmp ((*(struct direct **) d1)->d_name,
+ (*(struct direct **) d2)->d_name);
+}
diff --git a/imap/src/osdep/amiga/ssl_none.c b/imap/src/osdep/amiga/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/amiga/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/amiga/tcp_ami.c b/imap/src/osdep/amiga/tcp_ami.c
new file mode 100644
index 00000000..974717c0
--- /dev/null
+++ b/imap/src/osdep/amiga/tcp_ami.c
@@ -0,0 +1,797 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Amiga TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+#undef write /* don't use redefined write() */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+extern long maxposint; /* get this from write.c */
+
+/* Local function prototypes */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ int sock = -1;
+ int ctr = 0;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons (port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Amiga programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (hostname,host+1); /* yes, copy number part */
+ hostname[(strlen (hostname))-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (hostname)) == -1)
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host); /* hostname is user's argument */
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ /* get an open socket for this system */
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(he = gethostbyname (lcase (strcpy (hostname,host)))))
+ sprintf (tmp,"No such host as %.80s",host);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+#ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */
+ for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+#else /* the one true address then */
+ memcpy (&sin.sin_addr,he->h_addr,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+#endif
+ }
+ }
+ if (sock >= 0) { /* won */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ /* stash in the snuck-in byte */
+ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ else if (!silent) mm_log (tmp,ERROR);
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * pointer to "first byte read in" storage or NIL
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
+ unsigned long port)
+{
+ int i,ti,sock,flgs;
+ time_t now;
+ struct protoent *pt = getprotobyname ("tcp");
+ fd_set fds,efds;
+ struct timeval tmo;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* make a socket */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return -1;
+ }
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ close (sock);
+ errno = EMFILE;
+ return -1;
+ }
+ flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
+ /* set non-blocking if want open timeout */
+ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
+ /* open connection */
+ while ((i = connect (sock,(struct sockaddr *) sin,
+ sizeof (struct sockaddr_in))) < 0 && (errno == EINTR));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (i < 0) switch (errno) { /* failed? */
+ case EAGAIN: /* DG brain damage */
+ case EINPROGRESS: /* what we expect to happen */
+ case EALREADY: /* or another form of it */
+ case EISCONN: /* restart after interrupt? */
+ case EADDRINUSE: /* restart after interrupt? */
+ break; /* well, not really, it was interrupted */
+ default:
+ sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
+ close (sock); /* flush socket */
+ return -1;
+ }
+
+ if (ctr) { /* want open timeout */
+ now = time (0); /* open timeout */
+ ti = ttmo_open ? now + ttmo_open : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* block for error or readable */
+ FD_SET (sock,&efds);
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* success, make sure really connected */
+ fcntl (sock,F_SETFL,flgs);/* restore blocking status */
+ /* This used to be a zero-byte read(), but that crashes Solaris */
+ /* get socket status */
+ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
+ }
+ if (i <= 0) { /* timeout or error? */
+ i = i ? errno : ETIMEDOUT;/* determine error code */
+ close (sock); /* flush socket */
+ errno = i; /* return error code */
+ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port,
+ strerror (errno));
+ return -1;
+ }
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* disabled */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi < 0) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* select says there's data to read? */
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi < 0) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* got data? */
+ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpso < 0) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpso+1,0,&fds,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* OK to send data? */
+ while (((i = write (stream->tcpso,string,size)) < 0) &&(errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ close (stream->tcpsi); /* nuke the socket */
+ if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ (getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+static char *myClientAddr = NIL;
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ cpystr (getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ "UNKNOWN" : ((sin.sin_family == AF_INET) ?
+ inet_ntoa (sin.sin_addr) : "NON-IPv4"));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+static char *myClientHost = NIL;
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ cpystr ("UNKNOWN") : ((sin.sin_family == AF_INET) ?
+ tcp_name (&sin,T) : cpystr ("NON-IPv4"));
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+static char *myServerAddr = NIL;
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ cpystr (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ "UNKNOWN" : ((sin.sin_family == AF_INET) ?
+ inet_ntoa (sin.sin_addr) : "NON-IPv4"));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static char *myServerHost = NIL;
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ /* get stdin's name */
+ if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that Amiga requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ?
+ (char *) he->h_name : name;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/tcp_ami.h b/imap/src/osdep/amiga/tcp_ami.h
new file mode 100644
index 00000000..d4b29797
--- /dev/null
+++ b/imap/src/osdep/amiga/tcp_ami.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ char *remotehost; /* remote host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/amiga/tenex.c b/imap/src/osdep/amiga/tenex.c
new file mode 100644
index 00000000..eee61fba
--- /dev/null
+++ b/imap/src/osdep/amiga/tenex.c
@@ -0,0 +1,1470 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *tmp);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+char *tenex_file (char *dst,char *name);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ tenex_status, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_fast, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = tenex_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!tenex_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt");
+ return ret; /* return success */
+}
+
+/* Tenex Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&tenexproto);
+ if (stream->local) fatal ("tenex recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!tenex_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ LOCAL->fd = fd; /* bind the file */
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch fast data
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = strcrlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return (char *) LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ MM_FLAGS (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,i);
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ tenex_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* Tenex mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void tenex_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\n",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* Tenex mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return LONGT;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (tenex_file(file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&tenexproto);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message))
+ ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+
+/* Tenex mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *tenex_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "mail.txt") : s;
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/tz_bsd.c b/imap/src/osdep/amiga/tz_bsd.c
new file mode 100644
index 00000000..75ee624c
--- /dev/null
+++ b/imap/src/osdep/amiga/tz_bsd.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ /* append timezone from tm struct */
+ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone);
+}
diff --git a/imap/src/osdep/amiga/unix.c b/imap/src/osdep/amiga/unix.c
new file mode 100644
index 00000000..be3c437b
--- /dev/null
+++ b/imap/src/osdep/amiga/unix.c
@@ -0,0 +1,2708 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "unix.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time (for mbox driver) */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+long unix_isvalid_fd (int fd);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* mbox mail routines */
+
+/* Function prototypes */
+
+DRIVER *mbox_valid (char *name);
+long mbox_create (MAILSTREAM *stream,char *mailbox);
+long mbox_delete (MAILSTREAM *stream,char *mailbox);
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbox_open (MAILSTREAM *stream);
+long mbox_ping (MAILSTREAM *stream);
+void mbox_check (MAILSTREAM *stream);
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* OK if mailbox format good */
+ if (unix_isvalid_fd (fd)) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* UNIX mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long unix_isvalid_fd (int fd)
+{
+ int zn;
+ int ret = NIL;
+ char tmp[MAILTMPLEN],*s,*t,c = '\n';
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
+ for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');)
+ c = *s++;
+ if (c == '\n') VALID (s,t,ret,zn);
+ }
+ return ret; /* return what we should */
+}
+
+
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ /* write the pseudo-header */
+ sprintf (s += strlen (s),
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = unix_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ unix_unlock (fd,NIL,&lockx);
+ unix_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ unix_unlock (fd,NIL,&lockx);
+ }
+ unix_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&unixproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,&lock,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && unix_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ DRIVER *d;
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ (d && strcmp (d->name,"mbox") && !(d->flags & DR_DISABLE));
+ d = d->next); /* see if mbox driver active */
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!((d && mbox_valid (mailbox) && (mailbox = "mbox")) ||
+ unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);/* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df); /* note that unix_unlock() released the fd */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out CRs (note also copies NUL) */
+ for (x = tmp; x = strchr (x,'\r'); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ MM_LOG (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ if ((i == 1) && (*s == '\n')) {
+ k = 2;
+ m = 1;
+ }
+ else if ((i == 2) && (*s == '\r') && (s[1] == '\n'))
+ k = m = 2;
+ else k = m = 0; /* file does not end with newline! */
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ /* flush ending blank line */
+ elt->private.msg.text.text.size -= m;
+ elt->rfc822_size -= k;
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ return strlen (hdr); /* return header length */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 1;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\n",1);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
+
+/* MBOX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mboxdriver = {
+ "mbox", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
+ (DRIVER *) NIL, /* next driver */
+ mbox_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* find mailboxes */
+ unix_lsub, /* find subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbox_create, /* create mailbox */
+ mbox_delete, /* delete mailbox */
+ mbox_rename, /* rename mailbox */
+ mbox_status, /* status of mailbox */
+ mbox_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbox_ping, /* ping mailbox to see if still alive */
+ mbox_check, /* check for new messages */
+ mbox_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ mbox_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mboxproto = {&mboxdriver};
+
+/* MBOX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbox_valid (char *name)
+{
+ /* only INBOX, mbox must exist */
+ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) &&
+ (unix_valid (sysinbox()) || !errno || (errno == ENOENT)))
+ return &mboxdriver;
+ return NIL; /* can't win (yet, anyway) */
+}
+
+/* MBOX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!compare_cstring (mailbox,"INBOX")) return unix_create (NIL,"mbox");
+ sprintf (tmp,"Can't create non-INBOX name as mbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+
+/* MBOX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbox_rename (stream,mailbox,NIL);
+}
+
+
+/* MBOX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char tmp[MAILTMPLEN];
+ long ret = unix_rename (stream,"~/mbox",newname);
+ /* recreate file if renamed INBOX */
+ if (ret) unix_create (NIL,"mbox");
+ else MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success */
+}
+
+/* MBOX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ if (!status.recent && /* calculate post-snarf results */
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBOX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbox_open (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ unsigned long recent = 0;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mboxproto;
+ /* change mailbox file name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr ("mbox");
+ /* open mailbox, snarf new mail */
+ if (!(unix_open (stream) && mbox_ping (stream))) return NIL;
+ stream->inbox = T; /* mark that this is an INBOX */
+ /* notify upper level of mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
+ mail_recent (stream,recent); /* including recent messages */
+ return stream;
+}
+
+/* MBOX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+static int snarfed = 0; /* number of snarfs */
+
+long mbox_ping (MAILSTREAM *stream)
+{
+ int sfd;
+ unsigned long size;
+ struct stat sbuf;
+ char *s;
+ DOTLOCK lock,lockx;
+ /* time to try snarf and sysinbox non-empty? */
+ if (LOCAL && !stream->rdonly && !stream->lock &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ !stat (sysinbox (),&sbuf) && sbuf.st_size) {
+ MM_CRITICAL (stream); /* yes, go critical */
+ /* open and lock sysinbox */
+ if ((sfd = unix_lock (sysinbox (),O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) >= 0) {
+ /* locked sysinbox in good format? */
+ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) ||
+ !unix_isvalid_fd (sfd)) {
+ sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format",
+ sysinbox ());
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sysinbox good, parse and excl-lock mbox */
+ else if (unix_parse (stream,&lock,LOCK_EX)) {
+ lseek (sfd,0,L_SET); /* read entire sysinbox into memory */
+ read (sfd,s = (char *) fs_get (size + 1),size);
+ s[size] = '\0'; /* tie it off */
+ /* append to end of mbox */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+
+ /* copy to mbox */
+ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) {
+ sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ }
+ /* sysinbox better not have changed */
+ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) {
+ sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu",
+ sysinbox (),size,(unsigned long) sbuf.st_size);
+ MM_LOG (LOCAL->buf,ERROR);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ /* Believe it or not, a Singaporean government system actually had
+ * symlinks from /var/mail/user to ~user/mbox. To compound this
+ * error, they used an SVR4 system; BSD and OSF locks would have
+ * prevented it but not SVR4 locks.
+ */
+ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size))
+ syslog (LOG_ALERT,"File %s and %s are the same file!",
+ sysinbox (),stream->mailbox);
+ }
+ else { /* data copied OK */
+ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */
+ if (!snarfed++) { /* have we snarfed before? */
+ /* syslog if server, else user log */
+ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s",
+ size,stream->mailbox,sysinbox ());
+ if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ "unknown"))
+ syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ());
+ else MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ /* done with sysinbox text */
+ fs_give ((void **) &s);
+ /* all done with mbox */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ /* all done with sysinbox */
+ unix_unlock (sfd,NIL,&lockx);
+ }
+ MM_NOCRITICAL (stream); /* done with critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ return unix_ping (stream); /* do the unix routine now */
+}
+
+/* MBOX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mbox_check (MAILSTREAM *stream)
+{
+ /* do local ping, then do unix routine */
+ if (mbox_ping (stream)) unix_check (stream);
+}
+
+
+/* MBOX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = unix_expunge (stream,sequence,options);
+ mbox_ping (stream); /* do local ping */
+ return ret;
+}
+
+
+/* MBOX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ if (mbox_valid (mailbox)) return unix_append (stream,"mbox",af,data);
+ sprintf (tmp,"Can't append to that name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/unix.h b/imap/src/osdep/amiga/unix.h
new file mode 100644
index 00000000..76cde8b5
--- /dev/null
+++ b/imap/src/osdep/amiga/unix.h
@@ -0,0 +1,219 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines, Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ int remote = 0; \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ { \
+ while (x[zn-13] == ' ') zn--; \
+ x += zn - 12; \
+ remote = 1; \
+ } \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ else if ( (x[-13] == ' ') && (x[-16] == ' ') \
+ && (x[-20] ==' ') && \
+ ( ((x[-22] == ' ') && (x[-23] == ',')) || \
+ ((x[-23] == ' ') && (x[-24] == ',')) ) ) { \
+ char weekday[4]={0,}, month[4]={0,}, time[11]={0,}; \
+ char tzone[4]={0,}; \
+ char realtime[80]; \
+ int day,year,start=-26; \
+ if (x[-23] == ' ') x--; \
+ sscanf(&x[start],"%3c, %d %s %d %s %s", \
+ weekday,&day,month,&year,time,tzone); \
+ sprintf(realtime,"%s %s %2d %s %d %s", \
+ weekday,month,day,time, \
+ ( (year < 100) ? year+1900 : year),tzone); \
+ if (remote) \
+ strcat(realtime," remote from "); \
+ else \
+ strcat(realtime,"\n"); \
+ strncpy(&x[start],realtime,strlen(realtime)); \
+ zn = -2; \
+ ti = -7; \
+ } \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ else if (x[-9] == ' ') { \
+ if ( ( (x[-12] == ' ') && (x[-16] == ' ') && \
+ ( ((x[-18] == ' ') && (x[-19] == ',') ) || \
+ ((x[-19] == ' ') && (x[-20] == ',')) ) \
+ || \
+ ((x[-14] == ' ') && (x[-18] == ' ') && \
+ ( ((x[-20] == ' ') && (x[-21] == ',') ) || \
+ ((x[-21] == ' ') && (x[-22] == ',')) ) ) ) ) { \
+ char weekday[4]={0,}, month[4]={0,},time[11]={0,}; \
+ int day,year,start=-24; \
+ char realtime[80]; \
+ if (x[-12] == ' ') x++; \
+ if (x[-19] == ' ') x++; \
+ sscanf(&x[start],"%3c, %d %3c %d %s",weekday, \
+ &day,month,&year,time); \
+ sprintf(realtime,"%s %s %2d %s %d",weekday,month,day,time,\
+ ( (year < 100) ? year+1900 : year)); \
+ if (remote) \
+ strcat(realtime," remote from "); \
+ else \
+ strcat(realtime,"\n"); \
+ strncpy(&x[start],realtime,strlen(realtime)); \
+ ti=-5; \
+ zn=0; \
+ } \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Amiga From user Wed, 6 Dec 92 05:53:22 who did this !!!
+ * CHANGED in place to
+ * From user Wed Dec 2 05:53:22 1992
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+#define CHUNK 16384 /* read-in chunk size */
diff --git a/imap/src/osdep/amiga/write.c b/imap/src/osdep/amiga/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/amiga/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/dos/bezrkdos.c b/imap/src/osdep/dos/bezrkdos.c
new file mode 100644
index 00000000..81617380
--- /dev/null
+++ b/imap/src/osdep/dos/bezrkdos.c
@@ -0,0 +1,901 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Berkeley mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 June 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to those Merry Marvels of Musical
+ * Madness . . .
+ * -> The Incomparable Leland Stanford Junior University Marching Band <-
+ * who entertain, awaken, and outrage Stanford fans in the fact of repeated
+ * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
+ * HUSKY FEVER!!!].
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys\stat.h>
+#include <dos.h>
+#include "rfc822.h"
+#include "dummy.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* Berkeley I/O stream local data */
+
+typedef struct bezerk_local {
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ char *buf; /* temporary buffer */
+} BEZERKLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((BEZERKLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *bezerk_valid (char *name);
+long bezerk_isvalid (char *name,char *tmp);
+int bezerk_valid_line (char *s,char **rx,int *rzn);
+void *bezerk_parameters (long function,void *value);
+void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void bezerk_list (MAILSTREAM *stream,char *ref,char *pat);
+void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long bezerk_create (MAILSTREAM *stream,char *mailbox);
+long bezerk_delete (MAILSTREAM *stream,char *mailbox);
+long bezerk_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *bezerk_open (MAILSTREAM *stream);
+void bezerk_close (MAILSTREAM *stream,long options);
+char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,
+ long flags);
+long bezerk_ping (MAILSTREAM *stream);
+void bezerk_check (MAILSTREAM *stream);
+long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options);
+long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+void bezerk_gc (MAILSTREAM *stream,long gcflags);
+char *bezerk_file (char *dst,char *name);
+long bezerk_badname (char *tmp,char *s);
+long bezerk_parse (MAILSTREAM *stream);
+unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Berkeley mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER bezerkdriver = {
+ "bezerk", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ bezerk_valid, /* mailbox is valid for us */
+ bezerk_parameters, /* manipulate parameters */
+ bezerk_scan, /* scan mailboxes */
+ bezerk_list, /* list mailboxes */
+ bezerk_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ bezerk_create, /* create mailbox */
+ bezerk_delete, /* delete mailbox */
+ bezerk_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ bezerk_open, /* open mailbox */
+ bezerk_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ bezerk_header, /* fetch message header */
+ bezerk_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ bezerk_ping, /* ping mailbox to see if still alive */
+ bezerk_check, /* check for new messages */
+ bezerk_expunge, /* expunge deleted messages */
+ bezerk_copy, /* copy messages to another mailbox */
+ bezerk_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM bezerkproto = {&bezerkdriver};
+
+/* Berkeley mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *bezerk_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL;
+}
+
+
+/* Berkeley mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+long bezerk_isvalid (char *name,char *tmp)
+{
+ int fd;
+ long ret = NIL;
+ struct stat sbuf;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ errno = -1; /* in case bezerk_valid_line fails */
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0)
+ ret = bezerk_valid_line (tmp,NIL,NIL);
+ close (fd); /* close the file */
+ }
+ }
+ /* in case INBOX but not bezerk format */
+ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+int bezerk_valid_line (char *s,char **rx,int *rzn)
+{
+ char *x;
+ int zn;
+ int ti = 0;
+ /* line must begin with "From " */
+ if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
+ (s[4] != ' ')) return NIL;
+ /* find end of line */
+ for (x = s + 5; *x && *x != '\012'; x++);
+ if (!x) return NIL; /* end of line not found */
+ if (x[-1] == '\015') x--; /* ignore CR */
+ if ((x - s < 27)) return NIL; /* line too short */
+ if (x - s >= 41) { /* possible search for " remote from " */
+ for (zn = -1; x[zn] != ' '; zn--);
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
+ x += zn - 12;
+ }
+ if (x[-5] == ' ') { /* ends with year? */
+ /* no timezone? */
+ if (x[-8] == ':') zn = 0,ti = -5;
+ /* three letter timezone? */
+ else if (x[-9] == ' ') ti = zn = -9;
+ /* numeric timezone? */
+ else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
+ }
+ else if (x[-4] == ' ') { /* no year and three leter timezone? */
+ if (x[-9] == ' ') zn = -4,ti = -9;
+ }
+ else if (x[-6] == ' ') { /* no year and numeric timezone? */
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
+ zn = -6,ti = -11;
+ }
+ /* time must be www mmm dd hh:mm[:ss] */
+ if (ti && !((x[ti - 3] == ':') &&
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
+ (x[ti - 11] == ' '))) return NIL;
+ if (rx) *rx = x; /* set return values */
+ if (rzn) *rzn = zn;
+ return ti;
+}
+
+/* Berkeley manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *bezerk_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* Berkeley mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Berkeley mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void bezerk_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (stream,ref,pat);
+}
+
+
+/* Berkeley mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (stream,ref,pat);
+}
+
+/* Berkeley mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_create (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_create (stream,mailbox);
+}
+
+
+/* Berkeley mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_delete (stream,mailbox);
+}
+
+
+/* Berkeley mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return dummy_rename (stream,old,newname);
+}
+
+/* Berkeley mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *bezerk_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char *s;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &bezerkproto;
+ if (stream->local) fatal ("bezerk recycle stream");
+ if (!mailboxfile (tmp,stream->mailbox))
+ return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox);
+ if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->rdonly = T; /* this driver is readonly */
+ stream->local = fs_get (sizeof (BEZERKLOCAL));
+ /* canonicalize the stream mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = fd; /* note the file */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->buf = NIL; /* initially no local buffer */
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!bezerk_ping (stream)) return NIL;
+ if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL;
+ stream->perm_user_flags = NIL;
+ return stream; /* return stream to caller */
+}
+
+/* Berkeley mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void bezerk_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T;
+ if (options & CL_EXPUNGE) bezerk_expunge (stream,NIL,NIL);
+ close (LOCAL->fd); /* close the local file */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Berkeley mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char tmp[MAILTMPLEN];
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,(size_t) *length);
+ return LOCAL->buf;
+}
+
+
+/* Berkeley mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ FDDATA d;
+ unsigned long hdrsize,hdrpos;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* if message not seen */
+ /* mark message as seen */
+ if (elt->seen && !(flags & FT_PEEK)) {
+ elt->seen = T;
+ mm_flags (stream,msgno);
+ }
+ /* get location of text data */
+ hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize);
+ d.fd = LOCAL->fd; /* set initial stringstruct */
+ d.pos = hdrpos + hdrsize;
+ /* flush old buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
+ INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
+ return T; /* success */
+}
+
+/* Berkeley mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long bezerk_ping (MAILSTREAM *stream)
+{
+ /* punt if stream no longer alive */
+ if (!(stream && LOCAL)) return NIL;
+ /* parse mailbox, punt if parse dies */
+ return (bezerk_parse (stream)) ? T : NIL;
+}
+
+
+/* Berkeley mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void bezerk_check (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ if (bezerk_ping (stream)) { /* ping mailbox */
+ /* get new message status */
+ while (i <= stream->nmsgs) mail_elt (stream,i++);
+ mm_log ("Check completed",(long) NIL);
+ }
+}
+
+/* Berkeley mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ return LONGT;
+}
+
+/* Berkeley mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ int fd;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if (!bezerk_isvalid (mailbox,tmp) && errno) {
+ if (errno == ENOENT)
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ else if (pc) return (*pc) (stream,sequence,mailbox,options);
+ else if (mailboxfile (tmp,mailbox)) {
+ sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ else bezerk_badname (tmp,mailbox);
+ return NIL;
+ }
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ /* for each requested message */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
+ /* number of bytes to copy */
+ j = elt->private.msg.full.offset + elt->rfc822_size;
+ do { /* read from source position */
+ k = min (j,(unsigned long) MAILTMPLEN);
+ read (LOCAL->fd,tmp,(unsigned int) k);
+ if (write (fd,tmp,(unsigned int) k) < 0) {
+ sprintf (tmp,"Unable to write message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ chsize (fd,sbuf.st_size);
+ close (fd); /* punt */
+ mm_nocritical (stream);
+ return NIL;
+ }
+ } while (j -= k); /* until done */
+ }
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
+ if (mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return T;
+}
+
+/* Berkeley mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN MAILTMPLEN
+
+long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &bezerkproto;
+ /* make sure valid mailbox */
+ if (!bezerk_isvalid (mailbox,tmp) && errno) {
+ if (errno == ENOENT) {
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
+ bezerk_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ }
+ else if (mailboxfile (tmp,mailbox)) {
+ sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ else bezerk_badname (tmp,mailbox);
+ return NIL;
+ }
+ tzset (); /* initialize timezone stuff */
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!bezerk_append_msg (stream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (stream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = sbuf.st_size; /* size of scratch file */
+
+ mm_critical (stream); /* go critical */
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ while (i) /* until written all bytes */
+ if ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) &&
+ (fwrite (buf,1,j,df) == j)) i -= j;
+ fclose (sf); /* done with scratch file */
+ /* make sure append wins */
+ if (i || (fflush (df) == EOF)) {
+ chsize (fd,sbuf.st_size); /* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ret = NIL; /* return error */
+ }
+ fclose (df);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ int c;
+ unsigned long i,uf;
+ char tmp[MAILTMPLEN];
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* build initial header */
+ if ((fprintf (sf,"From %s@%s %sStatus: ",
+ myusername (),mylocalhost (),date) < 0) ||
+ (f&fSEEN && (putc ('R',sf) == EOF)) ||
+ (fputs ("\nX-Status: ",sf) == EOF) ||
+ (f&fDELETED && (putc ('D',sf) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',sf) == EOF)) ||
+ (f&fANSWERED && (putc ('A',sf) == EOF)) ||
+ (f&fDRAFT && (putc ('T',sf) == EOF)) ||
+ (fputs ("\nX-Keywords:",sf) == EOF)) return NIL;
+ while (uf) /* write user flags */
+ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0)
+ return NIL;
+ /* tie off flags */
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ /* possible delimiter if line starts with F */
+ if ((c = 0xff & SNX (msg)) == 'F') {
+ /* copy line to buffer */
+ for (i = 1,tmp[0] = c; SIZE (msg) && (c != '\n') && (i < MAILTMPLEN);)
+ if (((c = 0xff & SNX (msg)) != '\r') || !(SIZE (msg)) ||
+ (CHR (msg) != '\n')) tmp[i++] = c;
+ if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') &&
+ (tmp[4] == ' ')) { /* possible "From " line? */
+ /* yes, see if need to write a widget */
+ if (((c != '\n') || bezerk_valid_line (tmp,NIL,NIL)) &&
+ (putc ('>',sf) == EOF)) return NIL;
+ }
+ /* write buffered text */
+ if (fwrite (tmp,1,i,sf) != i) return NIL;
+ if (c == '\n') continue; /* all done if got a complete line */
+ }
+ /* copy line, toss out CR from CRLF */
+ do if (((c == '\r') && SIZE (msg) && ((c = 0xff & SNX (msg)) != '\n') &&
+ (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF)) return NIL;
+ while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T));
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long bezerk_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+/* Parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long bezerk_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
+ long i;
+ int j,ti,zn;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ bezerk_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
+ /* while there is data to read */
+ while (i = sbuf.st_size - curpos){
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ /* read first buffer's worth */
+ read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
+ tmp[j] = '\0'; /* tie off buffer */
+ if (!(ti = bezerk_valid_line (tmp,&t,&zn))) {
+ mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
+ bezerk_close (stream,NIL);
+ return NIL;
+ }
+
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* note offset of message */
+ elt->private.msg.full.offset =
+ (s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp;
+ /* generate plausable IMAPish date string */
+ db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
+ /* dd */
+ db[0] = t[ti - 2]; db[1] = t[ti - 1];
+ /* mmm */
+ db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
+ /* hh */
+ db[12] = t[ti + 1]; db[13] = t[ti + 2];
+ /* mm */
+ db[15] = t[ti + 4]; db[16] = t[ti + 5];
+ if (t[ti += 6] == ':') { /* ss if present */
+ db[18] = t[++ti]; db[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else db[18] = db[19] = '0'; /* assume 0 seconds */
+ /* yy -- advance over timezone if necessary */
+ if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
+ db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
+ t = zn ? (t + zn) : "LCL"; /* zzz */
+ db[21] = *t++; db[22] = *t++; db[23] = *t++;
+ if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0';
+ else { /* numeric time zone */
+ db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
+
+ curpos += s - tmp; /* advance position after header */
+ t = strchr (s,'\012'); /* start of next line */
+ /* find start of next message */
+ while (!(bezerk_valid_line (s,NIL,NIL))) {
+ if (t) { /* have next line? */
+ t++; /* advance to new line */
+ curpos += t - s; /* update position and size */
+ elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1));
+ s = t; /* move to next line */
+ t = strchr (s,'\012');
+ }
+ else { /* try next buffer */
+ j = strlen (s); /* length of unread data in buffer */
+ if ((i = sbuf.st_size - curpos) && (i != j)) {
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ /* read another buffer's worth */
+ read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
+ tmp[j] = '\0'; /* tie off buffer */
+ if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox");
+ }
+ else {
+ curpos += j; /* last bit of data */
+ elt->rfc822_size += j;
+ break;
+ }
+ }
+ }
+ }
+ /* update parsed file size */
+ LOCAL->filesize = sbuf.st_size;
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return T; /* return the winnage */
+}
+
+/* Berkeley locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ long siz;
+ size_t i = 0;
+ char c = '\0';
+ char *s;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ long pos = elt->private.special.offset + elt->private.msg.full.offset;
+ /* is size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ /* get to header position */
+ lseek (LOCAL->fd,pos,SEEK_SET);
+ /* search message for CRLF CRLF */
+ for (siz = 1; siz <= elt->rfc822_size; siz++) {
+ if (!i && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
+ return pos;
+ else i--;
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz);
+ return pos; /* return to caller */
+ }
+ else if ((c == '\012') && (*s == '\015')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return pos; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ }
+ return pos; /* have position */
+}
diff --git a/imap/src/osdep/dos/drivers.bat b/imap/src/osdep/dos/drivers.bat
new file mode 100644
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/dos/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/dos/drivraux.bat b/imap/src/osdep/dos/drivraux.bat
new file mode 100644
index 00000000..a0b354cd
--- /dev/null
+++ b/imap/src/osdep/dos/drivraux.bat
@@ -0,0 +1,28 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for DOS
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/dos/dummy.h b/imap/src/osdep/dos/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/dos/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/dos/dummydos.c b/imap/src/osdep/dos/dummydos.c
new file mode 100644
index 00000000..4bf147b8
--- /dev/null
+++ b/imap/src/osdep/dos/dummydos.c
@@ -0,0 +1,689 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for DOS
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long dummy_badname (char *tmp,char *s);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+ /* driver parameters */
+static char *file_extension = NIL;
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') &&
+ (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_EXTENSION:
+ if (file_extension) fs_give ((void **) &file_extension);
+ if (*(char *) value) file_extension = cpystr ((char *) value);
+ case GET_EXTENSION:
+ ret = (void *) file_extension;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+#define LISTTMPLEN 128
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[LISTTMPLEN],file[LISTTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOINFERIORS,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ struct find_t f;
+ struct stat sbuf;
+ char *s,tmp[LISTTMPLEN],tmpx[LISTTMPLEN];
+ char *base = (dir && (dir[0] == '\\')) ? NIL : myhomedir ();
+ /* build name */
+ if (base) sprintf (tmpx,"%s\\",base);
+ else tmpx[0] = '\0';
+ if (dir) strcat (tmpx,dir);
+ /* punt if bogus name */
+ if (!mailboxfile (tmp,tmpx)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*." : "\\*.");
+ strcat (tmp,file_extension ? file_extension : "*");
+ /* do nothing if can't open directory */
+ if (!_dos_findfirst (tmp,_A_NORMAL|_A_SUBDIR,&f)) {
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (tmpx[strlen (tmpx) - 1] == '\\') do if (*f.name != '.') {
+ if (base) sprintf (tmpx,"%s\\",base);
+ else tmpx[0] = '\0';
+ if (dir) sprintf (tmpx + strlen (tmpx),"%s%s",dir,f.name);
+ else strcat (tmpx,f.name);
+ if (mailboxfile (tmp,tmpx) && !stat (tmp,&sbuf)) {
+ /* suppress extension */
+ if (file_extension && (s = strchr (f.name,'.'))) *s = '\0';
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents);
+ strcat (tmp,"\\"); /* set up for dmatch call */
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\'))
+ dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents);
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ while (!_dos_findnext (&f));
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ int fd;
+ size_t csiz,ssiz,bsiz;
+ char *buf,tmp[MAILTMPLEN];
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !mailboxfile (tmp,name) || stat (tmp,&sbuf) || (csiz > sbuf.st_size) ||
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ return (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox)) ?
+ dummy_create_path (stream,tmp,NIL) : dummy_badname (tmp,mailbox);
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %s: %s",path,strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!mailboxfile (tmp,mailbox)) return dummy_badname (tmp,mailbox);
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %s: %s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ /* make file name */
+ if (!mailboxfile (file,old)) return dummy_badname (tmp,old);
+ /* no trailing \ allowed */
+ if (!(s = mailboxfile (tmp,newname)) || ((s = strrchr (s,'\\')) && !s[1]))
+ return dummy_badname (tmp,newname);
+ if (s) { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (file,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,file)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %s to %s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (!mailboxfile (tmp,stream->mailbox))
+ sprintf (tmp,"Can't open this name: %.80s",stream->mailbox);
+ else if (compare_cstring (stream->mailbox,"INBOX") &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ sprintf (tmp,"%s: %s",strerror (errno),stream->mailbox);
+ else {
+ if (fd >= 0) { /* if got a file */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ else fd = -1; /* a-OK */
+ }
+ if (fd < 0) { /* no file, right? */
+ if (!stream->silent) { /* only if silence not requested */
+ /* say there are 0 messages */
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+ }
+ }
+ mm_log (tmp,stream->silent ? WARN: ERROR);
+ return NIL; /* always fails */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%s: %s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long dummy_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/dos/env_dos.c b/imap/src/osdep/dos/env_dos.c
new file mode 100644
index 00000000..152dd322
--- /dev/null
+++ b/imap/src/osdep/dos/env_dos.c
@@ -0,0 +1,300 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myLocalHost = NIL; /* local host name */
+static char *myClientAddr = NIL;/* client host address */
+static char *myClientHost = NIL;/* client host name */
+static char *myServerAddr = NIL;/* server host address */
+static char *myServerHost = NIL;/* server host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static long list_max_level = 5; /* maximum level of list recursion */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,NIL,NIL};
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Dummy definitions to prevent errors */
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() ""
+#define MD5ENABLE "\\.nosuch.."
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ tzset (); /* get timezone from TZ environment stuff */
+ sprintf (date + strlen (date)," (%.50s)",
+ tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]);
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ int i;
+ char *s;
+ if (!myHomeDir) { /* get home directory name if not yet known */
+ i = strlen (myHomeDir = cpystr ((s = getenv ("HOME")) ? s : ""));
+ if (i && ((myHomeDir[i-1] == '\\') || (myHomeDir[i-1]=='/')))
+ myHomeDir[i-1] = '\0'; /* tie off trailing directory delimiter */
+ }
+ return myHomeDir;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *s;
+ char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL);
+ /* forbid extraneous extensions */
+ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) &&
+ ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) ||
+ strchr (s+1,'.'))) return NIL;
+ /* absolute path name? */
+ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name);
+ else sprintf (dst,"%s\\%s",myhomedir (),name);
+ if (ext) sprintf (dst + strlen (dst),".%s",ext);
+ return ucase (dst);
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM DEFAULTPROTO;
+ return &DEFAULTPROTO; /* return default driver's prototype */
+}
+
+/* Global data */
+
+static unsigned rndm = 0; /* initial `random' number */
+
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+/* Default mailgets routine on DOS
+ * Accepts: readin function pointer
+ * stream to use
+ * number of bytes
+ * identifier data
+ * Returns: string read in, truncated if necessary
+ *
+ * This is a sample mailgets routine. It simply truncates any data larger
+ * than 63K. On most systems, you generally don't use a mailgets
+ * routine at all, but on DOS it's required to prevent the application from
+ * crashing.
+ */
+
+static char *dos_gets_buf = NIL;
+
+char *dos_default_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md)
+{
+ readprogress_t *rp = mail_parameters (NIL,GET_READPROGRESS,NIL);
+ char *ret,tmp[MAILTMPLEN+1];
+ unsigned long i,j,dsc,rdi = 0;
+ unsigned long dos_max = 63 * 1024;
+ if (!dos_gets_buf) /* one-time initialization */
+ dos_gets_buf = (char *) fs_get ((size_t) dos_max + 1);
+ ret = (md->flags & MG_COPY) ?
+ ((char *) fs_get ((size_t) size + 1)) : dos_gets_buf;
+ if (size > dos_max) {
+ sprintf (tmp,"Mailbox %s, %s %lu[%.80s], %lu octets truncated to %ld",
+ md->stream->mailbox,(md->flags & MG_UID) ? "UID" : "#",
+ md->msgno,md->what,size,(long) dos_max);
+ mm_log (tmp,WARN); /* warn user */
+ dsc = size - dos_max; /* number of bytes to discard */
+ size = dos_max; /* maximum length string we can read */
+ }
+ else dsc = 0; /* nothing to discard */
+ dos_gets_buf[size] = '\0'; /* tie off string */
+ if (rp) for (i = size; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ (*f) (stream,j,ret + rdi);
+ (*rp) (md,rdi += j);
+ }
+ else (*f) (stream,size,dos_gets_buf);
+ /* toss out everything after that */
+ for (i = dsc; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ (*f) (stream,j,tmp);
+ if (rp) (*rp) (md,rdi += j);
+ }
+ return ret;
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/dos/env_dos.h b/imap/src/osdep/dos/env_dos.h
new file mode 100644
index 00000000..bb0e8c0a
--- /dev/null
+++ b/imap/src/osdep/dos/env_dos.h
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/MAILBOX.TMP",myhomedir ())
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *dos_default_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+long safe_write (int fd,char *buf,long nbytes);
+long random ();
+#if _MSC_VER < 700
+#define getpid random
+#endif
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/dos/fdstring.c b/imap/src/osdep/dos/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/dos/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/dos/fdstring.h b/imap/src/osdep/dos/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/dos/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/dos/fs_dos.c b/imap/src/osdep/dos/fs_dos.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/dos/fs_dos.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/dos/ftl_dos.c b/imap/src/osdep/dos/ftl_dos.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/dos/ftl_dos.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/dos/makefile b/imap/src/osdep/dos/makefile
new file mode 100644
index 00000000..40cf2276
--- /dev/null
+++ b/imap/src/osdep/dos/makefile
@@ -0,0 +1,98 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: Portable C client makefile -- MS-DOS version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+OS = wsk
+EXTRAAUTHENTICATORS=
+DEFAULTAUTHENTICATORS= ext md5 pla log
+EXTRADRIVERS =
+DRIVERS = imap nntp pop3 mtx bezerk
+DEFAULTDRIVER = mtx
+CFLAGS= -AL /DCHUNKSIZE=4096 -nologo $(EXTRACFLAGS)
+CC = cl
+
+all: mtest.exe
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_$(OS).h
+ copy os_$(OS).h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ ECHO #define DEFAULTPROTO $(DEFAULTDRIVER)proto >> LINKAGE.H
+ ECHO if (!mail_parameters (NIL,GET_GETS)) >> LINKAGE.C
+ ECHO mail_parameters (NIL,SET_GETS,(void *) dos_default_gets); >> LINKAGE.C
+ ECHO mail_versioncheck (CCLIENTVERSION); >> LINKAGE.C
+
+mtest.obj: mail.h smtp.h misc.h osdep.h mtest.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_$(OS).obj: mail.h osdep.h env_dos.h fs.h ftl.h nl.h tcp.h \
+ os_$(OS).c fs_dos.c ftl_dos.c nl_dos.c env_dos.c pmatch.c write.c
+
+mtxdos.obj: mail.h misc.h osdep.h mtxdos.c
+
+bezrkdos.obj: mail.h misc.h osdep.h bezrkdos.c
+
+dummydos.obj: mail.h dummy.h misc.h osdep.h dummydos.c
+
+cclient.lib: mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_$(OS).obj \
+ mtxdos.obj bezrkdos.obj dummydos.obj
+ erase cclient.lib
+ lib cclient +mail+misc+fdstring+flstring+netmsg+newsrc+rfc822+smanager+utf8+utf8aux+imap4r1+nntp+pop3+smtp+os_$(OS)+mtxdos+bezrkdos+dummydos;
+
+mtest.exe: cclient.lib mtest.obj
+ mtest$(OS)
diff --git a/imap/src/osdep/dos/mkautaux.bat b/imap/src/osdep/dos/mkautaux.bat
new file mode 100644
index 00000000..5d24d83b
--- /dev/null
+++ b/imap/src/osdep/dos/mkautaux.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for DOS
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 7 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/dos/mkauths.bat b/imap/src/osdep/dos/mkauths.bat
new file mode 100644
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/dos/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/dos/mtestdbw.bat b/imap/src/osdep/dos/mtestdbw.bat
new file mode 100644
index 00000000..95452a01
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdbw.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS B&W link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llbwtcp.lib llibce.lib
diff --git a/imap/src/osdep/dos/mtestdnf.bat b/imap/src/osdep/dos/mtestdnf.bat
new file mode 100644
index 00000000..451bdda3
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdnf.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS PC-NFS link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib ltklib.lib
diff --git a/imap/src/osdep/dos/mtestdnv.bat b/imap/src/osdep/dos/mtestdnv.bat
new file mode 100644
index 00000000..2b444e18
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdnv.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Novell link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llibsock.lib
diff --git a/imap/src/osdep/dos/mtestdpc.bat b/imap/src/osdep/dos/mtestdpc.bat
new file mode 100644
index 00000000..ca131181
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdpc.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS PC/TCP link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib lsocket.lib lnetlib.lib lpc.lib lconfig.lib llibce.lib;
diff --git a/imap/src/osdep/dos/mtestdwa.bat b/imap/src/osdep/dos/mtestdwa.bat
new file mode 100644
index 00000000..3d26146f
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdwa.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Waterloo link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib wattcplg.lib
diff --git a/imap/src/osdep/dos/mtestwsk.bat b/imap/src/osdep/dos/mtestwsk.bat
new file mode 100644
index 00000000..5d8922e3
--- /dev/null
+++ b/imap/src/osdep/dos/mtestwsk.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Winsock link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOD:llibce mtest.obj,mtest.exe,,cclient.lib winsock.lib llibcewq.lib libw.lib, mtest
diff --git a/imap/src/osdep/dos/mtxdos.c b/imap/src/osdep/dos/mtxdos.c
new file mode 100644
index 00000000..ad969267
--- /dev/null
+++ b/imap/src/osdep/dos/mtxdos.c
@@ -0,0 +1,875 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 June 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys\stat.h>
+#include <dos.h>
+#include "rfc822.h"
+#include "dummy.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ unsigned char *buf; /* temporary buffer */
+} MTXLOCAL;
+
+
+/* Drive-dependent data passed to init method */
+
+typedef struct mtx_data {
+ int fd; /* file data */
+ unsigned long pos; /* initial position */
+} MTXDATA;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+long mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_badname (char *tmp,char *s);
+long mtx_parse (MAILSTREAM *stream);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : (DRIVER *) NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ long ret = NIL;
+ char *s;
+ struct stat sbuf;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (stream,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (stream,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_create (stream,mailbox);
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_delete (stream,mailbox);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return dummy_rename (stream,old,newname);
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char *s;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ if (!mailboxfile (tmp,stream->mailbox))
+ return (MAILSTREAM *) mtx_badname (tmp,stream->mailbox);
+ /* open, possibly creating INBOX */
+ if (((fd = open (tmp,O_BINARY|(stream->rdonly ? O_RDONLY:O_RDWR),NIL)) < 0)&&
+ (compare_cstring (stream->mailbox,"INBOX") ||
+ ((fd = open (tmp,O_BINARY|O_RDWR|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))<0))){
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ /* canonicalize the stream mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = fd; /* note the file */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->buf = NIL; /* initially no local buffer */
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mtx_ping (stream)) return NIL;
+ if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T;
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ close (LOCAL->fd); /* close the local file */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,(size_t) *length);
+ return LOCAL->buf;
+}
+
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ FDDATA d;
+ unsigned long hdrsize,hdrpos;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* if message not seen */
+ if (elt->seen && !(flags & FT_PEEK)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno);
+ mm_flags (stream,msgno);
+ }
+ /* get location of text data */
+ hdrpos = mtx_hdrpos (stream,msgno,&hdrsize);
+ d.fd = LOCAL->fd; /* set initial stringstruct */
+ d.pos = hdrpos + hdrsize;
+ /* flush old buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
+ INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
+ return T; /* success */
+}
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno);
+}
+
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ /* punt if stream no longer alive */
+ if (!(stream && LOCAL)) return NIL;
+ /* parse mailbox, punt if parse dies */
+ return (mtx_parse (stream)) ? T : NIL;
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ if (mtx_ping (stream)) { /* ping mailbox */
+ /* get new message status */
+ while (i <= stream->nmsgs) mail_elt (stream,i++);
+ mm_log ("Check completed",(long) NIL);
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ MESSAGECACHE *elt;
+ char tmp[MAILTMPLEN];
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* get cache element */
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more deleted message */
+ }
+ else if (i++ && delta) { /* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,(unsigned long) MAILTMPLEN);
+ lseek (LOCAL->fd,j,SEEK_SET);
+ read (LOCAL->fd,tmp,(size_t) m);
+ /* write to destination position */
+ lseek (LOCAL->fd,j - delta,SEEK_SET);
+ write (LOCAL->fd,tmp,(size_t) m);
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ elt->private.special.offset -= delta;
+ }
+ }
+ if (n) { /* truncate file after last message */
+ chsize (LOCAL->fd,LOCAL->filesize -= delta);
+ sprintf (tmp,"Expunged %ld messages",n);
+ mm_log (tmp,(long) NIL); /* output the news */
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL: /* name is bogus */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ return mtx_badname (tmp,mailbox);
+ default: /* file exists, but not valid format */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MTX-format mailbox: %s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,(long) MAILTMPLEN);
+ read (LOCAL->fd,tmp,(size_t) j);
+ if (write (fd,tmp,(size_t) j) < 0) {
+ sprintf (tmp,"Unable to write message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ chsize (fd,sbuf.st_size);
+ j = k;
+ ret = NIL; /* note error */
+ break;
+ }
+ } while (k -= j); /* until done */
+ }
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
+ dummy_create (NIL,"INBOX.MTX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ return mtx_badname (tmp,mailbox);
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ /* open destination mailbox */
+ if (!mailboxfile (file,mailbox) ||
+ ((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* revert file if failure */
+ if (!ret || (fflush (df) == EOF)) {
+ chsize (fd,sbuf.st_size); /* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ fclose (df); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long mtx_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+/* Parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char *s,*t,*x,lbuf[65];
+ char tmp[MAILTMPLEN];
+ long i;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* while there is stuff to parse */
+ while (i = sbuf.st_size - curpos) {
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ if ((i = read (LOCAL->fd,lbuf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
+ curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ lbuf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (lbuf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find end of line at %ld in %ld bytes, text: %s",
+ curpos,i,(char *) lbuf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - lbuf; /* note start of text offset */
+ if (!((s = strchr (lbuf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
+ (char *) lbuf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+ /* intantiate an elt for this message */
+ (elt = mail_elt (stream,++nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* as well as offset from header of message */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ /* parse the header components */
+ if (!(mail_parse_date (elt,lbuf) &&
+ (elt->rfc822_size = strtol (x = s,(char **) &s,10)) && (!(s && *s))&&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) lbuf,(char *) x,(char *) t);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* update current position to next header */
+ curpos += i + elt->rfc822_size;
+ /* calculate system flags */
+ if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (i & fDELETED) elt->deleted = T;
+ if (i & fFLAGGED) elt->flagged = T;
+ if (i & fANSWERED) elt->answered = T;
+ if (i & fDRAFT) elt->draft = T;
+ if (curpos > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ }
+ /* update parsed file size */
+ LOCAL->filesize = sbuf.st_size;
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return T; /* return the winnage */
+}
+
+/* Update status string
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* not if readonly you don't */
+ if (stream->rdonly || !elt->valid) return;
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << 29 - find_rightmost_bit (&j);
+ sprintf (tmp,"%010lo%02o",k, /* print new flag string */
+ fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,SEEK_SET);
+ write (LOCAL->fd,tmp,12); /* write new flags */
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ size_t i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ long pos = elt->private.special.offset + elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ /* get to header position */
+ lseek (LOCAL->fd,pos,SEEK_SET);
+ /* search message for CRLF CRLF */
+ for (siz = 1; siz <= elt->rfc822_size; siz++) {
+ if (!i && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
+ return pos;
+ else i--;
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz);
+ return pos; /* return to caller */
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ }
+ return pos; /* have position */
+}
diff --git a/imap/src/osdep/dos/nl_dos.c b/imap/src/osdep/dos/nl_dos.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/dos/nl_dos.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/dos/os_dbw.c b/imap/src/osdep/dos/os_dbw.c
new file mode 100644
index 00000000..e28e1ab0
--- /dev/null
+++ b/imap/src/osdep/dos/os_dbw.c
@@ -0,0 +1,91 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (B&W) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+#include "stdlib.h"
+#include "bwtcp.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#define read soread
+#define write sowrite
+#define close soclose
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ char *s;
+ if (!myLocalHost) { /* known yet? */
+ /* get local host name from DISPLAY env var */
+ if (!((s = getenv ("DISPLAY")) || (s = getenv ("display")))) {
+ mm_log ("Environment variable 'DISPLAY' is not set", ERROR);
+ s = "random-pc";
+ }
+ myLocalHost = cpystr (s);
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ char *s = *host; /* in case of error */
+ sin->sin_addr.s_addr = rhost (host);
+ if (sin->sin_addr.s_addr == -1) {
+ *host = s; /* error, restore old host name */
+ return NIL;
+ }
+ *host = cpystr (*host); /* make permanent copy of name */
+ return T; /* success */
+}
diff --git a/imap/src/osdep/dos/os_dbw.h b/imap/src/osdep/dos/os_dbw.h
new file mode 100644
index 00000000..f22cae6f
--- /dev/null
+++ b/imap/src/osdep/dos/os_dbw.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (B&W/Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dnf.c b/imap/src/osdep/dos/os_dnf.c
new file mode 100644
index 00000000..dcc3ea64
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnf.c
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (PC-NFS) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <sys\nfs_time.h>
+#include <sys\tk_types.h>
+#include <sys\socket.h>
+#include <netinet\in.h>
+#include <tk_errno.h>
+#include <sys\uio.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ unsigned long myip;
+ /* see if known host name */
+ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp;
+ /* no, try host address */
+ else if (get_myipaddr ((char *) &myip))
+ sprintf (s = tmp,"[%s]",inet_ntoa (myip));
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
diff --git a/imap/src/osdep/dos/os_dnf.h b/imap/src/osdep/dos/os_dnf.h
new file mode 100644
index 00000000..3a832217
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnf.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (PC-NFS) version
+ *
+ * Author: Mike Seibel from Novell version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <tklib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dnv.c b/imap/src/osdep/dos/os_dnv.c
new file mode 100644
index 00000000..e0341764
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnv.c
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <sys\socket.h>
+#include <netinet\in.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#define read soread
+#define write sowrite
+#define close soclose
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ struct hostent *he;
+ struct in_addr in;
+ /* could we get local id? */
+ if ((in.s_addr = getmyipaddr ()) != -1) {
+ if (he = gethostbyaddr ((char *) &in.s_addr,4,PF_INET)) s = he->h_name;
+ else sprintf (s = tmp,"[%s]",inet_ntoa (in));
+ }
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ char *s = *host; /* in case of error */
+ sin->sin_addr.s_addr = rhost (host);
+ if (sin->sin_addr.s_addr == -1) {
+ *host = s; /* error, restore old host name */
+ return NIL;
+ }
+ *host = cpystr (*host); /* make permanent copy of name */
+ return T; /* success */
+}
diff --git a/imap/src/osdep/dos/os_dnv.h b/imap/src/osdep/dos/os_dnv.h
new file mode 100644
index 00000000..f22cae6f
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnv.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (B&W/Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dpc.c b/imap/src/osdep/dos/os_dpc.c
new file mode 100644
index 00000000..eece19d7
--- /dev/null
+++ b/imap/src/osdep/dos/os_dpc.c
@@ -0,0 +1,102 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (PC/TCP) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <4bsddefs.h>
+#include <sys\socket.h>
+#include <errno.h>
+#include <arpa\inet.h>
+#include <netinet\in.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet */
+ char *s,tmp[MAILTMPLEN];
+ long myip;
+ /* see if known host name */
+ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp;
+ /* no, try IP address */
+ else if (myip = gethostid ()) {
+ struct in_addr in;
+ in.s_addr = myip;
+ sprintf (s = tmp,"[%s]",inet_ntoa (in));
+ }
+ /* older kernel, look harder. */
+ else if (getconf ("ifcust","ip-address",tmp+1,MAILTMPLEN-2)) {
+ *(s = tmp) = '['; /* wrap the brackets around it */
+ strcat (tmp,"]");
+ }
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
diff --git a/imap/src/osdep/dos/os_dpc.h b/imap/src/osdep/dos/os_dpc.h
new file mode 100644
index 00000000..5de3e637
--- /dev/null
+++ b/imap/src/osdep/dos/os_dpc.h
@@ -0,0 +1,41 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (PC/TCP) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dwa.c b/imap/src/osdep/dos/os_dwa.c
new file mode 100644
index 00000000..36e5b147
--- /dev/null
+++ b/imap/src/osdep/dos/os_dwa.c
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (Waterloo) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <tcp.h> /* must be before TCPSTREAM definition */
+#include "tcp_dwa.h" /* must be before osdep include our tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+
+/* Undo compatibility definition */
+
+#undef tcp_open
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#include "tcp_dwa.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char *s,hname[32],tmp[MAILTMPLEN];
+ long myip;
+
+ if (!sock_initted++) sock_init();
+ tcp_cbrk (0x01); /* turn off ctrl-break catching */
+ /*
+ * haven't discovered a way to find out the local host's
+ * name with wattcp yet.
+ */
+ if (myip = gethostid ()) sprintf (s = tmp,"[%s]",inet_ntoa (hname,myip));
+ else s = "random-pc";
+ myLocalHost = cpystr (s);
+ }
+ return myLocalHost;
+}
diff --git a/imap/src/osdep/dos/os_dwa.h b/imap/src/osdep/dos/os_dwa.h
new file mode 100644
index 00000000..f70fa31b
--- /dev/null
+++ b/imap/src/osdep/dos/os_dwa.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (Waterloo) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define tcp_open TCP_open
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_wsk.c b/imap/src/osdep/dos/os_wsk.c
new file mode 100644
index 00000000..8627ef89
--- /dev/null
+++ b/imap/src/osdep/dos/os_wsk.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Winsock version
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_wsk.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#include "tcp_wsk.c"
diff --git a/imap/src/osdep/dos/os_wsk.h b/imap/src/osdep/dos/os_wsk.h
new file mode 100644
index 00000000..10213cef
--- /dev/null
+++ b/imap/src/osdep/dos/os_wsk.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 16-bit Winsock version
+ *
+ * Author: Mike Seibel from Novell version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+#define WSA_VERSION ((1 << 8) | 1)
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/dos/pmatch.c b/imap/src/osdep/dos/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/dos/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/dos/tcp_dos.c b/imap/src/osdep/dos/tcp_dos.c
new file mode 100644
index 00000000..062f260c
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dos.c
@@ -0,0 +1,434 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MS-DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ struct sockaddr_in sin;
+ int sock;
+ char *s,tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* look up host name */
+ else if (!lookuphost (&host,&sin)) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* copy port number in network format */
+ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",errno);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+#if 0
+ /* needed? */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ errno = ENOBUFS; /* just in case */
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
+ switch (errno) { /* analyze error */
+ case ECONNREFUSED:
+ s = "Refused";
+ break;
+ case ENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case ETIMEDOUT:
+ s = "Timed out";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
+ mm_log (tmp,ERROR);
+ close (sock);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = host; /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the socket */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcps >= 0) close (stream->tcps);
+ stream->tcps = -1;
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* all we can do for now */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/dos/tcp_dos.h b/imap/src/osdep/dos/tcp_dos.h
new file mode 100644
index 00000000..2933a5ff
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dos.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS2 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long lookuphost (char **host,struct sockaddr_in *sin);
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/dos/tcp_dwa.c b/imap/src/osdep/dos/tcp_dwa.c
new file mode 100644
index 00000000..4275f27c
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dwa.c
@@ -0,0 +1,344 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Waterloo DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+/* Global data */
+
+short sock_initted = 0; /* global so others using net can see it */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *TCP_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ tcp_Socket *sock;
+ char *s,tmp[MAILTMPLEN];
+ unsigned long adr,i,j,k,l;
+ port &= 0xffff; /* erase flags */
+ /* initialize if first time here */
+ if (!sock_initted++) sock_init();
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s)
+ adr = (i << 24) + (j << 16) + (k << 8) + l;
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else { /* lookup host name */
+ if (!(adr = resolve (host))) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* OK to instantiate socket now */
+ sock = (tcp_Socket *) fs_get (sizeof (tcp_Socket));
+ /* open connection */
+ if (!tcp_open (sock,(word) 0,adr,(word) port,NULL)) {
+ sprintf (tmp,"Can't connect to %.80s,%ld",host,port);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &sock);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (host); /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,(size_t) n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int status;
+ if (!stream->tcps) return NIL;/* no-no nuked socket */
+ while (stream->ictr < 1) { /* if buffer empty, block for input and read */
+ if (!_ip_delay1 (stream->tcps,600,NULL,&status))
+ stream->ictr = sock_fastread (stream->tcps,
+ stream->iptr = stream->ibuf,BUFLEN);
+ else if (status == 1) { /* nuke the socket if closed */
+ sock_close (stream->tcps);
+ fs_give ((void **) &stream->tcps);
+ return NIL;
+ }
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ /* output the cruft */
+ sock_puts (stream->tcps,string);
+ return T; /* all done */
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ sock_write (stream->tcps,string,(int) size);
+ return T;
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ if (stream->tcps){ /* nuke the socket */
+ sock_close (stream->tcps);
+ _ip_delay2 (stream->tcps,0,NULL,NULL);
+ }
+ fs_give ((void **) &stream->tcps);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/dos/tcp_dwa.h b/imap/src/osdep/dos/tcp_dwa.h
new file mode 100644
index 00000000..360eea85
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dwa.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Waterloo DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ tcp_Socket *tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/dos/tcp_wsk.c b/imap/src/osdep/dos/tcp_wsk.c
new file mode 100644
index 00000000..90e206a9
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_wsk.c
@@ -0,0 +1,818 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons ((u_short) port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(he = gethostbyname (lcase (strcpy (tmp,host)))))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* error? */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock); /* do possible cleanup action */
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port)
+{
+ int sock;
+ char *s;
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError());
+ return -1;
+ }
+ wsa_sock_open++; /* count this socket as open */
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) ==
+ SOCKET_ERROR) {
+ switch (WSAGetLastError ()){/* analyze error */
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s,
+ WSAGetLastError ());
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t tc,t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) <
+ 0) && (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ struct timeval tmo;
+ int i;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ stream->ictr = i; /* set new byte count */
+ /* point at TCP buffer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ /* block and write */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpso+1,NULL,&fds,NULL,
+ tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) &&
+ (WSAGetLastError () == WSAEINTR));
+ else while (((i = write (stream->tcpso,string,
+ min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ if (i == SOCKET_ERROR) return tcp_abort (stream);
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ string += i;
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0))) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T);
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that NT requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
+ (*bn) (BLOCK_NONE,NIL);
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/dos/tcp_wsk.h b/imap/src/osdep/dos/tcp_wsk.h
new file mode 100644
index 00000000..6e8f9af7
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_wsk.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 16384 /* 32768 causes stdin read() to barf */
+
+#include <windows.h>
+#define _INC_WINDOWS
+#include <winsock.h>
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ SOCKET tcpsi; /* tcp socket */
+ SOCKET tcpso; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/dos/write.c b/imap/src/osdep/dos/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/dos/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/mac/dummy.h b/imap/src/osdep/mac/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/mac/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/mac/dummymac.c b/imap/src/osdep/mac/dummymac.c
new file mode 100644
index 00000000..a0d6d0d2
--- /dev/null
+++ b/imap/src/osdep/mac/dummymac.c
@@ -0,0 +1,295 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for Mac
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/mac/env_mac.c b/imap/src/osdep/mac/env_mac.c
new file mode 100644
index 00000000..d0a88df7
--- /dev/null
+++ b/imap/src/osdep/mac/env_mac.c
@@ -0,0 +1,236 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myHomeDir = NIL; /* home directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ char tmp[MAILTMPLEN];
+ switch ((int) function) {
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ /* set home directory if not defined */
+ if (!myHomeDir) myHomeDir = cpystr ("");
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost ? myLocalHost : "random-mac";
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ sprintf (tmp,"%s:News State",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * format of date and time
+ *
+ * This depends upon the ReadLocation() call in System 7 and the
+ * user properly setting his location/timezone in the Map control
+ * panel.
+ * Nothing is done about the gmtFlags.dlsDelta byte yet, since I
+ * don't know how it's supposed to work.
+ */
+
+static void do_date (char *date,char *fmt)
+{
+ long tz,tzm;
+ time_t ti = time (0);
+ struct tm *t = localtime (&ti);
+ MachineLocation loc;
+ ReadLocation (&loc); /* get location/timezone poop */
+ /* get sign-extended time zone */
+ tz = (loc.gmtFlags.gmtDelta & 0x00ffffff) |
+ ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0);
+ tz /= 60; /* get timezone in minutes */
+ tzm = tz % 60; /* get minutes from the hour */
+ /* output time */
+ strftime (date,MAILTMPLEN,fmt,t);
+ /* now output time zone */
+ sprintf (date += strlen (date),"%+03ld%02ld",tz/60,tzm >= 0 ? tzm : -tzm);
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%a, %d %b %Y %H:%M:%S ");
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,"%2d-%b-%Y %H:%M:%S ");
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ return (char *) mail_parameters (NIL,GET_LOCALHOST,NIL);
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ return (char *) mail_parameters (NIL,GET_HOMEDIR,NIL);
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM dummyproto;
+ return &dummyproto; /* return default driver's prototype */
+}
+
+/* Block until event satisfied
+ * Called as: while (wait_condition && wait ());
+ * Returns T if OK, NIL if user wants to abort
+ *
+ * Allows user to run a desk accessory, select a different window, or go
+ * to another application while waiting for the event to finish. COMMAND/.
+ * will abort the wait.
+ * Assumes the Apple menu has the apple character as its first character,
+ * and that the main program has disabled all other menus.
+ */
+
+long wait ()
+{
+ EventRecord event;
+ WindowPtr window;
+ MenuInfo **m;
+ long r;
+ Str255 tmp;
+ /* wait for an event */
+ WaitNextEvent (everyEvent,&event,(long) 6,NIL);
+ switch (event.what) { /* got one -- what is it? */
+ case mouseDown: /* mouse clicked */
+ switch (FindWindow (event.where,&window)) {
+ case inMenuBar: /* menu bar item? */
+ /* yes, interesting event? */
+ if (r = MenuSelect (event.where)) {
+ /* round-about test for Apple menu */
+ if ((*(m = GetMHandle (HiWord (r))))->menuData[1] == appleMark) {
+ /* get desk accessory name */
+ GetItem (m,LoWord (r),tmp);
+ OpenDeskAcc (tmp); /* fire it up */
+ SetPort (window); /* put us back at our window */
+ }
+ else SysBeep (60); /* the fool forgot to disable it! */
+ }
+ HiliteMenu (0); /* unhighlight it */
+ break;
+ case inContent: /* some window was selected */
+ if (window != FrontWindow ()) SelectWindow (window);
+ break;
+ default: /* ignore all others */
+ break;
+ }
+ break;
+ case keyDown: /* key hit - if COMMAND/. then punt */
+ if ((event.modifiers & cmdKey) && (event.message & charCodeMask) == '.')
+ return NIL;
+ break;
+ default: /* ignore all others */
+ break;
+ }
+ return T; /* try wait test again */
+}
+
+/* Return random number
+ */
+
+long random ()
+{
+ return (long) rand () << 16 + rand ();
+}
+
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/mac/env_mac.h b/imap/src/osdep/mac/env_mac.h
new file mode 100644
index 00000000..4a5a8c97
--- /dev/null
+++ b/imap/src/osdep/mac/env_mac.h
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s:Mailbox List",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s:Mailbox List Temp",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/mac/fs_mac.c b/imap/src/osdep/mac/fs_mac.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/mac/fs_mac.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/mac/ftl_mac.c b/imap/src/osdep/mac/ftl_mac.c
new file mode 100644
index 00000000..782381b8
--- /dev/null
+++ b/imap/src/osdep/mac/ftl_mac.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ /* nuke the resolver */
+ if (resolveropen) CloseResolver ();
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/mac/linkage.c b/imap/src/osdep/mac/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/mac/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/mac/linkage.h b/imap/src/osdep/mac/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/mac/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/mac/mtest.sit.hqx b/imap/src/osdep/mac/mtest.sit.hqx
new file mode 100644
index 00000000..4089c44f
--- /dev/null
+++ b/imap/src/osdep/mac/mtest.sit.hqx
@@ -0,0 +1,171 @@
+(This file must be converted with BinHex 4.0)
+:#@edCA0d,R0TG!"6594%8dP8)3#3""l`!!!"4[4p8dP8)3!#!!!Hm(*-BA8#bJ#
+3!aB"#Jd!$'edCA0d,VNZFR0bB`#3%j@,!*!8"h-!N!6rN!4bFh*M8P0&4!%!TFk
+UMDPEIVJ!!"f3!!#3"JEY!*!%`hJ!N!KIY3c!q+l(4rK4`['9YjP(D"GY*FH1(-X
+cXLBR0(,Fbb2([#i[,dqCK"rKGS3@iEFb3JQeji4&VeZ[1Tp``PGTPS4GCi46F[`
+)*dr*J*-T2h+-c2N46VMfC-$*Vi46-Z(N'*P4`L5F(#G2#5GiGT&MK"*'9LD!erG
+CjLAcHUq9(@&*RN@fj0Ne))``F[c)XC9`B,`kD)!("P!!B+%+X!"Ij9MdF'6TDbk
+eDS@rkBHYZjA")jZhqKjc,5mCd(FTrHqIGj@qeG-@lI&rjBp%)kdK&fVILa2,2T2
+)Nrd4Q1G"rm8Y@pe%8-SkIQSGk2H@qIe3dIJcJrAH1R9c&i2J#HpTbZ[82H$e3hN
+Gr+r1$kAiHS*IpmchqmF6`GH$qi)l!%)YfjZ9XS-II05ZY0Hd[$N205VQN@SCP1m
+)Y64Y8k#VBqraRH@Q)09I"'0XA!G`I`Z`"YrDNrJL[SBi1UackpVJ'9h60qKDB,'
+ZI9HZ+l((G5@JIUk!US1Z-MDXaLiE+K4V$)T"`eF&6AH$4eI`"F3!(Y2Kj)ABCH6
+#b6&ffHN-mSr[[a($&e6dMmla&Iiep%rHVrQhHi#(0JaJe+#i-PJ&%!J"P1Cl!ep
+YQ2T$jSEJi2Z0c+hDh!RbcmB"'fT`%@-'kLB+!r6'c4USMFh0fb68Y(llUk)VPLr
+Ed,hR3+5c[D2(piA[kG@V9rTHlZTmZbd5lH`ji1X1qelC&HhSl'V[kHkL2X*kMAQ
+aAlcJMXYmY&+R(%SV"d9HaYKaMM2'05VCQ-Q6Z-8I6HNNK$jrP@'C#fhdRG$`*Iq
+%&@Cib6p[RSd[23pm09LaTDQaQHE,TKFhEZ)8(AZN&j&$@cB%&dm`1'D23KiA`i4
+k2MCcJkB$!`hclN6Fi$P,$&'1lZ1NTbALF5'11FV9NaR+!fJ,m`$QQX5KhSi!*8i
+0H"P%&KpL"M!d&Sm2'A1eSI*J(ND+Nh2H4CbM[(`bCJHJ,N8-+K58K!`YM1)PBC(
+MF0C+`KM%5qDK'9kBG#fF$!KI)Lr"'SQFp+A[c%U3!1a*+dC(4bhbb%J@6`TE[35
+MM(Nm(X4XR"M-a)90**KqZ"l(V4#iN!!lHpDXM)NM`EB,Cr%6K!Nq4h*KmA)pB*I
+)`FD3!b4&J$+3!2'`Cjcj!bJL9'E`T&l56UjPreMAX3FQ,GJZd#hK9K%451'Ll4J
+[Q!K6EaUrTfIlcI)Grdj,Q,L8F($6rf6,&2hjQq2Ql%alc$FQUj8Y-F8G'4R*miZ
+6r#N"Nhme6(qhkM0Gm4bIGkdBkR++CrL5,4D&DElPi!UIMbP,)&-ICUi63Q#+l`6
+Mr0435I-cJc8P-0hc+6l8CL,C!fPlFU@j0YHYP5HlVH6iFMqck@,jbDiKPVai*iE
+U$Ik%rBbr0$p6)-ka&c4LTeHf'r90kr2kc1PIL8)!1m$[-EHZbBe6E*MC$6#jJ*S
+D5ADDE#fUXfa6S4$,$R1GEqqVFra(qSj!BB$kcE6AAIeme%lY-+NSX$"!#a3%h`&
+Q*k"SP1XGG*8[q[)kriiqr[N`q606mMGrMr6Ym@A(IIpC!`re#`28larr"3,URqc
+[!JVpml$rlr*6*KrShqr[f3f'4hk$ZAhbP(CPb2rrCL3-m3pHa&&p!ZrP1,mSX("
+(RI3Qr550fhJiM2F2G!"KiYhmHX("5F#`F3Cii$"NqG''q!Q&BH'NM`)*!fm-NMJ
+G#iK#50`+6KKLT8)))B%,06GHI$PV%q'LL"-iAR[``!-0+5$5!(j#))h,X24N"1M
+Z3b$mh!-#ETlA5jqQAj-I-'p$,*iGIjfpb3C00LpePpdq'U&fJp!9LaP)F'!L&(#
+B-5QFhja-i,Ii3Z#f[f4jF[adr42@M*(aS%(r(cJ$V%XHH6)M*j%XNB0M090iPKm
+LA!c@+El%Er(aKNG'k+Uq'6Iie,4jIhRpArhcH[pGRlRkjh#kDmVcVp8l0@+3!*-
+C-3dda3h982@pIFIkIZcpjA`I1q0HjqjcpETeZPLFAMG*`Fd9K!Ihi0Vp`bA5LG*
+hUZq8VJ`VcUpGcBK0cMcR"h&$E,bKHRr0IYFEPf+ABZGi0Kklp*kV`G9iZ[%FBMH
+rb60J'$JdYQJ98C5$i*'hHLqX$cj("j*GmUV[S,cU1p,C'LAqPjfYZeGLrMhLP&p
+!I"@iP2Q)8riXB-@A8,d'p(lS*8`T3Z`S(+*+ZVBKKRHdK,Ph`N#[r#[[UN(HZi,
+RmL$f+@NM9SlBFD(YENEXX0#SAS2B+U&4(82X+D&4I3Da#U&48BGBJp#BAipB3'K
+82B2B3PQ$$a&l6QJ8DBJY%4T&Rb$Q&4UHaBLY&4UP5a&E)65mYBK9#BhL5X4@#Sf
+5"BMjK)Dk#,(5*lDrdp8@m8ADSYhl)UeYr`%0!!GYG'9cG#kj3)#D#J&2H#J!&c6
++!*!$J!#3"!&EEhJT,!#3%4B!N!MrN!438Np+5d&)6!%!UKPEcE$CcD)!!(*+!*!
+'&`d!N!3HUJ#3#1[c$J#9qCRFXlGVCEICCSIR+lY0&eVRAMZqXX[00PXS2r@Df1A
+KkHA*"VNBTjER*iZH[pFm&Z8ZE"qcfblm[',l@,%Y@BlrBZI+'dRENNhB`TG6`U+
+%rm+qcS9IXHh##19fLpl##EGFH-DfXHfjAGJq`Xl0#&Z"UN(Pee&qXHelQ@6(IQ(
+lb#E,Nrd4YNmb@CI$RfG![9r(Q-!B#jFc&X+DS8F3ArE,60Kl5)j0,',#3EXF'lA
+Ff"lcGF6XpqJ3VDZ[VCVRG&G6"[,RPeil*ApGbRl6q$rjPKb*9Y(1D'@k1jk56ib
+Eb(5E,A,-M0L$VA*XbY0-Yr-@1CBlLqRfhkE85@)CT[d855D6KeK',QTi*XDBC34
+L5e$cI81&5RrqMB2Q!HmIb'5XY(jK24d42+cfld3SK%*fYiD6QclP6#EAG(r)aeP
+A9eeCik`c3H*Pl-%"%f1(rd'SBd9bYr%[Ke#EaC*,Y$c-`"ZCB(aPl&BS5Mh,BLD
+N0[#0c'FQ[EfTSphSF9G+mm35REeC$@ABQb1@KQ[EQd)a(q9fL4i("NpNfN2KQ-m
+SrQ-S(2)pT,1(,a$YBE%N(&VZLmCm8CdpQL8R8qpEN@Q2GN5E4pEl1X,aD,-2+*'
+J,iSDk,LB&@mhVGUh%i%T`9"c@lc&*aE90$@hqTTDI0'15bkj3XE)'FC`$f"%"*l
+1P"DI2aMbLE&Sh#I+hk8AU)Im6@dGbUP#l9$8Yd)"kNG#BZUa&")[D50+5NM)&9b
+(+Q,e$K1L)[VIr25C($[MAIaX"HGhq)+1&j!!e[2jJj+Bi9hm,'b0jr&KUjKP@[a
+prP3lUX&jI')FUX(jqP([8`qIabF--RE1iir'PSMXGrLk$$B+fa(Rm6-0[2G([SX
+r"YZFmrJ'bKlZfl[ie([83qIV4le(06c22e6lh(IaUAI([SY2[8XIeB'afAb9Ir$
+Df*FU$RcS#1"L*RJ,Ea'm-fRG)RJrZ&A`AVj,m,SFJVIm#X%lcb9ikpb#ep-PH+p
+#E1P2"'r!,RL[ALpi`bm+hXK8V%Z`hSce%F(lBEhJM4F*hQXl"Hp(eQ,G*(MAbRj
+Q$[XD'T2@ZpNUa&HaZ`@Ri-$UI+HX[rmZrkFkaRE&f&5@+e`QI%6A4AYGekUNX",
+a$f$r%H%$&&q992!JbRG&K)XDRZXL%808*$F5I@jPT8B8j'B8aRlIb[FAeAq55dH
+1L1MQrZMBlqI#N6heG3RbIm0h"PIj6mS9SQrVVEIZf)'ii2IIHL[fZKNcr(lX)9e
+Qc0LaJqPIj*p+"@Tqh$jQE2+q8I`V51f,88$DMjNaBmC[idIcNIFrX['[3-eISDr
+,(liA2GB#PGG%M!Y$j0I99(,S4!lH0"+9j69jk$RX1"[J-k&q&K)M!KJPmk2bp!&
+6A(i)A$f%aJ9#mk**M)h2!k&-a+NY%$!d(f"'9ic!cqF$K"F86-V-6b31L6b5G`0
+1M&'($4C$r*(IEi'!bf%jZAp2)MJRAj%!m+V!I53X6@!NNPIi-M$VJ*EN'9#9N!"
+a&YKjY(!C*kkY+8YVm&1V[[Q*Em&G4"8%*T5f,[#88kD'H-`rfpl-l%cSlTfdA9f
+"JHN(J%eSm*4@)PpM4k`P',DM)A3i[eTGJ6&"`DPF8NCpe"&Fi@Z-%4"P0#'KVZJ
+[*D-5cj9JHF(B(,XfiL-JBI-6ph@T+dUX9+M'@9P0&@T[#VEaI(3iraee4Fp0i$M
+,UZ%S8Bqf"80A0`@dM*j99r5IR0'bfSC5UT%ah0(LLr"D$Z!BeBaU'aTVU*EKMXE
+fTQDHHdChlq40kSUZ9+[Z+8@0""1kSbfiP#2TZlpQQDfZk&!&D9'*Qh46eMA0V8e
+4Z8%STi[9&If[j942(BHFSX&33-Rq5%lCFLGG'%BMSCk#N4UTJi!'FlG!4h'FNPU
+T&$KMB#kf"'2"F+LT$GjI0$b-H3($G!YKeP3ZTYbbDi+G[TDDF![[Um`"V"&D(HU
+G[+-pdDB)cfQ`AL29cPmJ9B,h"%-m&1`FlT@F&1Fi2G6j(E%QQ8Nbd5%AUL[i8-(
+a90C32aYL`ACH(TD"2Rj'AF'05MqA53XEUIGmSH9U3qZ"%&GA'!MMC-i"%Nd8C3+
+*ea'RPkXVE!)&TEc"6VT5lqI&(N)CUk&iURNZrKLR,d,jQ,S#3d'4UZ9F3M,(%FV
+(eC9GU+*i5ZTi,V&QcP[%Ek[9&GfJ-QCP3`PRc'!(&4QFM[2Ve*@09rUKYEkmK,M
+I&28hchBiC+i'`RTe"FjV+U%9HSK`lP&ApKk`"H%%+aYUUF2-`Bj`iqcCPhd3LN,
+1kAje"C+FIA"HE5A2rZT`F(CM&%M03dM[I4dTVL&p3ehC*"A*8pR!1c`@l'LFj5M
+NkEqTJp$a'"p6ZV5kJEM'l'p6q*CA,Zf!&T,VPY)iT)9HdcLY3aT(IdlMC*l91)C
+c'SI8d"Q0SkQK8aT(8d1R0)ljV-E*rSh'XIa+iea`9Z-S@ZL8aY'dd%Q0SfQKNaT
+Re$Q0-rUdaY'8d!Q0Ne*#ac@1TS41D"a0#Ch31"HHd$LTbPMIi(A9bcQQ#EB&8Yl
+)!#XVHU$IbG$d`+Y1aS"Zd*h9$4RRG-22[C'MZL&GXcGbh(&BhDmZMP6LeqTL3!Z
+3!,Vi$jq8e-8C,D#ULhHdJ+BZ6QN"FK[He!*(0&$+EAM94dkhicBF8D$pEJ-T8*C
+5S1Q2e-9E@Z!"ZL1#F*R4@q-TDk#qP#q[J"K)kc#&J8b@U#Yh(T@-+R"h"VJc'!$
+*DASST808jr%G6d*c(NrT%10C(8*+i)`1k9F#*cd*c@NiT5kbckN,bePeFF&[e-@
+)AkN,43fF8KHD'MMT5@KUi+4U'(e@0B`jTaSd2A"#0D6d`((9S1Q"%kT"d`-R9)2
+e,GA`5krK4R90H3d004jd,$UX28B*bA-BIkkZQ-03F#4*aJQ&9*b"MU8C$*NYbQT
+ULE0UI1hKk,9DjLPPmpjMbNE12-AUkJ6'+kcqpJ6'YQ$+$aQS@ATZYc@P3c3rC%L
+(r,IEQY)KQJ*kABHNU[JH051TE"%j+UD3!1mDA*QLCE4'AE8j0#"j1,H'I,(f$TR
+G"QG*hRYLPQ63&Cjdc"@QUC)hAH(GfKcDXY)&0930FdZm[4fha2bA-j`f@JNpVDk
+D%P*jhFKjr98(HI`aII2fr1Uc#2q-&rQ-E(I[HlDS+e!8$X09@*c$fS,Y`4Ke#8I
+Ulf&00mJp,+Jp$*ErT@iB8#+N'hkQ4)5[k"mS9eIS"Xk+eVdecK+`M$!'9)Mf+JQ
+hYiG$(T9*G32-C$M0T,SlD`pZ89GYbZ3(5TiQ18m!03haSqNSRfY91+SPIXR0U5Q
+6YlNj@mf*lLqMR*b45*[2dp4fp@Yk3R(dDKX@8%@cX)m&fhK@`PHZkhT%AD%LE(+
+218Y,FEHFN!$ME'Q*qMSk&VFe8G2)+KhG1%eGY5Q6[@J4Z9lBZf5pqEU[A&V@!08
+R'%Tp(Ea83afESpCerS*+F*f3!$dr(QbqZLADG-fV26#+L30B)c8XMkq6#j!!`6i
+BVH*95KlUQC'9S9LEHPrD-(Z3!0I-mFS@P[(jKV,P[K"RaHK3GA09T0+bKF4K@D@
+qjF(AFaUVpN&jC6ARM2*JQi`c@0YaDNB0C498Uk`'Ak!kh06bDNBADVPA1MP5DE#
+T,4`BcXQU)LfUj'bBY5JBDJPI-maBip8Z"'m5BjM"Ql&SQ&1'EiLab(e4r'*T!9@
+daKH+$fG2p`$)c&DfQ1G%69S'0ZBj(I1JbqVVZ3GG&Sf'Sj688EHD2'LjRq4+'FY
+4+CRC"R$)JjElU84@k3h0d@#%FMqDd@5Y6V@eR&NpiA#EaTH$[5F1e65EDRS-kb+
+e!Z@9Lk('K+cbB'G08ka9eA[2U5Xi@0&Yi&,jaS,QL+Tr"cfA+FFm&rZ3!1FbpCc
+RmVkcRN[H1FrPMmjj,[RR2*GTjcbALpr`A!Ca,MRVG8ar`qXBp$$HIml$X*rf-!T
+1HaL&Tcf-5dpl')l6dfdc6Xc1lPCA0[2Rr8$cE!1GFjQ+9+i`Lepe,BiLcG+3!$c
+92#G8qdK1+IllJ)SN9FXjK63JM[4[kSUEPBjaVj*6bVhjS-U8cJ8HkL*M8cc@fN%
+ia*3TR$QUHk[JQ!QR%3VYpI'A)`l1pDRjPlVDZKR8AC&`C)CfadP+"j!!krZD$QJ
+HdJ%d!hK'"kLhMlbM!l6E4dlT!2*pcmcjTLD#hjRce5D#6bQ'IZIhT',)1Z[5QRr
+MdQUZc5qFS04%i0Y1N!!b$haUcPHE"cijjjY@jrD4JFN,ZRhNcCRmkp8e0Imb`1R
+UY%JkJ[QAe&4,rrc,bDQ@LHG[5,P[VHBk(kfEiMUREjK9'H!ZcA0qaF91KcHhR&j
+P9L@P4XJl2U0'IMkYm[EG*cHQ[)k"1HDd$kSK6Hq%@qTZ!XhV',UE)$h"V1raQ`6
+)D98bT,FH8-F&fjXL-k1A+[HTT'R3&cbMY,bZa!Te6EN54eRpIqj6qBA#+(a$B@b
+kmKXdUc'1pqCiTp4!rVUHliGR2&681hq`KVK@4[e"U962kp`5LYV[HFYMj6a!DSC
+ij9rP'9A'Ce5*@SlRqcTbPSVXk,m8rjAkMQrm6UUq-NFa2A%8CQ+2ZcSI5qQZS6X
+AdR2l1[pjBfBUCdee$GqiFP`hV$QL'`CQ9G)6h,apG+SfrI$0fkN*0-f[16@"4MF
+3r1m%fMVY[L5icNlFfm$-(AK4$hLChK'3!+EJh[idd'qB`$I%#2,M6%akN!"U#)p
+,qVqSJrqI(QlqArf8"brKC8[r3[T,rV)Hf6b-Jjj%IZ`a#hm3'K2Um0BX["Z+ap$
+2Zr*CM(8*&HaQi4RX+i@2#jm4RXC,X&B,EKjG,r`9R4"fim`DS3+ReJZI%'l$UGh
+#-cLcFIM-TiHaEdlPTV`c5hk9dfmr[*XU$F&(6`f,+$&b4N3QcX9E+,[hCEiSl`d
+kC6p1f6Z8IDZbAk[X0bRlEmVlV&*j6hbLj(GfRl83'pkeaclq0#PATlRUVaq4HPJ
+VeXlT4ELUF,(8Bj3UqT)2jUh(lZYjeq&NPp56FEI8Bc)JpM,@ql(QZ3,ZcQ65(@C
+kpb*h*a0XFj+ET*l-Uk5[@CRBcYUNQlVhimaY!401ZHQ8&*pr`(a9K%NpqJF$KUU
+E!VT%T43ACjXlr51Q&bfTPFUN(N1[D2%DmSV&5jL)r0ejA9*mHP'Tdq2%fE[&DPY
+RPNR8*qbSdM+F,mblERS4&(8$6Yq#q-5mp8#V,kY'I+eB,HS$&UP(pe8pNCl&[er
+U-mk8&LDIG"l)bV(94X5%25,X(,9N91p@CamE(FP-f"GX3%9ehAmTA5A&q4[r@%@
+Im5Gj@m`Va8jcjprbPfi'6$McI0iGE&c"E(1AAlGiJa4I[-(p[H5"+`VBE+Uaehc
+PKJ*6paI&bBRL3L1kjr&!@dAIiH[ceSSjJHGXa9@ImYX6da+9diY+H+dbR`j8%lC
+D-IDN9V%H`aT8F+@iiV!l%F56bSaqID)P-3eRRVH&fF`rA9G3b@C9ljkFp'bSQLf
+m2k$V6NVab-MZKaG[%0'EPMVTS(p%SBJDe!If*#i'ETIpXe)FrB-B'd&p`k-6dBP
+cM$Pi)*G$l04p+@,CHG2Q,ET0IK[U[MIa2V(1jVCC[@ER(QD+#GGCGfkIrh"brde
+VGklckk5iM5AXK#-+LDQfZYUANP[&@YFGE25P3I$4Z-4BejIG3@B4DlYIGPK4J4H
+R,RjXfdbVc4Uk)Q#VG`C'BmeEk-69h&p&K$&8a*'I41eeKakYef)lqQ1pMfj+(UD
+kk6BkTM&,94!c+k,(Y5bRhZA[M$4h2icp!F)$`Xfph@b%%VZ6BKCAmP"JDAXV-24
+dVZ6(9Yhpmrdl,%ecLr`2iT6"[)aGQ$cXZ'LadlELd-lH6ffXkXqpYIF6F[C+I+B
+D4bbImP2MkPl*Ear2l`ANPT%mj,MS3j4EV2G6[F$B@(@C-hPijVh5Kj%91N4iTZc
+(9[EBYfGD!`jdbG6!L(UR2eI19pMMY5U4(Zm)*E,G#fi@RNA''p'0P-(M9$&aa8m
+2Lh0qHVMU$V"@[L1Ij3GX8r1Cp8TAb3pCcU0lQ2ka+af(jKDjaSrH`bjh(1VH0pr
+ThiXH,qEm8qarN!$Rd)-FHU,*'10j6,JrRehZcjf&E0`2"#bl2REA(8c[dp[biF+
+QN!"h!IF[a,T)cXDZjpF9ER[q4ZEUBSAEN8pGP$QXK9CGmDVNppDK0m5CSlbMEG1
+BIZ0DacDUKQ-lVm4$bEhH@@*1BS&BI"64%T31-Ph!1K1Sk)&4lNJ1)e6TS'dhF2H
+*FjKC,2EVQJ`5[E#4&4BPl[YmZFe8@(6(ClpFIYIDJ!&m2pdV`R[3#`8qLYUmSa(
+9+9'V9irGT*+qj(EE@LQHZ%q+lcq!$Pr"G+DY,%2k-`E@!mFAPG5@PXePh3QaX3k
+m,jUNH2k+[SffkNr[qZlU*lBpZS[(9f`ra)TY-ke@a0EdhXc2q*8cTSdpIF[i#Bp
+b)UHhTmr$6lL8%hS$[9BQKaJQq6Q0lp$ab5eH2AD2%qZJXaSGeTR@cBXA2CcXUh)
+&$N[la%2+Q9[kcr3TCe`Kmdh1GX21BZFqTL[-SE13!+i&6&rJC(TE!6-90%,EIDm
+)0Q1Hi(CprB&mGV(J,R[S'lY+h5kADqZfR@ZUGl-Lff4NrJVUT9"jl$-i[rkYmbY
+&i56#p*2R,DN-UVU@6DaD[Fa5fP@k'LG&l1+P8+jX($m4aeNc2fR!UEJG,mj-*Zr
+UDMEB)0fPq1BEPPUkYVQ,(Fa9l#k@iRIGX(5#RFISP,[L#ZDZq15f3U1l`Q'8iY3
+4ckjpGKe3$R4YfhU$)f2l9RZ'Zq+4VHk+hKYmTSeVPZSTjiCLlh-ICD3)`4@'Z8E
+`a'451AR&L'GhIc&[XTLGRe&Im9*&IF8FShIF!-VDqJV$GAMQhq%&'`TBpjF+*d2
+pCIUI)Tljjl,E+[`6aBa%-E,iR$J'D%X3fCMI)-j,6)X)k"IplC0%%mkjmYdDaLa
+aRTMKY3jNDDZ[%)Y&G-1U``8QU,cU*lBQl0fIRrqYj*1hIMf[QZFbVkS-UR-+H""
+pjLMUhPI5Gc!(RE3CXCF4ZaHa,b$f)m4Z41`@a2iGX5rdDqBF-Cq0#jLaQaM)PIT
+B$$J4i1`(6Jca&X5p5Mb+Z!I9#+2A-4H!Phl1BFZK6)X'P1Pd)2UT'YLl+([Xje"
+&X(G3"E#rQ$,#hLBf5[&#iIEbUHk)jIC*K8+LS[#3!(52q$"f"m9[bQGYVFb8Q&`
+SL*@*#SPH*-BrdXA*r3'p@1bk+AAU$prJ+hU&X5pX[UJ04bEU2XP'ZCd0j"eZ*#m
+%qlqCaqq1CTX@9GH5ZhFh`M30XVe"RY0mK03(jKUH*(m%QFdK*`6lFR*!X+p[Z,+
+'iPH95189,&0B*NpR#(H9ZJKIZ,r848kam%eb)A$qm9,A!Z$S*TAMdQVBIhjH,+V
+Cf"I`*%GkTjmP&c&i6"6,f)mBhYe(XFaka1!98Xc`AF4!9K6,rL&HEiKhr9'XaBT
+c5)ZK9LdVF@iPjNB`#p&b(@+GF!U3!(2,HX3LQ2j!R9YZ4Lb'qBkPL(d'X6E8'Vh
+5mJA%FKKHG*M,@Zj%E)HFAmL!'0L'amb)i9Q$2$B#X30+E$4Lqr*I3PE--!@a2EB
+rTZG''UC4RA1R`,YMKKUFHkCM*Hi9CKB"XDFQ2B@EFTRK`iKpQePi[Dp(,5Nb&l&
+1a(*B&krhpi'a5ijPASAB6UEMHBp$V"$+Rf)6%)0DjV&*r!@-Q6aQ3mc%$$b@KaM
+F(Kkl'$',NSGG',YRVj+(!qHQ+RR-3LaAb@-1BK192+j!6&6bF!(lN!"F'`YHeEK
+RIfEle8fYEB2[A-fZ#erMLpD9d*0B"pl(UMkP0I8+9U()@"I&&&Md@Mh0K'A94F1
+aF$0Z,XqLDlNaF1$Vb#S4Ui0,Si#Ji5H4$`Vm2`!5FJ!!!3#3!`%8!*!$&!#3!c)
+U9*!(+LVX!!!U+P53#Z`!!#T8N!-*EA4PFh3ZFfPd!J#3!e0*9%46593K!3!!T!)
+-!*!$!3!!8dP84&0*9#%"!!#N!J`!N"+`fEpN!!!Hm!!!!8C892q3"953!q`!N!4
+8N!Er9*!&l!!!+P53"Iq3!e53!q`!N!-U9*!+l1`!N!-Ul&53"qcX!*!&l!$X9*!
+$l*!$!*!+l*!$!*!,!3#3!rq3#J#3#"!!+!!@!48#!3#3"J-!N!-"!*!$!43!N!-
+8!*!$-J&20H`KbJ#3!a`!-J!!8f9dC`#3!`S!!2rr!*!%!8pi(2'-:
diff --git a/imap/src/osdep/mac/nl_mac.c b/imap/src/osdep/mac/nl_mac.c
new file mode 100644
index 00000000..6c42cd08
--- /dev/null
+++ b/imap/src/osdep/mac/nl_mac.c
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* destination provided? */
+ if ((i = srcl * 2) > *dstl) /* calculate worst-case situation */
+ for (i = j = srcl; j; --j) if (*d++ == '\015') i++;
+ /* flush destination buffer if too small */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* copy string */
+ c = *d++ = *src++; /* copy character */
+ /* append line feed to bare CR */
+ if ((c == '\015') && (*src != '\012')) *d++ = '\012';
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) if ((SNX (s) == '\015') && ((CHR (s) != '\012') || !j)) i++;
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/mac/os_mac.c b/imap/src/osdep/mac/os_mac.c
new file mode 100644
index 00000000..156eb5c3
--- /dev/null
+++ b/imap/src/osdep/mac/os_mac.c
@@ -0,0 +1,82 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+/* PPC cretins broke the MachineLocation struct */
+
+#define gmtFlags u
+
+#include <limits.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#define tcp_port MacTCP_port
+#include <MacTCPCommonTypes.h>
+#include <AddressXlation.h>
+#include <TCPPB.h>
+#include <Desk.h>
+#include <Devices.h>
+#include <Errors.h>
+#include <Files.h>
+#include <Fonts.h>
+#include <Menus.h>
+#include <Script.h>
+#include <ToolUtils.h>
+#include <Windows.h>
+#undef tcp_port
+
+#include "tcp_mac.h" /* must be before osdep.h */
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+
+static short TCPdriver = 0; /* MacTCP's reference number */
+short resolveropen = 0; /* TCP's resolver open */
+
+
+#include "env_mac.c"
+#include "fs_mac.c"
+#include "ftl_mac.c"
+#include "nl_mac.c"
+#include "tcp_mac.c"
+
+#define open(a,b,c) open (a,b)
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() "" /* dummy definition to prevent build errors */
+#define MD5ENABLE ""
+
+#include "auth_md5.c"
+#include "auth_pla.c"
+#include "auth_log.c"
diff --git a/imap/src/osdep/mac/os_mac.h b/imap/src/osdep/mac/os_mac.h
new file mode 100644
index 00000000..5cdad642
--- /dev/null
+++ b/imap/src/osdep/mac/os_mac.h
@@ -0,0 +1,69 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <types.h>
+#include <unix.h>
+#ifndef noErr
+#include <Desk.h>
+#include <Devices.h>
+#include <Errors.h>
+#include <Events.h>
+#include <Fonts.h>
+#include <Memory.h>
+#include <Menus.h>
+#include <ToolUtils.h>
+#include <Windows.h>
+#endif
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+extern short resolveropen; /* make this global so caller can sniff */
+
+#include "env_mac.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#define TCPDRIVER "\p.IPP"
+
+#define gethostid clock
+
+long wait (void);
+long random (void);
diff --git a/imap/src/osdep/mac/osdep.h b/imap/src/osdep/mac/osdep.h
new file mode 100644
index 00000000..9755c565
--- /dev/null
+++ b/imap/src/osdep/mac/osdep.h
@@ -0,0 +1,29 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+#include "os_mac.h"
diff --git a/imap/src/osdep/mac/pmatch.c b/imap/src/osdep/mac/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/mac/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/mac/tcp_mac.c b/imap/src/osdep/mac/tcp_mac.c
new file mode 100644
index 00000000..8d39d4f3
--- /dev/null
+++ b/imap/src/osdep/mac/tcp_mac.c
@@ -0,0 +1,557 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 13 January 2008
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 75; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long ttmo_close = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_CLOSETIMEOUT:
+ ttmo_close = (long) value;
+ case GET_CLOSETIMEOUT:
+ ret = (void *) ttmo_close;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream;
+ struct hostInfo hst;
+ struct TCPCreatePB *createpb;
+ struct TCPOpenPB *openpb;
+ char *s;
+ unsigned long i,j,k,l;
+ char tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* init MacTCP */
+ if (!TCPdriver && OpenDriver (TCPDRIVER,&TCPdriver)) {
+ mm_log ("Can't init MacTCP",ERROR);
+ return NIL;
+ }
+ if (!resolveropen && OpenResolver (NIL)) {
+ mm_log ("Can't init domain resolver",ERROR);
+ return NIL;
+ }
+ resolveropen = T; /* note resolver open now */
+ /* domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
+ hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l;
+ hst.addr[1] = 0; /* only one address to try! */
+ sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l);
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ else { /* look up host name */
+ if (!tcp_dns_upp) tcp_dns_upp = NewResultProc (tcp_dns_result);
+ if (StrToAddr (host,&hst,tcp_dns_upp,NIL)) {
+ while (hst.rtnCode == cacheFault && wait ());
+ /* kludge around MacTCP bug */
+ if (hst.rtnCode == outOfMemory) {
+ mm_log ("Re-initializing domain resolver",WARN);
+ CloseResolver (); /* bop it on the head and try again */
+ OpenResolver (NIL); /* note this will leak 12K */
+ StrToAddr (host,&hst,tcp_dns_upp,NIL);
+ while (hst.rtnCode == cacheFault && wait ());
+ }
+ if (hst.rtnCode) { /* still have error status? */
+ switch (hst.rtnCode) { /* analyze return */
+ case nameSyntaxErr:
+ s = "Syntax error in name";
+ break;
+ case noResultProc:
+ s = "No result procedure";
+ break;
+ case noNameServer:
+ s = "No name server found";
+ break;
+ case authNameErr:
+ s = "Host does not exist";
+ break;
+ case noAnsErr:
+ s = "No name servers responding";
+ break;
+ case dnrErr:
+ s = "Name server returned an error";
+ break;
+ case outOfMemory:
+ s = "Not enough memory to resolve name";
+ break;
+ case notOpenErr:
+ s = "Driver not open";
+ break;
+ default:
+ s = NIL;
+ break;
+ }
+ if (s) sprintf (tmp,"%s: %.80s",s,host);
+ else sprintf (tmp,"Unknown resolver error (%ld): %.80s",
+ hst.rtnCode,host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ }
+
+ /* create local TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->ictr = 0; /* initialize input */
+ stream->pb.ioCRefNum = TCPdriver;
+ createpb = &stream->pb.csParam.create;
+ openpb = &stream->pb.csParam.open;
+ stream->pb.csCode = TCPCreate;/* create a TCP stream */
+ /* set up buffer for TCP */
+ createpb->rcvBuffLen = 4*BUFLEN;
+ createpb->rcvBuff = fs_get (createpb->rcvBuffLen);
+ createpb->notifyProc = NIL; /* no special notify procedure */
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb))
+ fatal ("Can't create TCP stream");
+ /* open TCP connection */
+ stream->pb.csCode = TCPActiveOpen;
+ openpb->ulpTimeoutValue = (int) ttmo_open;
+ openpb->ulpTimeoutAction = T;
+ openpb->validityFlags = timeoutValue|timeoutAction;
+ /* remote host (should try all) */
+ openpb->remoteHost = hst.addr[0];
+ openpb->remotePort = port; /* caller specified remote port */
+ openpb->localPort = 0; /* generate a local port */
+ openpb->tosFlags = 0; /* no special TOS */
+ openpb->precedence = 0; /* no special precedence */
+ openpb->dontFrag = 0; /* allow fragmentation */
+ openpb->timeToLive = 255; /* standards say 60, UNIX uses 255 */
+ openpb->security = 0; /* no special security */
+ openpb->optionCnt = 0; /* no IP options */
+ openpb->options[0] = 0;
+ openpb->userDataPtr = NIL; /* no special data pointer */
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* got back error status? */
+ sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port);
+ mm_log (tmp,ERROR);
+ /* nuke the buffer */
+ stream->pb.csCode = TCPRelease;
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
+ /* free its buffer */
+ fs_give ((void **) &createpb->rcvBuff);
+ fs_give ((void **) &stream);/* and the local stream */
+ return NIL;
+ }
+
+ /* copy host names for later use */
+ stream->host = cpystr (hst.cname);
+ /* tie off trailing dot */
+ stream->host[strlen (stream->host) - 1] = '\0';
+ /* the open gave us our address */
+ i = (openpb->localHost >> 24) & 0xff;
+ j = (openpb->localHost >> 16) & 0xff;
+ k = (openpb->localHost >> 8) & 0xff;
+ l = openpb->localHost & 0xff;
+ sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l);
+ stream->localhost = cpystr (tmp);
+ if (!myLocalHost) myLocalHost = cpystr (tmp);
+ stream->port = port; /* copy port number */
+ return stream;
+}
+
+
+/* Called when have return from DNS
+ * Accepts: host info pointer
+ * user data pointer
+ */
+
+ResultUPP tcp_dns_upp = NIL;
+
+pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr)
+{
+ /* dummy routine */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* no authenticated opens on Mac */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ time_t t = time (0);
+ struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
+ struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ stream->pb.csCode = TCPRcv; /* receive TCP data */
+ receivepb->commandTimeoutValue = (int) ttmo_read;
+ receivepb->rcvBuff = stream->ibuf;
+ receivepb->rcvBuffLen = BUFLEN;
+ receivepb->secondTimeStamp = 0;
+ receivepb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* punt if got an error */
+ time_t tc = time (0);
+ if ((stream->pb.ioResult == commandTimeout) && tmoh &&
+ ((*tmoh) (tc - t,tc - tl, stream->host))) continue;
+ /* nuke connection */
+ stream->pb.csCode = TCPAbort;
+ abortpb->userDataPtr = NIL;
+ PBControlSync ((ParmBlkPtr) &stream->pb);
+ return NIL;
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = receivepb->rcvBuffLen;
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ struct TCPSendPB *sendpb = &stream->pb.csParam.send;
+ struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
+ struct {
+ unsigned short length;
+ Ptr buffer;
+ unsigned short trailer;
+ } wds;
+ while (wds.length = (size > (unsigned long) 32768) ? 32768 : size) {
+ wds.buffer = string; /* buffer */
+ wds.trailer = 0; /* tie off buffer */
+ size -= wds.length; /* this many words will be output */
+ string += wds.length;
+ stream->pb.csCode = TCPSend;/* send TCP data */
+ sendpb->ulpTimeoutValue = (int) ttmo_write;
+ sendpb->ulpTimeoutAction = 0;
+ sendpb->validityFlags = timeoutValue|timeoutAction;
+ sendpb->pushFlag = T; /* send the data now */
+ sendpb->urgentFlag = NIL; /* non-urgent data */
+ sendpb->wdsPtr = (Ptr) &wds;
+ sendpb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* punt if got an error */
+ /* nuke connection */
+ stream->pb.csCode =TCPAbort;
+ abortpb->userDataPtr = NIL;
+ PBControlSync ((ParmBlkPtr) &stream->pb);
+ return NIL;
+ }
+ }
+ return T; /* success */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ struct TCPClosePB *closepb = &stream->pb.csParam.close;
+ struct TCPCreatePB *createpb = &stream->pb.csParam.create;
+ stream->pb.csCode = TCPClose; /* close TCP stream */
+ closepb->ulpTimeoutValue = (int) ttmo_close;
+ closepb->ulpTimeoutAction = 0;
+ closepb->validityFlags = timeoutValue|timeoutAction;
+ closepb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ stream->pb.csCode =TCPRelease;/* flush the buffers */
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
+ /* free its buffer */
+ fs_give ((void **) &createpb->rcvBuff);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+/* TCP/IP return host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return remote host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP return local host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: local host name for this stream
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ int i;
+ struct hostInfo hst;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[i = (strlen (name))-1] == ']') return name;
+ if (StrToAddr (name,&hst,tcp_dns_upp,NIL)) {
+ while (hst.rtnCode == cacheFault && wait ());
+ /* kludge around MacTCP bug */
+ if (hst.rtnCode == outOfMemory) {
+ mm_log ("Re-initializing domain resolver",WARN);
+ CloseResolver (); /* bop it on the head and try again */
+ OpenResolver (NIL); /* note this will leak 12K */
+ StrToAddr (name,&hst,tcp_dns_upp,NIL);
+ while (hst.rtnCode == cacheFault && wait ());
+ }
+ /* still have error status? */
+ if (hst.rtnCode) return name;
+ }
+ return hst.cname; /* success */
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/mac/tcp_mac.h b/imap/src/osdep/mac/tcp_mac.h
new file mode 100644
index 00000000..7e9b4a72
--- /dev/null
+++ b/imap/src/osdep/mac/tcp_mac.h
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN (size_t) 8192 /* TCP input buffer */
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ struct TCPiopb pb; /* MacTCP parameter block */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+extern ResultUPP tcp_dns_upp;
+pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr);
diff --git a/imap/src/osdep/nt/drivers.bat b/imap/src/osdep/nt/drivers.bat
new file mode 100755
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/nt/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/drivraux.bat b/imap/src/osdep/nt/drivraux.bat
new file mode 100755
index 00000000..30649a78
--- /dev/null
+++ b/imap/src/osdep/nt/drivraux.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C
+if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/nt/dummy.h b/imap/src/osdep/nt/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/nt/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/nt/dummynt.c b/imap/src/osdep/nt/dummynt.c
new file mode 100644
index 00000000..b44d271f
--- /dev/null
+++ b/imap/src/osdep/nt/dummynt.c
@@ -0,0 +1,724 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <direct.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate INBOX */
+ if (!*s) return &dummydriver;
+ /* remove trailing \ */
+ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0';
+ if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG: /* file */
+ case S_IFDIR: /* future use */
+ return &dummydriver;
+ }
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ struct _finddata_t f;
+ struct stat sbuf;
+ long fhandle;
+ char tmp[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*");
+ /* do nothing if can't open directory */
+ if ((fhandle = _findfirst (tmp,&f)) >= 0) {
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '\\') do
+ if (((f.name[0] != '.') ||
+ (f.name[1] && ((f.name[1] != '.') || f.name[2]))) &&
+ ((len + strlen (f.name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (tmp,pat,'\\') ||
+ pmatch_full (strcat (tmp,"\\"),pat,'\\') ||
+ dmatch (tmp,pat,'\\')) &&
+ mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) {
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break;
+ strcat (tmp,"\\");/* set up for dmatch call */
+ }
+ /* try again with trailing \ */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') &&
+ !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))
+ break;
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ while (!_findnext (fhandle,&f));
+ _findclose(fhandle);
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ struct _finddata_t f;
+ int fd,nochild;
+ long fhandle,csiz,ssiz,bsiz;
+ char *s,*buf,tmp[MAILTMPLEN];
+ /* if not \NoInferiors */
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*") &&
+ ((fhandle = _findfirst (tmp,&f)) >= 0)) {
+ nochild = T;
+ do if ((f.name[0] != '.') || (f.name[1] && ((f.name[1] != '.') ||
+ f.name[2]))) nochild = NIL;
+ while (nochild && !_findnext (fhandle,&f));
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ _findclose (fhandle); /* all done, flush directory */
+ }
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !(s = dummy_file (tmp,name)) || stat (s,&sbuf) ||
+ (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox))
+ return dummy_create_path (stream,tmp,NIL);
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ mm_log (tmp,ERROR);
+ }
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ long ret = NIL;
+ /* no trailing \ allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'\\')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ mm_log (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ /* found superior to destination name? */
+ else if ((s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,mbx)) return NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ mm_log (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = (unsigned long) time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/nt/env_nt.c b/imap/src/osdep/nt/env_nt.c
new file mode 100644
index 00000000..18bc2369
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.c
@@ -0,0 +1,774 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: NT environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 February 2008
+ */
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *sysInbox = NIL; /* system inbox name */
+static long list_max_level = 5; /* maximum level of list recursion */
+ /* block environment init */
+static short block_env_init = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* UNIX other user namespace */
+static NAMESPACE nsother = {"#user.",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,&nsother,NIL};
+static long alarm_countdown = 0;/* alarm count down */
+static void (*alarm_rang) (); /* alarm interrupt function */
+static unsigned int rndm = 0; /* initial `random' number */
+static int server_nli = 0; /* server and not logged in */
+static int logtry = 3; /* number of login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+ /* callback to get username */
+static userprompt_t mailusername = NIL;
+static long is_nt = -1; /* T if NT, NIL if not NT, -1 unknown */
+static HINSTANCE netapi = NIL;
+typedef NET_API_STATUS (CALLBACK *GETINFO) (LPCWSTR,LPCWSTR,DWORD,LPBYTE *);
+static GETINFO getinfo = NIL;
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERPROMPT :
+ mailusername = (userprompt_t) value;
+ case GET_USERPROMPT :
+ ret = (void *) mailusername;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) {
+ char *s;
+ for (s = tz; *s; s++) if (*s & 0x80) return;
+ sprintf (date + strlen (date)," (%.50s)",tz);
+ }
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return random number
+ */
+
+long random (void)
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+
+/* Set alarm timer
+ * Accepts: new value
+ * Returns: old alarm value
+ */
+
+long alarm (long seconds)
+{
+ long ret = alarm_countdown;
+ alarm_countdown = seconds;
+ return ret;
+}
+
+
+/* The clock ticked
+ */
+
+void CALLBACK clock_ticked (UINT IDEvent,UINT uReserved,DWORD dwUser,
+ DWORD dwReserved1,DWORD dwReserved2)
+{
+ if (alarm_rang && !--alarm_countdown) (*alarm_rang) ();
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ if (!check_nt ()) {
+ if (!auth_md5.server) fatal ("Can't run on Windows without MD5 database");
+ server_nli = T; /* Windows server not logged in */
+ }
+ /* only do this if for init call */
+ if (server && service && sslservice) {
+ long port;
+ struct servent *sv;
+ /* set server name in syslog */
+ openlog (server,LOG_PID,LOG_MAIL);
+ fclose (stderr); /* possibly save a process ID */
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ /* make sure stdout does binary */
+ setmode (fileno (stdin),O_BINARY);
+ setmode (fileno (stdout),O_BINARY);
+ }
+ alarm_rang = clkint; /* note the clock interrupt */
+ timeBeginPeriod (1000); /* set the timer interval */
+ timeSetEvent (1000,1000,clock_ticked,NIL,TIME_PERIODIC);
+}
+
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+static int gotprivs = NIL; /* once-only flag to grab privileges */
+
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
+{
+ HANDLE hdl;
+ LUID tcbpriv;
+ TOKEN_PRIVILEGES tkp;
+ char *s;
+ /* need to get privileges? */
+ if (!gotprivs++ && check_nt ()) {
+ /* hack for inetlisn */
+ if (argc >= 2) myClientHost = argv[1];
+ /* get process token and TCB priv value */
+ if (!(OpenProcessToken (GetCurrentProcess (),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hdl) &&
+ LookupPrivilegeValue ((LPSTR) NIL,SE_TCB_NAME,&tcbpriv)))
+ return NIL;
+ tkp.PrivilegeCount = 1; /* want to enable this privilege */
+ tkp.Privileges[0].Luid = tcbpriv;
+ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ /* enable it */
+ AdjustTokenPrivileges (hdl,NIL,&tkp,sizeof (TOKEN_PRIVILEGES),
+ (PTOKEN_PRIVILEGES) NIL,(PDWORD) NIL);
+ /* make sure it won */
+ if (GetLastError() != ERROR_SUCCESS) return NIL;
+ }
+
+ /* cretins still haven't given up */
+ if ((strlen (user) >= MAILTMPLEN) ||
+ (authuser && (strlen (authuser) >= MAILTMPLEN)))
+ syslog (LOG_ALERT,"SYSTEM BREAK-IN ATTEMPT, host=%.80s",tcp_clienthost ());
+ else if (logtry > 0) { /* still have available logins? */
+ /* authentication user not supported */
+ if (authuser && *authuser && compare_cstring (authuser,user))
+ mm_log ("Authentication id must match authorization id",ERROR);
+ if (check_nt ()) { /* NT: authserver_login() call not supported */
+ if (!pass) mm_log ("Unsupported authentication mechanism",ERROR);
+ else if (( /* try to login and impersonate the guy */
+#ifdef LOGIN32_LOGON_NETWORK
+ LogonUser (user,".",pass,LOGON32_LOGON_NETWORK,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+#endif
+ LogonUser (user,".",pass,LOGON32_LOGON_INTERACTIVE,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+ LogonUser (user,".",pass,LOGON32_LOGON_BATCH,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+ LogonUser (user,".",pass,LOGON32_LOGON_SERVICE,
+ LOGON32_PROVIDER_DEFAULT,&hdl)) &&
+ ImpersonateLoggedOnUser (hdl)) return env_init (user,NIL);
+ }
+ else { /* Win9x: done if from authserver_login() */
+ if (!pass) server_nli = NIL;
+ /* otherwise check MD5 database */
+ else if (s = auth_md5_pwd (user)) {
+ /* change NLI state based on pwd match */
+ server_nli = strcmp (s,pass);
+ memset (s,0,strlen (s));/* erase sensitive information */
+ fs_give ((void **) &s); /* flush erased password */
+ }
+ /* success if no longer NLI */
+ if (!server_nli) return env_init (user,NIL);
+ }
+ }
+ s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts";
+ /* note the failure in the syslog */
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authentication user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return server_login (user,NIL,authuser,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ return server_login ("Guest",NIL,NIL,argc,argv);
+}
+
+
+/* Initialize environment
+ * Accepts: user name
+ * home directory, or NIL to use default
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ myUserName = cpystr (user); /* remember user name */
+ if (!myHomeDir) /* only if home directory not set up yet */
+ myHomeDir = (home && *home) ? cpystr (home) : win_homedir (user);
+ return T;
+}
+
+/* Check if NT
+ * Returns: T if NT, NIL if Win9x
+ */
+
+int check_nt (void)
+{
+ if (is_nt < 0) { /* not yet set up? */
+ OSVERSIONINFO ver;
+ ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&ver);
+ is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) ? T : NIL;
+ }
+ return is_nt;
+}
+
+
+/* Return Windows home directory
+ * Accepts: user name
+ * Returns: home directory
+ */
+
+char *win_homedir (char *user)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ PUSER_INFO_1 ui;
+ /* Win9x default */
+ if (!check_nt ()) sprintf (tmp,"%s\\My Documents",defaultDrive ());
+ /* get from user info on NT */
+ else if ((netapi || (netapi = LoadLibrary ("netapi32.dll"))) &&
+ (getinfo ||
+ (getinfo = (GETINFO) GetProcAddress (netapi,"NetUserGetInfo"))) &&
+ MultiByteToWideChar (CP_ACP,0,user,strlen (user) + 1,
+ (WCHAR *) tmp,MAILTMPLEN) &&
+ !(*getinfo) (NIL,(LPWSTR) &tmp,1,(LPBYTE *) &ui) &&
+ WideCharToMultiByte (CP_ACP,0,ui->usri1_home_dir,-1,
+ tmp,MAILTMPLEN,NIL,NIL) && tmp[0]) {
+ /* make sure doesn't end with delimiter */
+ if ((*(s = tmp + strlen (tmp) - 1) == '\\') || (*s == '/')) *s = '\0';
+ }
+ /* no home dir, found Win2K user profile? */
+ else if ((s = getenv ("USERPROFILE")) && (t = strrchr (s,'\\'))) {
+ strncpy (tmp,s,t-s); /* copy up to user name */
+ sprintf (tmp+(t-s),"\\%.100s\\My Documents",user);
+ }
+ /* last resort NT default */
+ else sprintf (tmp,"%s\\users\\default",defaultDrive ());
+ return cpystr (tmp);
+}
+
+
+/* Return default drive
+ * Returns: default drive
+ */
+
+static char *defaultDrive (void)
+{
+ char *s = getenv ("SystemDrive");
+ return (s && *s) ? s : "C:";
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ UCHAR usr[MAILTMPLEN];
+ DWORD len = MAILTMPLEN;
+ char *user,*path,*d,*p,pth[MAILTMPLEN];
+ char *ret = "SYSTEM";
+ /* get user name if don't have it yet */
+ if (!myUserName && !server_nli &&
+ /* use callback, else logon name */
+ ((mailusername && (user = (char *) (*mailusername) ())) ||
+ (GetUserName (usr,&len) && _stricmp (user = (char *) usr,"SYSTEM")))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return user;
+ }
+ /* try HOMEPATH, then HOME */
+ if (p = getenv ("HOMEPATH"))
+ sprintf (path = pth,"%s%s",
+ (d = getenv ("HOMEDRIVE")) ? d : defaultDrive (),p);
+ else if (!(path = getenv ("HOME")))
+ sprintf (path = pth,"%s\\My Documents",defaultDrive ());
+ /* make sure doesn't end with delimiter */
+ if ((*(p = path + strlen (path) -1) == '\\') || (*p == '/')) *p = '\0';
+ env_init (user,path); /* initialize environment */
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) /* Guest is an anonymous user */
+ *flags = _stricmp (myUserName,"Guest") ? MU_LOGGEDIN : MU_ANONYMOUS;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ if (check_nt ()) sprintf (tmp,MAILFILE,myUserName);
+ else sprintf (tmp,"%s\\INBOX",myhomedir ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ else strcpy (dst,myhomedir());/* no arguments, wants home directory */
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char homedev[3];
+ char *dir = myhomedir ();
+ if (dir[0] && isalpha (dir[0]) && (dir[1] == ':')) {
+ homedev[0] = dir[0]; /* copy home device */
+ homedev[1] = dir[1];
+ homedev[2] = '\0';
+ }
+ else homedev[0] = '\0'; /* ??no home device?? */
+ *dst = '\0'; /* default to empty string */
+ /* check for INBOX */
+ if (!compare_cstring (name,"INBOX"));
+ /* reject names with / */
+ else if (strchr (name,'/')) dst = NIL;
+ else switch (*name) {
+ case '#': /* namespace names */
+ if (((name[1] == 'u') || (name[1] == 'U')) &&
+ ((name[2] == 's') || (name[2] == 'S')) &&
+ ((name[3] == 'e') || (name[3] == 'E')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) && (name[5] == '.')) {
+ /* copy user name to destination buffer */
+ for (dir = dst,name += 6; *name && (*name != '\\'); *dir++ = *name++);
+ *dir++ = '\0'; /* tie off user name */
+ /* look up homedir for user name */
+ if (dir = win_homedir (dst)) {
+ /* build resulting name */
+ sprintf (dst,"%s\\%s",dir,name);
+ fs_give ((void **) &dir);
+ }
+ else dst = NIL;
+ }
+ else dst = NIL; /* unknown namespace name */
+ break;
+ case '\\': /* absolute path on default drive? */
+ sprintf (dst,"%s%s",homedev,name);
+ break;
+ default: /* any other name */
+ if (name[1] == ':') { /* some other drive? */
+ if (name[2] == '\\') strcpy (dst,name);
+ else sprintf (dst,"%c:\\%s",name[0],name+2);
+ }
+ /* build home-directory relative name */
+ else sprintf (dst,"%s\\%s",dir,name);
+ }
+ return dst; /* return it */
+}
+
+/* Lock file name
+ * Accepts: return buffer for file name
+ * file name
+ * locking to be placed on file if non-NIL
+ * Returns: file descriptor of lock or -1 if error
+ */
+
+int lockname (char *lock,char *fname,int op)
+{
+ int ld;
+ char c,*s;
+ /* Win2K and Win98 have TEMP under windir */
+ if (!((s = lockdir (lock,getenv ("windir"),"TEMP")) ||
+ /* NT4, NT3.x and Win95 use one of these */
+ (s = lockdir (lock,getenv ("TEMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
+ /* try one of these */
+ (s = lockdir (lock,defaultDrive (),"WINNT\\TEMP")) ||
+ (s = lockdir (lock,defaultDrive (),"WINDOWS\\TEMP")) ||
+ /* C:\TEMP is last resort */
+ (s = lockdir (lock,defaultDrive (),"TEMP")))) {
+ mm_log ("Unable to find temporary directory",ERROR);
+ return -1;
+ }
+ /* generate file name */
+ while (c = *fname++) switch (c) {
+ case '/': case '\\': case ':':
+ *s++ = '!'; /* convert bad chars to ! */
+ break;
+ default:
+ *s++ = c;
+ break;
+ }
+ *s++ = c; /* tie off name */
+ /* get the lock */
+ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
+ flock (ld,op); /* apply locking function */
+ return ld; /* return locking file descriptor */
+}
+
+/* Build lock directory, check to see if it exists
+ * Accepts: return buffer for lock directory
+ * first part of possible name
+ * optional second part
+ * Returns: pointer to end of buffer if buffer has a good name, else NIL
+ */
+
+char *lockdir (char *lock,char *first,char *last)
+{
+ struct stat sbuf;
+ char c,*s;
+ if (first && *first) { /* first part must be non-NIL */
+ /* copy first part */
+ for (s = lock; c = *first++; *s++ = (c == '/') ? '\\' : c);
+ if (last && *last) { /* copy last part if specified */
+ /* write trailing \ in case not in first */
+ if (s[-1] != '\\') *s++ = '\\';
+ while (c = *last++) *s++ = (c == '/') ? '\\' : c;
+ }
+ if (s[-1] == '\\') --s; /* delete trailing \ if any */
+ *s = s[1] = '\0'; /* tie off name at this point */
+ if (!stat (lock,&sbuf)) { /* does the name exist? */
+ *s++ = '\\'; /* yes, reinstall trailing \ */
+ return s; /* return the name */
+ }
+ }
+ return NIL; /* failed */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM CREATEPROTO,APPENDPROTO;
+ return type ? &APPENDPROTO : &CREATEPROTO;
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned int) data) alarm ((unsigned int) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/env_nt.h b/imap/src/osdep/nt/env_nt.h
new file mode 100644
index 00000000..95575246
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.h
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: NT environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ())
+
+/* Note: the \CRAM-MD5.PWD file only works on DOS-based Windows (Win9x/Me)
+ * and not on NT-based Windows (WinNT/2K/XP). If installed on NT-based
+ * Windows, servers will advertise CRAM-MD5 authentication but it will
+ * never succeed.
+ */
+
+#define MD5ENABLE "\\cram-md5.pwd"
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+int check_nt (void);
+char *win_homedir (char *user);
+static char *defaultDrive (void);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+int lockname (char *lock,char *fname,int op);
+char *lockdir (char *lock,char *first,char *last);
+void unlockfd (int fd,char *lock);
+long safe_write (int fd,char *buf,long nbytes);
+void *mm_blocknotify (int reason,void *data);
+long random ();
+#if _MSC_VER < 700
+#define getpid random
+#endif
diff --git a/imap/src/osdep/nt/fdstring.c b/imap/src/osdep/nt/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/nt/fdstring.h b/imap/src/osdep/nt/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/nt/fs_nt.c b/imap/src/osdep/nt/fs_nt.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/nt/fs_nt.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/nt/ftl_nt.c b/imap/src/osdep/nt/ftl_nt.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/nt/ftl_nt.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/nt/ip4_nt.c b/imap/src/osdep/nt/ip4_nt.c
new file mode 100644
index 00000000..23d399e1
--- /dev/null
+++ b/imap/src/osdep/nt/ip4_nt.c
@@ -0,0 +1,184 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char **adl,tmp[MAILTMPLEN];
+ struct hostent *he;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ adl = he->h_addr_list;
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = (void *) adl;
+ }
+ else { /* error */
+ adl = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (adl = (char **) *next)) *next = ++adl;
+ else adl = NIL; /* failure */
+ return adl ? (void *) *adl : NIL;
+}
diff --git a/imap/src/osdep/nt/ip6_nt.c b/imap/src/osdep/nt/ip6_nt.c
new file mode 100644
index 00000000..c15dfbc0
--- /dev/null
+++ b/imap/src/osdep/nt/ip6_nt.c
@@ -0,0 +1,288 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv6 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+
+/*
+ * There is some amazingly bad design in IPv6 sockets.
+ *
+ * Supposedly, the new getnameinfo() and getaddrinfo() functions create an
+ * abstraction that is not dependent upon IPv4 or IPv6. However, the
+ * definition of getnameinfo() requires that the caller pass the length of
+ * the sockaddr instead of deriving it from sa_family. The man page says
+ * that there's an sa_len member in the sockaddr, but actually there isn't.
+ * This means that any caller to getnameinfo() and getaddrinfo() has to know
+ * the size for the protocol family used by that sockaddr.
+ *
+ * The new sockaddr_in6 is bigger than the generic sockaddr (which is what
+ * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect).
+ * Rather than increase the size of sockaddr, there's a new sockaddr_storage
+ * which is only usable for allocating space.
+ */
+
+#define SADRLEN sizeof (struct sockaddr_storage)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+#define SADR6(sadr) ((struct sockaddr_in6 *) sadr)
+#define SADR6LEN sizeof (struct sockaddr_in6)
+#define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr
+#define ADR6LEN sizeof (struct in6_addr)
+#define SADR6PORT(sadr) SADR6 (sadr)->sin6_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ }
+ return "NON-IP";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ switch (sadr->sa_family) {
+ case PF_INET:
+ return ntohs (SADR4PORT (sadr));
+ case PF_INET6:
+ return ntohs (SADR6PORT (sadr));
+ }
+ return -1;
+}
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+
+{
+ char tmp[MAILTMPLEN];
+ static struct addrinfo *hints;
+ struct addrinfo *ai;
+ void *adr = NIL;
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ hints->ai_family = AF_UNSPEC;/* allow any address family */
+ hints->ai_socktype = SOCK_STREAM;
+ /* numeric name only */
+ hints->ai_flags = AI_NUMERICHOST;
+ }
+ /* case-independent lookup */
+ if (text && (strlen (text) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) {
+ switch (*family = ai->ai_family) {
+ case AF_INET: /* IPv4 */
+ adr = fs_get (*len = ADR4LEN);
+ memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len);
+ break;
+ case AF_INET6: /* IPv6 */
+ adr = fs_get (*len = ADR6LEN);
+ memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len);
+ break;
+ }
+ freeaddrinfo (ai); /* free addrinfo */
+ }
+ return adr;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ case AF_INET6: /* IPv6 */
+ sadr->sa_family = PF_INET6;
+ /* copy host address */
+ memcpy (&SADR6ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR6PORT (sadr) = htons (port);
+ *len = SADR6LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ }
+ return NIL;
+}
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ struct addrinfo *cur = NIL;
+ static struct addrinfo *hints;
+ static struct addrinfo *ai = NIL;
+ static char lcname[MAILTMPLEN];
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ /* allow any address family */
+ hints->ai_family = AF_UNSPEC;
+ hints->ai_socktype = SOCK_STREAM;
+ /* need canonical name */
+ hints->ai_flags = AI_CANONNAME;
+ }
+ if (name) { /* name supplied? */
+ if (ai) {
+ freeaddrinfo (ai); /* free old addrinfo */
+ ai = NIL;
+ }
+ /* case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) {
+ cur = ai; /* current block */
+ if (canonical) /* set canonical name */
+ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname;
+ /* remember as next block */
+ if (next) *next = (void *) ai;
+ }
+ else { /* error */
+ cur = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) {
+ *next = cur; /* set as last address */
+ /* set canonical in case changed */
+ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname;
+ }
+
+ if (cur) { /* got data? */
+ if (family) *family = cur->ai_family;
+ switch (cur->ai_family) {
+ case AF_INET:
+ if (len) *len = ADR4LEN;
+ return (void *) &SADR4ADR (cur->ai_addr);
+ case AF_INET6:
+ if (len) *len = ADR6LEN;
+ return (void *) &SADR6ADR (cur->ai_addr);
+ }
+ }
+ if (len) *len = 0; /* error return */
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_mit.c b/imap/src/osdep/nt/kerb_mit.c
new file mode 100644
index 00000000..57ede524
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_mit.c
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIT Kerberos routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 March 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ switch (error) {
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_FCC_NOFILE: /* MIT */
+ case KRB5_CC_NOTFOUND: /* Heimdal */
+ return LONGT;
+ }
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_w2k.c b/imap/src/osdep/nt/kerb_w2k.c
new file mode 100644
index 00000000..38d0ce30
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_w2k.c
@@ -0,0 +1,699 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GSSAPI Kerberos Shim 5 for Windows 2000/XP IMAP Toolkit
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 March 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* The purpose of this module is to be a shim, so that the auth_gss.c module
+ * (written for MIT Kerberos) will compile, link, and run with SSPI Kerberos
+ * on Windows 2000 systems.
+ * There is no attempt whatsoever to make this be a complete implementation
+ * of GSSAPI. A number of shortcuts were taken that a real GSSAPI
+ * implementation for SSPI can't do.
+ * Nor is there any attempt to make the types identical with MIT Kerberos;
+ * you can't link this library with object files compiled with the MIT
+ * Kerberos .h files.
+ */
+
+
+/* GSSAPI generic definitions */
+
+
+#define SECURITY_WIN32
+#include <security.h>
+
+
+/* GSSAPI types for which we use SSPI equivalent types */
+
+typedef ULONG OM_uint32;
+typedef PCredHandle gss_cred_id_t;
+typedef ULONG gss_cred_usage_t;
+typedef PCtxtHandle gss_ctx_id_t;
+typedef SEC_CHAR * gss_name_t;
+typedef ULONG gss_qop_t;
+
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE SEC_E_OK
+#define GSS_S_BAD_MECH SEC_E_SECPKG_NOT_FOUND
+#define GSS_S_CONTINUE_NEEDED SEC_I_CONTINUE_NEEDED
+#define GSS_S_CREDENTIALS_EXPIRED SEC_E_CERT_EXPIRED
+#define GSS_S_FAILURE SEC_E_INTERNAL_ERROR
+#define GSS_S_NO_CRED SEC_E_NO_CREDENTIALS
+#define GSS_S_NO_CONTEXT SEC_E_INVALID_HANDLE
+
+
+/* Flag bits for context-level services */
+
+#define GSS_C_DELEG_FLAG ISC_REQ_DELEGATE
+#define GSS_C_MUTUAL_FLAG ISC_REQ_MUTUAL_AUTH
+#define GSS_C_REPLAY_FLAG ISC_REQ_REPLAY_DETECT
+#define GSS_C_SEQUENCE_FLAG ISC_REQ_SEQUENCE_DETECT
+#define GSS_C_CONF_FLAG ISC_REQ_CONFIDENTIALITY
+#define GSS_C_INTEG_FLAG ISC_REQ_INTEGRITY
+
+
+/* Credential usage options */
+
+#define GSS_C_BOTH SECPKG_CRED_BOTH
+#define GSS_C_INITIATE SECPKG_CRED_OUTBOUND
+#define GSS_C_ACCEPT SECPKG_CRED_INBOUND
+
+
+/* Major status codes defined by shim */
+
+#define GSS_S_BAD_BINDINGS 100
+#define GSS_S_BAD_NAME 101
+#define GSS_S_BAD_NAMETYPE 102
+#define GSS_S_BAD_STATUS 103
+
+/* GSSAPI types as used in GSSAPI */
+
+
+/* Buffer */
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc,*gss_buffer_t;
+
+
+/* Object identifier */
+
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc,*gss_OID;
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc,*gss_OID_set;
+
+
+/* Unused, but needed in prototypes */
+
+typedef void * gss_channel_bindings_t;
+
+
+/* Default constants */
+
+#define GSS_C_EMPTY_BUFFER {0,NIL}
+#define GSS_C_NO_BUFFER ((gss_buffer_t) NIL)
+#define GSS_C_NO_OID ((gss_OID) NIL)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) NIL)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) NIL)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) NIL)
+#define GSS_C_QOP_DEFAULT NIL
+
+
+/* Status code types for gss_display_status */
+
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+
+/* GSSAPI constants */
+
+const gss_OID gss_nt_service_name;
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+const gss_OID gss_mech_krb5;
+const gss_OID_set gss_mech_set_krb5;
+
+/* GSSAPI prototypes */
+
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec);
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token);
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type);
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string);
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name);
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec);
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer);
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle);
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name);
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer);
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state);
+
+/* Kerberos definitions */
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+
+#define STRING WINSTRING /* conflict with mail.h */
+#include <NTSecAPI.h>
+
+/* GSSAPI build-in object identifiers */
+
+static gss_OID_desc oids[] = { /* stupid C language makes this necessary */
+ {10,"\052\206\110\206\367\022\001\002\001\004"},
+ {9,"\052\206\110\206\367\022\001\002\002"}
+};
+
+ /* stupid C language ditto */
+static gss_OID_set_desc oidsets[] = {
+ {1,(gss_OID) oids+1}
+};
+
+ /* these are the real OIDs */
+const gss_OID gss_nt_service_name = oids+0;
+const gss_OID gss_mech_krb5 = oids+1;
+const gss_OID_set gss_mech_set_krb5 = oidsets+0;
+
+
+/* Other globals */
+
+ /* substitute for GSS_C_NO_CREDENTIAL */
+static gss_cred_id_t gss_default_cred = NIL;
+
+/* GSSAPI import name (convert to full service principal name)
+ * Accepts: pointer to return minor status
+ * buffer containining input name
+ * type of input name
+ * pointer to return output internal name
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name)
+{
+ OM_uint32 major_status = GSS_S_COMPLETE;
+ TimeStamp expiry;
+ static CredHandle gss_cred;
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (!gss_default_cred) { /* default credentials set up yet? */
+ if (AcquireCredentialsHandle/* no, acquire them now */
+ (NIL,MICROSOFT_KERBEROS_NAME_A,SECPKG_CRED_OUTBOUND,NIL,NIL,NIL,NIL,
+ &gss_cred,&expiry) != SEC_E_OK) return GSS_S_FAILURE;
+ /* have default credentials now */
+ gss_default_cred = &gss_cred;
+ }
+ /* must be the gss_nt_service_name format */
+ if (input_name_type != gss_nt_service_name)
+ major_status = GSS_S_BAD_NAMETYPE;
+ /* name must be of sane length */
+ else if (input_name_buffer->length > (MAILTMPLEN/2))
+ major_status = GSS_S_BAD_NAME;
+ else { /* copy name */
+ memcpy (tmp,input_name_buffer->value,input_name_buffer->length);
+ tmp[input_name_buffer->length] = '\0';
+ if (s = strchr (tmp,'@')) { /* find service/host/delimiter */
+ *s = '/'; /* convert to full service principal name */
+ *output_name = cpystr (tmp);
+ }
+ else major_status = GSS_S_BAD_NAME;
+ }
+ return major_status;
+}
+
+/* GSSAPI Initialize security context
+ * Accepts: pointer to return minor status
+ * claimant credential handle
+ * context (NIL means "none assigned yet")
+ * desired principal
+ * desired mechanisms
+ * required context attributes
+ * desired lifetime
+ * input channel bindings
+ * input token buffer
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 i;
+ OM_uint32 major_status;
+ TimeStamp expiry;
+ SecBuffer ibuf[1],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ *minor_status = 0; /* never any minor status */
+ /* error if non-default time requested */
+ if (time_req) return GSS_S_FAILURE;
+ if (mech_type && memcmp (mech_type,gss_mech_krb5,sizeof (gss_OID)))
+ return GSS_S_BAD_MECH;
+ /* ditto if any channel bindings */
+ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS)
+ return GSS_S_BAD_BINDINGS;
+
+ /* apply default credential if necessary */
+ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
+ claimant_cred_handle = gss_default_cred;
+ /* create output buffer storage as needed */
+ req_flags |= ISC_REQ_ALLOCATE_MEMORY;
+ /* make output buffer */
+ obuf[0].BufferType = SECBUFFER_TOKEN;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ /* output buffer descriptor */
+ obufs.ulVersion = SECBUFFER_VERSION;
+ obufs.cBuffers = 1;
+ obufs.pBuffers = obuf;
+ /* first time caller? */
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ /* yes, set up output context handle */
+ PCtxtHandle ctx = (PCtxtHandle) fs_get (sizeof (CtxtHandle));
+ major_status = InitializeSecurityContext (claimant_cred_handle,NIL,
+ target_name,req_flags,0,
+ SECURITY_NETWORK_DREP,NIL,0,ctx,
+ &obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ *context_handle = ctx; /* return updated context */
+ }
+ else { /* no, make SSPI buffer from GSSAPI buffer */
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[0].cbBuffer = input_token->length;
+ ibuf[0].pvBuffer = input_token->value;
+ /* input buffer descriptor */
+ ibufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf;
+ major_status = InitializeSecurityContext (claimant_cred_handle,
+ *context_handle,target_name,
+ req_flags,0,
+ SECURITY_NETWORK_DREP,&ibufs,0,
+ *context_handle,&obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ }
+ /* return output */
+ output_token->value = obuf[0].pvBuffer;
+ output_token->length = obuf[0].cbBuffer;
+ /* in case client wanted lifetime returned */
+ if (time_rec) *time_rec = expiry.LowPart;
+ return major_status;
+}
+
+/* GSSAPI display status text
+ * Accepts: pointer to return minor status
+ * status to display
+ * status type
+ * message context for continuation
+ * buffer to write status string
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string)
+{
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (*message_context) return GSS_S_FAILURE;
+ switch (status_type) { /* what type of status code? */
+ case GSS_C_GSS_CODE: /* major_status */
+ switch (status_value) { /* analyze status value */
+ case GSS_S_FAILURE:
+ s = "Unspecified failure"; break;
+ case GSS_S_CREDENTIALS_EXPIRED:
+ s = "Credentials expired"; break;
+ case GSS_S_BAD_BINDINGS:
+ s = "Bad bindings"; break;
+ case GSS_S_BAD_MECH:
+ s = "Bad mechanism type"; break;
+ case GSS_S_BAD_NAME:
+ s = "Bad name"; break;
+ case GSS_S_BAD_NAMETYPE:
+ s = "Bad name type"; break;
+ case GSS_S_BAD_STATUS:
+ s = "Bad status"; break;
+ case GSS_S_NO_CONTEXT:
+ s = "Invalid context handle"; break;
+ case GSS_S_NO_CRED:
+ s = "Unable to authenticate to Kerberos service";
+ mail_parameters (NIL,DISABLE_AUTHENTICATOR,"GSSAPI");
+ break;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ s = "No authenticating authority"; break;
+ case SEC_E_TARGET_UNKNOWN:
+ s = "Destination server unknown to Kerberos service"; break;
+ default:
+ sprintf (s = tmp,"SSPI code %lx",status_value);
+ }
+ break;
+ case GSS_C_MECH_CODE: /* minor status - drop into default */
+ default:
+ return GSS_S_BAD_STATUS; /* bad status type */
+ }
+ /* return status string */
+ status_string->length = strlen (status_string->value = cpystr (s));
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI delete security context
+ * Accepts: pointer to return minor status
+ * context to delete
+ * output context token
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ OM_uint32 major_status;
+ *minor_status = 0; /* never any minor status */
+ /* output token not supported */
+ major_status = output_token ? GSS_S_FAILURE :
+ DeleteSecurityContext (*context_handle);
+ fs_give ((void **) context_handle);
+ return major_status;
+}
+
+
+/* GSSAPI release buffer
+ * Accepts: pointer to return minor status
+ * buffer to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (&buffer->value);
+ return GSS_S_COMPLETE;
+}
+
+
+/* GSSAPI release name
+ * Accepts: pointer to return minor status
+ * pointer to name to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (input_name);
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI wrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * requested confidentiality
+ * requested quality of protection
+ * input message buffer
+ * pointer to return confidentiality state
+ * output message buffer
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[3];
+ SecBufferDesc bufs;
+ SecPkgContext_Sizes sizes;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = conf_req_flag; /* same as requested */
+ if ((major_status = /* get trailer and padding sizes */
+ QueryContextAttributes (context_handle,SECPKG_ATTR_SIZES,&sizes)) ==
+ SEC_E_OK) {
+ /* create big enough output buffer */
+ output_message_buffer->value =
+ fs_get (sizes.cbSecurityTrailer + input_message_buffer->length +
+ sizes.cbBlockSize);
+ /* MSDN claims that for EncryptMessage() in Kerberos, you need an
+ * uninitialized SECBUFFER_STREAM_HEADER; a SECBUFFER_DATA that "contains
+ * the message to be encrypted. The message is encrypted in place,
+ * overwriting the original contents of its buffer"; an uninitialized
+ * SECBUFFER_STREAM_TRAILER, and an uninitialized SECBUFFER_EMPTY. I've
+ * never been able to get it to work that way.
+ */
+ bufs.cBuffers = 3; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ buf[0].BufferType = SECBUFFER_TOKEN;
+ buf[0].pvBuffer = output_message_buffer->value;
+ buf[0].cbBuffer = sizes.cbSecurityTrailer;
+ /* I/O buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = ((char *) buf[0].pvBuffer) + buf[0].cbBuffer;
+ buf[1].cbBuffer = input_message_buffer->length;
+ memcpy (buf[1].pvBuffer,input_message_buffer->value,buf[1].cbBuffer);
+ buf[2].BufferType = SECBUFFER_PADDING;
+ buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer;
+ buf[2].cbBuffer = sizes.cbBlockSize;
+ if ((major_status = EncryptMessage (context_handle,qop_req,&bufs,0)) ==
+ GSS_S_COMPLETE) {
+ /* slide data as necessary (how annoying!) */
+ unsigned long i = sizes.cbSecurityTrailer - buf[0].cbBuffer;
+ if (i) buf[1].pvBuffer =
+ memmove (((char *) buf[0].pvBuffer) + buf[0].cbBuffer,
+ buf[1].pvBuffer,buf[1].cbBuffer);
+ if (i += (input_message_buffer->length - buf[1].cbBuffer))
+ buf[1].pvBuffer = memmove (((char *)buf[1].pvBuffer) + buf[1].cbBuffer,
+ buf[2].pvBuffer,buf[2].cbBuffer);
+ output_message_buffer->length = buf[0].cbBuffer + buf[1].cbBuffer +
+ buf[2].cbBuffer;
+ }
+ else fs_give (&output_message_buffer->value);
+ }
+ return major_status; /* return status */
+}
+
+/* GSSAPI unwrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * input message buffer
+ * output message buffer
+ * pointer to return confidentiality state
+ * pointer to return quality of protection
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[2];
+ SecBufferDesc bufs;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = NIL; /* or confidentiality state */
+ /* MSDN implies that all that is needed for DecryptMessage() in Kerberos
+ * is a single SECBUFFER_DATA which "contains the encrypted message. The
+ * encrypted message is decrypted in place, overwriting the original
+ * contents of its buffer." I've never been able to get it to work without
+ * using a SECBUFFER_STREAM for input and an uninitialized SECBUFFER_DATA
+ * for output.
+ * It *does* overwrite the input buffer, but not at the same point; e.g.
+ * with an input pointer of 0xa140a8 and size of 53, the output ends up
+ * at 0xa140d5 and size of 4.
+ */
+ bufs.cBuffers = 2; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ /* input buffer */
+ buf[0].BufferType = SECBUFFER_STREAM;
+ buf[0].pvBuffer = input_message_buffer->value;
+ buf[0].cbBuffer = input_message_buffer->length;
+ /* output buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = NIL;
+ buf[1].cbBuffer = 0;
+ /* decrypt and copy to output buffer */
+ if ((major_status = DecryptMessage (context_handle,&bufs,0,qop_state)) ==
+ SEC_E_OK)
+ memcpy (output_message_buffer->value = fs_get (buf[1].cbBuffer),
+ buf[1].pvBuffer,output_message_buffer->length = buf[1].cbBuffer);
+ return major_status; /* return status */
+}
+
+/* From here on are server-only functions, currently unused */
+
+
+/* GSSAPI acquire credentials
+ * Accepts: pointer to return minor status
+ * desired principal
+ * desired lifetime
+ * desired mechanisms
+ * credentials usage
+ * pointer to return credentials handle
+ * pointer to return mechanisms
+ * pointer to return lifetime
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI release credentials
+ * Accepts: pointer to return minor status
+ * credentials handle to free
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* GSSAPI Accept security context
+ * Accepts: pointer to return minor status
+ * context
+ * acceptor credentials
+ * input token buffer
+ * input channel bindings
+ * pointer to return source name
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * pointer to return delegated credentials
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI return printable name
+ * Accepts: pointer to return minor status
+ * internal name
+ * buffer to return output name
+ * output name type
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/mailfile.h b/imap/src/osdep/nt/mailfile.h
new file mode 100644
index 00000000..056e66b7
--- /dev/null
+++ b/imap/src/osdep/nt/mailfile.h
@@ -0,0 +1,29 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Spool file name
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 February 1996
+ * Last Edited: 30 August 2006
+ */
+
+#define MAILFILE "C:\\WINSMTP\\%s.MBX"
diff --git a/imap/src/osdep/nt/makefile.nt b/imap/src/osdep/nt/makefile.nt
new file mode 100644
index 00000000..0ea96e5a
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.nt
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- NT version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_nt.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_nt.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_md5.c auth_pla.c auth_log.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.ntk b/imap/src/osdep/nt/makefile.ntk
new file mode 100644
index 00000000..e383e0f8
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.ntk
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- NT version + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 /I\k5\include
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_ntk.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_ntk.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_mit.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.w2k b/imap/src/osdep/nt/makefile.w2k
new file mode 100644
index 00000000..a3d62ad6
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.w2k
@@ -0,0 +1,119 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- Windows 2000/XP version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+IP=6
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip$(IP)_nt.c
+ copy ip$(IP)_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_w2k.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_w2k.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_w2k.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_w2k.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/mbxnt.c b/imap/src/osdep/nt/mbxnt.c
new file mode 100644
index 00000000..bb3d4cc0
--- /dev/null
+++ b/imap/src/osdep/nt/mbxnt.c
@@ -0,0 +1,1694 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 28 September 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * buffer to write file name
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ struct utimbuf times;
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ ((fd = open (file,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* assume bogus format */
+ if (((((j = read (fd,hdr,HDRSIZE)) == HDRSIZE) && (hdr[0] == '*')) ||
+ /* locked, set byte 0 to "*", read rest */
+ ((j < 0) && (lseek (fd,1,L_SET) == 1) &&
+ (read (fd,hdr+1,HDRSIZE-1) == (HDRSIZE-1)) && (hdr[0] = '*'))) &&
+ (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') &&
+ (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') &&
+ isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) &&
+ isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) &&
+ isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (c = hdr[15]) &&
+ isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) &&
+ isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making a mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockname (lock,file,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr+1,HDRSIZE-1) != (HDRSIZE-1))) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) && (j = strtol (s+1,&s,10)) &&
+ (*s == ';') && (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) && (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create (stream,s)) {
+ /* done if made directory */
+ if ((s = strrchr (s,'\\')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i)
+ sprintf (s += strlen (s),"%s\015\012",
+ (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ "");
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ return ret;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ /* update header */
+ if ((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream);
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ mm_log ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ /* got file? */
+ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : &mbxproto,"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ mm_critical (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ if (ret) times.actime = time (0) - 1;
+ /* else preserve \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ /* preserve mtime */
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ mm_nocritical (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ mm_log (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#define NTKLUDGEOFFSET 7
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
+ HDRSIZE - NTKLUDGEOFFSET) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ /* get parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ mm_critical (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ mm_nocritical (stream); /* release critical */
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ }
+
+ else { /* can't get exclusive */
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/nt/mkautaux.bat b/imap/src/osdep/nt/mkautaux.bat
new file mode 100755
index 00000000..c65022d2
--- /dev/null
+++ b/imap/src/osdep/nt/mkautaux.bat
@@ -0,0 +1,31 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/nt/mkauths.bat b/imap/src/osdep/nt/mkauths.bat
new file mode 100755
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/nt/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/mtxnt.c b/imap/src/osdep/nt/mtxnt.c
new file mode 100644
index 00000000..13f61586
--- /dev/null
+++ b/imap/src/osdep/nt/mtxnt.c
@@ -0,0 +1,1232 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 15 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *file);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* MTX mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/nl_nt.c b/imap/src/osdep/nt/nl_nt.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/nt/nl_nt.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/nt/os_nt.c b/imap/src/osdep/nt/os_nt.c
new file mode 100644
index 00000000..0f6fea89
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_nt.h b/imap/src/osdep/nt/os_nt.h
new file mode 100644
index 00000000..650bb782
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.h
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 27 April 2007
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys\types.h>
+#include <time.h>
+#include <io.h>
+#include <conio.h>
+#include <process.h>
+#undef ERROR /* quell conflicting defintion warning */
+#include <windows.h>
+#include <lm.h>
+#undef ERROR
+#define ERROR (long) 2 /* must match mail.h */
+
+#if _MSC_VER >= 1400
+#define strtok_r strtok_s /* for some reason they called it this */
+#else
+/* strtok() is actually MT-safe in MSVC. Why is it that Microsoft can do
+ * their CRT right, but GNU, Sun, etc. can't?
+ */
+#define strtok_r(a,b,c) strtok(*(c) = a,b)
+#endif
+
+#include "env_nt.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "yunchan.h"
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/nt/os_ntk.c b/imap/src/osdep/nt/os_ntk.c
new file mode 100644
index 00000000..87c69123
--- /dev/null
+++ b/imap/src/osdep/nt/os_ntk.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version + Kerberos
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_mit.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_old.c b/imap/src/osdep/nt/os_old.c
new file mode 100644
index 00000000..5cd6c0c2
--- /dev/null
+++ b/imap/src/osdep/nt/os_old.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 21 December 2007
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_old.c"
diff --git a/imap/src/osdep/nt/os_w2k.c b/imap/src/osdep/nt/os_w2k.c
new file mode 100644
index 00000000..c7af4faa
--- /dev/null
+++ b/imap/src/osdep/nt/os_w2k.c
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows 2000 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_w2k.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_w2k.c"
diff --git a/imap/src/osdep/nt/pmatch.c b/imap/src/osdep/nt/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/nt/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/pseudo.c b/imap/src/osdep/nt/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/nt/pseudo.h b/imap/src/osdep/nt/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/nt/setproto.bat b/imap/src/osdep/nt/setproto.bat
new file mode 100755
index 00000000..ce7cb1ef
--- /dev/null
+++ b/imap/src/osdep/nt/setproto.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Set default prototype for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 9 October 1995
+REM Last Edited: 30 August 2006
+
+REM Set the default drivers
+ECHO #define CREATEPROTO %1proto >> LINKAGE.H
+ECHO #define APPENDPROTO %2proto >> LINKAGE.H
diff --git a/imap/src/osdep/nt/ssl_none.c b/imap/src/osdep/nt/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/nt/ssl_nt.c b/imap/src/osdep/nt/ssl_nt.c
new file mode 100644
index 00000000..c7efa486
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_nt.c
@@ -0,0 +1,721 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+
+/* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
+
+typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
+typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
+ HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
+ PCCERT_CHAIN_CONTEXT *);
+typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
+ PCERT_CHAIN_POLICY_PARA,
+ PCERT_CHAIN_POLICY_STATUS);
+typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
+typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
+
+static CNTS certNameToStr = NIL;
+static CGCC certGetCertificateChain = NIL;
+static CVCCP certVerifyCertificateChainPolicy = NIL;
+static CFCC certFreeCertificateChain = NIL;
+static CFCCX certFreeCertificateContext = NIL;
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ if ((lib = LoadLibrary ("crypt32.dll")) &&
+ (certGetCertificateChain = (CGCC)
+ GetProcAddress (lib,"CertGetCertificateChain")) &&
+ (certVerifyCertificateChainPolicy = (CVCCP)
+ GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
+ (certFreeCertificateChain = (CFCC)
+ GetProcAddress (lib,"CertFreeCertificateChain")) &&
+ (certFreeCertificateContext = (CFCCX)
+ GetProcAddress (lib,"CertFreeCertificateContext")))
+ certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!(*certGetCertificateChain)
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!(*certVerifyCertificateChainPolicy)
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ (*certFreeCertificateChain) (chain);
+ }
+ (*certFreeCertificateContext) (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_old.c b/imap/src/osdep/nt/ssl_old.c
new file mode 100644
index 00000000..121bd02e
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_old.c
@@ -0,0 +1,625 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#if(_WIN32_WINNT < 0x0400)
+typedef unsigned int ALG_ID;
+#else
+#include <wincrypt.h>
+ALGIDDEF
+#endif
+#include <schnlsp.h>
+#include <issperr.h>
+
+ /* in case a binary runs on Windows 2000 */
+#ifndef ISC_REQ_MANUAL_CRED_VALIDATION
+#define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000
+#endif
+#ifndef SEC_E_UNTRUSTED_ROOT
+#define SEC_E_UNTRUSTED_ROOT ((HRESULT) 0x80090325L)
+#endif
+#ifndef SEC_E_CERT_EXPIRED
+#define SEC_E_CERT_EXPIRED ((HRESULT) 0x80090328L)
+#endif
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ char tmp[MAILTMPLEN];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR +
+ ((flags & NET_NOVALIDATECERT) ? ISC_REQ_MANUAL_CRED_VALIDATION :
+ ISC_REQ_MUTUAL_AUTH);
+ SCHANNEL_CRED tlscred;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))
+ break; /* error getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ return "*Certificate has expired";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SChannel error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s = stream->ibuf;
+ size_t n = 0;
+ while (size) { /* until satisfied request */
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_w2k.c b/imap/src/osdep/nt/ssl_w2k.c
new file mode 100644
index 00000000..f5d8d1f5
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_w2k.c
@@ -0,0 +1,683 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 2000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (!EnumerateSecurityPackages (&np,&pp)) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (!(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ CertNameToStr (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!CertGetCertificateChain
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!CertVerifyCertificateChainPolicy
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ CertFreeCertificateChain (chain);
+ }
+ CertFreeCertificateContext (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ } while ((status = DecryptMessage
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((EncryptMessage
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ DeleteSecurityContext (&stream->context);
+ FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/tcp_nt.c b/imap/src/osdep/nt/tcp_nt.c
new file mode 100644
index 00000000..a6d735dc
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.c
@@ -0,0 +1,916 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2007
+ */
+
+#include "ip_nt.c"
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr *sadr,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+static char *myClientAddr = NIL;/* client host address */
+static char *myClientHost = NIL;/* client host name */
+static long myClientPort = -1; /* client port */
+static char *myServerAddr = NIL;/* server host address */
+static char *myServerHost = NIL;/* server host name */
+static long myServerPort = -1; /* server port */
+
+extern long maxposint; /* get this from write.c */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value ? (long) value : (long) WSA_INFINITE;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i,family;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s,*hostname,tmp[MAILTMPLEN];
+ void *adr,*next;
+ size_t adrlen;
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sv->s_port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp,
+ hostname = host);
+ (*bn) (BLOCK_NONE,NIL);
+ fs_give ((void **) &adr);
+ }
+ else sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (s) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ do {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port,
+ tmp,hostname)) == INVALID_SOCKET) &&
+ (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
+ !silent) mm_log (tmp,WARN);
+ (*bn) (BLOCK_NONE,NIL);
+ } while ((sock == INVALID_SOCKET) && s);
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* do possible cleanup action */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock);
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: protocol family
+ * address to connect to
+ * address length
+ * port
+ * scratch buffer
+ * host name
+ * Returns: socket if success, else SOCKET_ERROR with error string in scratch
+ */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst)
+{
+ int sock,err;
+ char *s,errmsg[100];
+ size_t len;
+ DWORD eo;
+ WSAEVENT event;
+ WSANETWORKEVENTS events;
+ unsigned long cmd = 0;
+ struct protoent *pt = getprotobyname ("tcp");
+ struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
+ sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) ==
+ INVALID_SOCKET)
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ());
+ else {
+ /* On Windows, FD_SETSIZE is the number of descriptors which can be
+ * held in an fd_set, as opposed to the maximum descriptor value on UNIX.
+ * Similarly, an fd_set in Windows is a vector of descriptor values, as
+ * opposed to a bitmask of set fds on UNIX. Thus, an fd_set can hold up
+ * to FD_SETSIZE values which can be larger than FD_SETSIZE, and the test
+ * that is used on UNIX is unnecessary here.
+ */
+ wsa_sock_open++; /* count this socket as open */
+ /* set socket nonblocking */
+ if (ttmo_open) WSAEventSelect (sock,event = WSACreateEvent (),FD_CONNECT);
+ else event = 0; /* no event */
+ /* open connection */
+ err = (connect (sock,sadr,len) == SOCKET_ERROR) ? WSAGetLastError () : NIL;
+ /* if timer in effect, wait for event */
+ if (event) while (err == WSAEWOULDBLOCK)
+ switch (eo = WSAWaitForMultipleEvents (1,&event,T,ttmo_open*1000,NIL)) {
+ case WSA_WAIT_EVENT_0: /* got an event? */
+ err = (WSAEnumNetworkEvents (sock,event,&events) == SOCKET_ERROR) ?
+ WSAGetLastError () : events.iErrorCode[FD_CONNECT_BIT];
+ break;
+ case WSA_WAIT_IO_COMPLETION:
+ break; /* fAlertable is NIL so shouldn't happen */
+ default: /* all other conditions */
+ err = eo; /* error from WSAWaitForMultipleEvents() */
+ break;
+ }
+
+ switch (err) { /* analyze result from connect and wait */
+ case 0: /* got a connection */
+ s = NIL;
+ if (event) { /* unset blocking mode */
+ WSAEventSelect (sock,event,NIL);
+ if (ioctlsocket (sock,FIONBIO,&cmd) == SOCKET_ERROR)
+ sprintf (s = errmsg,"Can't set blocking mode (%d)",
+ WSAGetLastError ());
+ }
+ break;
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSA_WAIT_TIMEOUT: case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default: /* horrible error 69 */
+ sprintf (s = errmsg,"Unknown error (%d)",err);
+ break;
+ }
+ /* flush event */
+ if (event) WSACloseEvent (event);
+ if (s) { /* got an error? */
+ sprintf (tmp,"Can't connect to %.80s,%ld: %.80s",hst,port,s);
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ }
+ fs_give ((void **) &sadr); /* and socket address */
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
+ (errno == EINTR));
+ else { /* socket case */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,s,
+ (int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl),
+ stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
+ else s = "TCP buffer read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return LONGT;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
+ else s = "TCP data read end of file";
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds,efds;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = write (stream->tcpso,string,min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpso,&fds);
+ FD_SET(stream->tcpso,&efds);
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* OK to send data? */
+ if (i > 0) while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"TCP write I/O error %d",errno);
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ string += i; /* how much we sent */
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) ? cpystr (stream->host) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientHost = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientHost = tcp_name (sadr,T);
+ if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientHost;
+}
+
+
+/* TCP/IP get client port number (server calls only)
+ * Returns: client port number
+ */
+
+long tcp_clientport ()
+{
+ if (!myClientHost && !myClientAddr) tcp_clientaddr ();
+ return myClientPort;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's name */
+ myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) { /* once-only */
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else { /* get stdin's name */
+ myServerHost = tcp_name (sadr,NIL);
+ if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost && !myServerAddr) tcp_serveraddr ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* get canonical name */
+ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr *sadr,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
+ if (allowreversedns) {
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ /* translate address to name */
+ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/tcp_nt.h b/imap/src/osdep/nt/tcp_nt.h
new file mode 100644
index 00000000..284cb226
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 16384 /* 32768 causes stdin read() to barf */
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef ERROR /* quell conflicting definition diagnostic */
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ SOCKET tcpsi; /* tcp socket */
+ SOCKET tcpso; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/nt/tenexnt.c b/imap/src/osdep/nt/tenexnt.c
new file mode 100644
index 00000000..4805b7c5
--- /dev/null
+++ b/imap/src/osdep/nt/tenexnt.c
@@ -0,0 +1,1310 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 18 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *file);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ tenex_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_flags, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &tenexproto;
+ if (stream->local) fatal ("tenex recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = unix_crlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ mm_flags (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL;
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* Tenex mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive access */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &tenexproto;
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/unixnt.c b/imap/src/osdep/nt/unixnt.c
new file mode 100644
index 00000000..2e92d39a
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.c
@@ -0,0 +1,2297 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "unixnt.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,char *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
+int unix_parse (MAILSTREAM *stream,char *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ int c,r;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
+ else { /* ignore leading whitespace */
+ for (s = tmp,c = '\n';
+ (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
+ c = *s++);
+ if (c == '\n') { /* at start of a line? */
+ VALID (s,t,r,c); /* yes, validate format */
+ if (r) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ }
+ }
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ /* yes, preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,NIL)) {
+ if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
+ if (s = strpbrk (tmp,"\r\n")) *s = '\0';
+ strcat (tmp,"\r\nDate: ");
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti,pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) {
+ close (fd); /* close file */
+ ret = T;
+ }
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ close (fd); /* close file before unlinking */
+ unlink (mbx); /* delete the file */
+ }
+ }
+ }
+ return ret;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ mm_critical (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!(s = dummy_file (tmp,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ else if ((ld = lockname (lock,file,NIL)) < 0)
+ sprintf (tmp,"Can't get lock for mailbox %.80s",old);
+
+ else { /* lock out other c-clients */
+ if (flock (ld,LOCK_EX|LOCK_NB)) {
+ close (ld); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ }
+ /* lock out non c-client applications */
+ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
+ LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,newname)) {
+ flock (ld,LOCK_UN);
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ mm_nocritical (stream);
+ return NIL; /* couldn't create superior */
+ }
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file)) /* want delete */
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ flock (ld,LOCK_UN); /* release c-client lock */
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ }
+ }
+ mm_nocritical (stream); /* no longer critical */
+ if (!ret) mm_log (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ int fd;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &unixproto;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+ if (!stream->rdonly) { /* make lock for read/write access */
+ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
+ mm_log ("Can't open mailbox lock, access is readonly",WARN);
+ /* can get the lock? */
+ else if (flock (fd,LOCK_EX|LOCK_NB)) {
+ if (!stream->silent)
+ mm_log ("Mailbox is open by another process, access is readonly",WARN);
+ close (fd);
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,tmp,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,tmp);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ /* have permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ mm_flags (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ return LOCAL->buf;
+ }
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ *s++ = c; /* copy it and any succeeding LF */
+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* and stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
+ if (!stream->silent) mm_log ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ char lock[MAILTMPLEN];
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ if (msg && !stream->silent) mm_log (msg,NIL);
+ }
+ else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ mm_critical (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
+ LOCAL->buf[j - 1] = '\r';
+ LOCAL->buf[j++] = '\n';
+ }
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,NIL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 4] == '\r')) j -= 2;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,NIL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ times.modtime = time (0); /* set mtime to now */
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = times.modtime - 1;
+
+ else times.actime = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ utime (file,&times); /* set the times */
+ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) mm_log (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
+ lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+ mm_critical (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ times.modtime = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ times.actime = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ ret = NIL; /* return error */
+ }
+ /* set atime to now-1 if successful copy */
+ else times.actime = times.modtime - 1;
+ utime (file,&times); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
+ if (lock && *lock) unlink (lock);
+ fclose (df); /* close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata */
+ if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
+ for (s = date; *s; *s++) switch (*s) {
+ default:
+ if (putc (*s,sf) == EOF) return NIL;
+ case '\r': case '\n':
+ break;
+ }
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing CRLF and return */
+ return (fputs ("\r\n",sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\r\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ if (!j) continue; /* do nothing if line emptied */
+ /* complete line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,char *lock,int op)
+{
+ int fd,ld,j;
+ int i = LOCKTIMEOUT * 60 - 1;
+ char tmp[MAILTMPLEN];
+ time_t t;
+ struct stat sb;
+ sprintf (lock,"%s.lock",file);/* build lock filename */
+ do { /* until OK or out of tries */
+ t = time (0); /* get the time now */
+ /* try to get the lock */
+ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
+ close (ld); /* got it, close the lock file! */
+ else if (errno != EEXIST) { /* miscellaneous error */
+ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
+ if (!(i%15)) mm_log (tmp,WARN);
+ }
+ /* lock exists, still active? */
+ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
+ ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
+ close (ld); /* got timed-out lock file */
+ else { /* active lock, try again */
+ if (!(i%15)) {
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait a second before next retry */
+ }
+ } while (*lock && ld < 0 && i--);
+ /* open file */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ struct utimbuf times;
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ times.actime = (times.modtime = (sbuf.st_mtime < now) ?
+ sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,&times))
+ LOCAL->filetime = times.modtime;
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ /* flush the lock file if any */
+ if (lock && *lock) unlink (lock);
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,char *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ mm_critical (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,
+ O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
+ NIL,lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ mm_log (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ mm_log (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ mm_log (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ mm_log (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in CRLF format newline */
+ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
+ /* header size */
+ elt->rfc822_size = elt->private.spare.data += k;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ mm_log (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data -= 2;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ if (s[i - 1] == '\n')
+ elt->rfc822_size +=
+ k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
+ else { /* file does not end with newline! */
+ elt->rfc822_size += i;
+ k = m = 0;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (k == 2) { /* last line was blank? */
+ elt->private.msg.text.text.size -= (m ? 1 : 2);
+ elt->rfc822_size -= 2;
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = (unsigned long) time (0);
+ if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ mm_log ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ mm_log ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,*t,tmp[MAILTMPLEN];
+ time_t now = time(0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (t += strlen (t)," %s",stream->user_flags[i]);
+ strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
+ for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
+ if (*s == '\n') *t++ = '\r';
+ *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
+ *t = '\0'; /* tie off pseudo header */
+ return t - hdr; /* return length of pseudo header */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ unsigned long pad = 50;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
+ *s++ = '\r'; *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\r'; *s++ = '\n';
+
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\r'; *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\r'; *s++ = '\n';
+ }
+ /* end of extended message status */
+ *s++ = '\r'; *s++ = '\n'; *s = '\0';
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ struct utimbuf times;
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 2;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ if (!size) { /* no messages and no pseudo, make one now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ LOCAL->pseudo = T;
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty ||
+ (((unsigned long) f.curpos) != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,NIL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
+ if (j != elt->private.spare.data) fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* can't happen it says here */
+ if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\r\n",2);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ times.modtime = (times.actime = time (0)) -1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
+ /* flush the lock file */
+ unix_unlock (LOCAL->fd,stream,lock);
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
+ size - ((unsigned long) LOCAL->filesize) : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (mm_diskerror (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ mm_log (tmp,ERROR);
+ mm_diskerror (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/nt/unixnt.h b/imap/src/osdep/nt/unixnt.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/nt/write.c b/imap/src/osdep/nt/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/nt/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/nt/yunchan.c b/imap/src/osdep/nt/yunchan.c
new file mode 100644
index 00000000..8c1f6905
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.c
@@ -0,0 +1,286 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Emulator for BSD flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure
+ */
+
+/* Our friends in Redmond have decided that you can not write to any segment
+ * which has a shared lock. This screws up the shared-write mailbox drivers
+ * (mbx, mtx, and tenex). As a workaround, we'll only lock the first byte of
+ * the file, meaning that you can't write that byte shared.
+ * This behavior seems to be new as of NT 4.0.
+ */
+
+int flock (int fd,int op)
+{
+ HANDLE hdl = (HANDLE) _get_osfhandle (fd);
+ DWORD flags = (op & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ OVERLAPPED offset = {NIL,NIL,0,0,NIL};
+ int ret = -1;
+ blocknotify_t bn = (blocknotify_t)
+ ((op & LOCK_NB) ? NIL : mail_parameters (NIL,GET_BLOCKNOTIFY,NIL));
+ if (hdl < 0) errno = EBADF; /* error in file descriptor */
+ else switch (op & ~LOCK_NB) { /* translate to LockFileEx() op */
+ case LOCK_EX: /* exclusive */
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ case LOCK_SH: /* shared */
+ if (!check_nt ()) return 0; /* always succeeds if not NT */
+ if (bn) (*bn) (BLOCK_FILELOCK,NIL);
+ /* bug for bug compatible with Unix */
+ UnlockFileEx (hdl,NIL,1,0,&offset);
+ /* lock the file as requested */
+ if (LockFileEx (hdl,flags,NIL,1,0,&offset)) ret = 0;
+ if (bn) (*bn) (BLOCK_NONE,NIL);
+ /* if failed */
+ if (ret) errno = (op & LOCK_NB) ? EAGAIN : EBADF;
+ break;
+ case LOCK_UN: /* unlock */
+ if (check_nt ()) UnlockFileEx (hdl,NIL,1,0,&offset);
+ ret = 0; /* always succeeds */
+ default: /* default */
+ errno = EINVAL; /* bad call */
+ break;
+ }
+ return ret;
+}
+
+/* Local storage */
+
+static char *loghdr; /* log file header string */
+static HANDLE loghdl = NIL; /* handle of event source */
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+ va_list args;
+ LPTSTR strs[2];
+ char tmp[MAILTMPLEN]; /* callers must be careful not to pop this */
+ unsigned short etype;
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ /* default event source */
+ if (!loghdl) openlog ("c-client",LOG_PID,LOG_MAIL);
+ switch (priority) { /* translate UNIX type into NT type */
+ case LOG_ALERT:
+ etype = EVENTLOG_ERROR_TYPE;
+ break;
+ case LOG_INFO:
+ etype = EVENTLOG_INFORMATION_TYPE;
+ break;
+ default:
+ etype = EVENTLOG_WARNING_TYPE;
+ }
+ va_start (args,message); /* initialize vararg mechanism */
+ vsprintf (tmp,message,args); /* build message */
+ strs[0] = loghdr; /* write header */
+ strs[1] = tmp; /* then the message */
+ /* report the event */
+ ReportEvent (loghdl,etype,(unsigned short) priority,2000,NIL,2,0,strs,NIL);
+ va_end (args);
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+ char tmp[MAILTMPLEN];
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ if (loghdl) fatal ("Duplicate openlog()!");
+ loghdl = RegisterEventSource (NIL,ident);
+ sprintf (tmp,(logopt & LOG_PID) ? "%s[%d]" : "%s",ident,getpid ());
+ loghdr = cpystr (tmp); /* save header for later */
+}
+
+/* Copy Unix string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl)
+{
+ unsigned long i,j;
+ char *d = src;
+ /* count number of LF's in source string(s) */
+ for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
+ /* flush destination buffer if too small */
+ if (*dst && (i > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((*dstl = i) + 1);
+ if (dstl) *dstl = i; /* return new buffer length to main program */
+ }
+ d = *dst; /* destination string */
+ /* copy strings, inserting CR's before LF's */
+ while (srcl--) switch (*src) {
+ case '\015': /* unlikely carriage return */
+ *d++ = *src++; /* copy it and any succeeding linefeed */
+ if (srcl && *src == '\012') {
+ *d++ = *src++;
+ srcl--;
+ }
+ break;
+ case '\012': /* line feed? */
+ *d++ ='\015'; /* yes, prepend a CR, drop into default case */
+ default: /* ordinary chararacter */
+ *d++ = *src++; /* just copy character */
+ break;
+ }
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of Unix string after unix_crlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long unix_crlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
+
+/* Undoubtably, I'm going to regret these two routines in the future. I
+ * regret them now. Their purpose is to work around two problems in the
+ * VC++ 6.0 C library:
+ * (1) tmpfile() creates the file in the current directory instead of a
+ * temporary directory
+ * (2) tmpfile() and fclose() think that on NT systems, it works to unlink
+ * the file while it's still open, so there's no need for the _tmpfname
+ * hook at fclose(). Unfortunately, that doesn't work in Win2K.
+ * I would be delighted to have a better alternative.
+ */
+
+#undef fclose /* use the real fclose() in close_file() */
+
+/* Substitute for Microsoft's tmpfile() that uses the real temporary directory
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+FILE *create_tempfile (void)
+{
+ FILE *ret = NIL;
+ char *s = _tempnam (getenv ("TEMP"),"msg");
+ if (s) { /* if got temporary name... */
+ /* open file, and stash name on _tmpfname */
+ if (ret = fopen (s,"w+b")) ret->_tmpfname = s;
+ else fs_give ((void **) &s);/* flush temporary string */
+ }
+ return ret;
+}
+
+
+/* Substitute for Microsoft's fclose() that always flushes _tmpfname
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+int close_file (FILE *stream)
+{
+ int ret;
+ char *s = stream->_tmpfname;
+ stream->_tmpfname = NIL; /* just in case fclose() tries to delete it */
+ ret = fclose (stream); /* close the file */
+ if (s) { /* was there a _tmpfname? */
+ unlink (s); /* yup, delete it */
+ fs_give ((void **) &s); /* and flush the name */
+ }
+ return ret;
+}
+
+/* Get password from console
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ static char pwd[PWDLEN];
+ int ch,i,done;
+ fputs (prompt,stderr); /* output prompt */
+ for (i = done = 0; !done; ) switch (ch = _getch()) {
+ case 0x03: /* CTRL/C stops program */
+ _exit (1);
+ case '\b': /* BACKSPACE erase previous character */
+ if (i) pwd[--i] = '\0';
+ break;
+ case '\n': case '\r': /* CR or LF terminates string */
+ done = 1;
+ break;
+ default: /* any other character is a pwd char */
+ if (i < (PWDLEN - 1)) pwd[i++] = ch;
+ break;
+ }
+ pwd[i] = '\0'; /* tie off string with null */
+ putchar ('\n'); /* echo newline */
+ return pwd;
+}
diff --git a/imap/src/osdep/nt/yunchan.h b/imap/src/osdep/nt/yunchan.h
new file mode 100644
index 00000000..25d60568
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.h
@@ -0,0 +1,86 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* For flock() emulation */
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+#define tmpfile create_tempfile
+#define fclose close_file
+#define fsync _commit
+#define ftruncate chsize
+#define gethostid clock
+#define sleep(x) Sleep (1000 * x)
+
+
+long alarm (long seconds);
+int flock (int fd,int op);
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl);
+unsigned long unix_crlflen (STRING *s);
+FILE *create_tempfile (void);
+int close_file (FILE *stream);
+char *getpass (const char *prompt);
diff --git a/imap/src/osdep/os2/auths.cmd b/imap/src/osdep/os2/auths.cmd
new file mode 100644
index 00000000..639f0807
--- /dev/null
+++ b/imap/src/osdep/os2/auths.cmd
@@ -0,0 +1,48 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Authenticator Linkage Generator for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+/* Erase old authenticators list */
+'if exist auths.c del auths.c'
+parse arg args
+n=words(args)
+a_file='auths.c'
+c_file='linkage.c'
+h_file='linkage.h'
+call stream a_file, 'C', 'open write'
+call stream c_file, 'C', 'open write'
+call stream h_file, 'C', 'open write'
+do i=1 to n
+ arg=word(args,i)
+ call lineout a_file, '#include "auth_'arg'.c"'
+ call lineout h_file, 'extern AUTHENTICATOR auth_'arg';'
+ call lineout c_file, ' auth_link (&auth_'arg'); /* link in the 'arg' authenticator */'
+ end
+call stream h_file, 'C', 'close'
+call stream c_file, 'C', 'close'
+call stream a_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/drivers.cmd b/imap/src/osdep/os2/drivers.cmd
new file mode 100644
index 00000000..8508834b
--- /dev/null
+++ b/imap/src/osdep/os2/drivers.cmd
@@ -0,0 +1,45 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Authenticator Linkage Generator for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+/* Erase old authenticators list */
+'if exist linkage.h del linkage.h'
+'if exist linkage.c del linkage.c'
+parse arg args
+n=words(args)
+c_file='linkage.c'
+h_file='linkage.h'
+call stream c_file, 'C', 'open write'
+call stream h_file, 'C', 'open write'
+do i=1 to n
+ arg=word(args,i)
+ call lineout h_file, 'extern DRIVER 'arg'driver;'
+ call lineout c_file, ' mail_link (&'arg'driver); /* link in the 'arg' driver */'
+ end
+call stream h_file, 'C', 'close'
+call stream c_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/dummy.h b/imap/src/osdep/os2/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/os2/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/os2/dummyos2.c b/imap/src/osdep/os2/dummyos2.c
new file mode 100644
index 00000000..f54999fc
--- /dev/null
+++ b/imap/src/osdep/os2/dummyos2.c
@@ -0,0 +1,714 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for OS2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Thanks to Nicholas Sheppard for the original version */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <fcntl.h>
+#include <os2.h>
+#undef ADDRESS
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate INBOX */
+ if (!*s) return &dummydriver;
+ /* remove trailing \ */
+ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0';
+ if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG: /* file */
+ case S_IFDIR: /* future use */
+ return &dummydriver;
+ }
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ unsigned long i = 1;
+ FILEFINDBUF3 f;
+ HDIR hd = HDIR_CREATE;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*");
+ /* do nothing if can't open directory */
+ if (!DosFindFirst (tmp,&hd,FILE_NORMAL,&f,sizeof (f),&i,FIL_STANDARD)) {
+ /* list it if not at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (!dir || dir[strlen (dir) -1] == '\\') do {
+ if (((f.name[0] != '.') ||
+ (f.name[1] && ((f.name[1] != '.') || f.name[2]))) &&
+ (strlen (f.name) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (tmp,pat,'\\') ||
+ pmatch_full (strcat (tmp,"\\"),pat,'\\') ||
+ dmatch (tmp,pat,'\\')) &&
+ mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) {
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break;
+ strcat (tmp,"\\");/* set up for dmatch call */
+ }
+ /* try again with trailing \ */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') &&
+ !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))
+ break;
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ i = 1;
+ } while (!DosFindNext (h,&f,sizeof (f),&i));
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ int fd;
+ long csiz,ssiz,bsiz;
+ char *s,*buf,tmp[MAILTMPLEN];
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !(s = dummy_file (tmp,name)) || stat (s,&sbuf) ||
+ (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox))
+ return dummy_create_path (stream,tmp,NIL);
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ mm_log (tmp,ERROR);
+ }
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ long ret = NIL;
+ /* no trailing \ allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1])) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ mm_log (mbx,ERROR);
+ return NIL;
+ }
+ /* found superior to destination name? */
+ if (s && (s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,mbx)) return NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ mm_log (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/os2/env_os2.c b/imap/src/osdep/os2/env_os2.c
new file mode 100644
index 00000000..3c679f5d
--- /dev/null
+++ b/imap/src/osdep/os2/env_os2.c
@@ -0,0 +1,318 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS/2 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\newsrc",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) {
+ char *s;
+ for (s = tz; *s; s++) if (*s & 0x80) return;
+ sprintf (date + strlen (date)," (%.50s)",tz);
+ }
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) { /* get home directory name if not yet known */
+ char *s;
+ if ((s = getenv ("PINEHOME")) || (s = getenv ("HOME")) ||
+ (s = getenv ("ETC"))) {
+ myHomeDir = cpystr (s);
+ while (s = strchr (myHomeDir,'/')) *s = '\\';
+ if ((s = strrchr (myHomeDir,'\\')) && !s[1]) *s = '\0';
+ }
+ else myHomeDir = cpystr ("");
+ }
+ return myHomeDir;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *s;
+ char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL);
+ /* forbid extraneous extensions */
+ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) &&
+ ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) ||
+ strchr (s+1,'.'))) return NIL;
+ /* absolute path name? */
+ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name);
+ else sprintf (dst,"%s\\%s",myhomedir (),name);
+ if (ext) sprintf (dst + strlen (dst),".%s",ext);
+ return dst;
+}
+
+/* Lock file name
+ * Accepts: return buffer for file name
+ * file name
+ * locking to be placed on file if non-NIL
+ * Returns: file descriptor of lock or -1 if error
+ */
+
+int lockname (char *lock,char *fname,int op)
+{
+ int ld;
+ char c,*s;
+ if (!((s = lockdir (lock,getenv ("TEMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
+ /* C:\TEMP is last resort */
+ (s = lockdir (lock,defaultDrive (),"TEMP")))) {
+ mm_log ("Unable to find temporary directory",ERROR);
+ return -1;
+ }
+ /* generate file name */
+ while (c = *fname++) switch (c) {
+ case '/': case '\\': case ':':
+ *s++ = '!'; /* convert bad chars to ! */
+ break;
+ default:
+ *s++ = c;
+ break;
+ }
+ *s++ = c; /* tie off name */
+ /* get the lock */
+ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
+ flock (ld,op); /* apply locking function */
+ return ld; /* return locking file descriptor */
+}
+
+/* Build lock directory, check to see if it exists
+ * Accepts: return buffer for lock directory
+ * first part of possible name
+ * optional second part
+ * Returns: pointer to end of buffer if buffer has a good name, else NIL
+ */
+
+char *lockdir (char *lock,char *first,char *last)
+{
+ struct stat sbuf;
+ char c,*s;
+ if (first && *first) { /* first part must be non-NIL */
+ /* copy first part */
+ for (s = lock; *first; c = *s++ = *first++);
+ if (last && *last) { /* copy last part if specified */
+ /* write trailing \ in case not in first */
+ if (c != '\\') *s++ = '\\';
+ while (*last) c = *s++ = *last++;
+ }
+ if (c == '\\') --s; /* delete trailing \ if any */
+ *s = '\0'; /* tie off name at this point */
+ return stat (lock,&sbuf) ? NIL : s;
+ }
+ return NIL; /* failed */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM DEFAULTPROTO;
+ return &DEFAULTPROTO; /* return default driver's prototype */
+}
+
+/* Global data */
+
+static unsigned rndm = 0; /* initial `random' number */
+
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/os2/env_os2.h b/imap/src/osdep/os2/env_os2.h
new file mode 100644
index 00000000..de86da44
--- /dev/null
+++ b/imap/src/osdep/os2/env_os2.h
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS/2 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+#include "env.h"
+
+long random (void);
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
+
+void rfc822_fixed_date (char *date);
+int lockname (char *lock,char *fname,int op);
+char *lockdir (char *lock,char *first,char *last);
+void unlockfd (int fd,char *lock);
diff --git a/imap/src/osdep/os2/fs_os2.c b/imap/src/osdep/os2/fs_os2.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/os2/fs_os2.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/os2/ftl_os2.c b/imap/src/osdep/os2/ftl_os2.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/os2/ftl_os2.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/os2/makefile.os2 b/imap/src/osdep/os2/makefile.os2
new file mode 100644
index 00000000..a2211e91
--- /dev/null
+++ b/imap/src/osdep/os2/makefile.os2
@@ -0,0 +1,109 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- OS/2 version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 30 August 2006
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+DEFAULTAUTHENTICATORS = ext md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+DEFAULTDRIVER = mbx
+CFLAGS = -DOMF -DCHUNKSIZE=65536 -O2 -Zomf $(EXTRACFLAGS)
+CC = gcc
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) $(CFLAGS) -o $@ -c $*.c
+
+osdep.h: os_os2.h
+ copy os_os2.h osdep.h
+ drivers.cmd $(EXTRADRIVERS) $(DRIVERS) dummy
+ auths.cmd $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ setproto.cmd $(DEFAULTDRIVER)
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_os2.obj: mail.h osdep.h env_os2.h fs.h ftl.h nl.h tcp.h tcp_os2.h \
+ os_os2.c fs_os2.c ftl_os2.c nl_os2.c env_os2.c tcp_os2.c \
+ mailfile.h auth_md5.c auth_log.c pmatch.c write.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummyos2.obj: mail.h dummy.h misc.h osdep.h dummyos2.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:$(CCLIENTLIB) \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/os2/mbxnt.c b/imap/src/osdep/os2/mbxnt.c
new file mode 100644
index 00000000..bb3d4cc0
--- /dev/null
+++ b/imap/src/osdep/os2/mbxnt.c
@@ -0,0 +1,1694 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 28 September 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * buffer to write file name
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ struct utimbuf times;
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ ((fd = open (file,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* assume bogus format */
+ if (((((j = read (fd,hdr,HDRSIZE)) == HDRSIZE) && (hdr[0] == '*')) ||
+ /* locked, set byte 0 to "*", read rest */
+ ((j < 0) && (lseek (fd,1,L_SET) == 1) &&
+ (read (fd,hdr+1,HDRSIZE-1) == (HDRSIZE-1)) && (hdr[0] = '*'))) &&
+ (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') &&
+ (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') &&
+ isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) &&
+ isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) &&
+ isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (c = hdr[15]) &&
+ isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) &&
+ isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making a mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockname (lock,file,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr+1,HDRSIZE-1) != (HDRSIZE-1))) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) && (j = strtol (s+1,&s,10)) &&
+ (*s == ';') && (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) && (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create (stream,s)) {
+ /* done if made directory */
+ if ((s = strrchr (s,'\\')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i)
+ sprintf (s += strlen (s),"%s\015\012",
+ (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ "");
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ return ret;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ /* update header */
+ if ((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream);
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ mm_log ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ /* got file? */
+ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : &mbxproto,"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ mm_critical (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ if (ret) times.actime = time (0) - 1;
+ /* else preserve \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ /* preserve mtime */
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ mm_nocritical (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ mm_log (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#define NTKLUDGEOFFSET 7
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
+ HDRSIZE - NTKLUDGEOFFSET) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ /* get parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ mm_critical (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ mm_nocritical (stream); /* release critical */
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ }
+
+ else { /* can't get exclusive */
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/os2/mtxnt.c b/imap/src/osdep/os2/mtxnt.c
new file mode 100644
index 00000000..13f61586
--- /dev/null
+++ b/imap/src/osdep/os2/mtxnt.c
@@ -0,0 +1,1232 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 15 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *file);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* MTX mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/os2/nl_os2.c b/imap/src/osdep/os2/nl_os2.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/os2/nl_os2.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/os2/os_os2.c b/imap/src/osdep/os2/os_os2.c
new file mode 100644
index 00000000..9c6f2f5f
--- /dev/null
+++ b/imap/src/osdep/os2/os_os2.c
@@ -0,0 +1,100 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OS/2 emx version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "tcp_os2.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fs_os2.c"
+#include "ftl_os2.c"
+#include "nl_os2.c"
+#include "env_os2.c"
+#include "tcp_os2.c"
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ struct hostent *he;
+ /* could we get local id? */
+ gethostname (tmp,MAILTMPLEN-1);
+ if (he = gethostbyname (lcase (tmp))) {
+ if (he->h_name) s = he->h_name;
+ else sprintf (s = tmp,"[%i.%i.%i.%i]",he->h_addr[0],he->h_addr[1],
+ he->h_addr[2],he->h_addr[3]);
+ }
+ else s = "random-pc";
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
+
+
+/*
+ * Emulator for BSD syslog() routine.
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
diff --git a/imap/src/osdep/os2/os_os2.h b/imap/src/osdep/os2/os_os2.h
new file mode 100644
index 00000000..0dd0a62b
--- /dev/null
+++ b/imap/src/osdep/os2/os_os2.h
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OS/2 emx version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "env_os2.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/os2/pmatch.c b/imap/src/osdep/os2/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/os2/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/os2/pseudo.c b/imap/src/osdep/os2/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/os2/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/os2/pseudo.h b/imap/src/osdep/os2/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/os2/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/os2/setproto.cmd b/imap/src/osdep/os2/setproto.cmd
new file mode 100644
index 00000000..5b9d6eef
--- /dev/null
+++ b/imap/src/osdep/os2/setproto.cmd
@@ -0,0 +1,34 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Set default prototype for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+parse arg default args
+h_file='linkage.h'
+call stream h_file, 'C', 'open write'
+call lineout h_file, '#define DEFAULTPROTO' default'proto'
+call stream h_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/tcp_os2.c b/imap/src/osdep/os2/tcp_os2.c
new file mode 100644
index 00000000..062f260c
--- /dev/null
+++ b/imap/src/osdep/os2/tcp_os2.c
@@ -0,0 +1,434 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MS-DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ struct sockaddr_in sin;
+ int sock;
+ char *s,tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* look up host name */
+ else if (!lookuphost (&host,&sin)) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* copy port number in network format */
+ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",errno);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+#if 0
+ /* needed? */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ errno = ENOBUFS; /* just in case */
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
+ switch (errno) { /* analyze error */
+ case ECONNREFUSED:
+ s = "Refused";
+ break;
+ case ENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case ETIMEDOUT:
+ s = "Timed out";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
+ mm_log (tmp,ERROR);
+ close (sock);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = host; /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the socket */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcps >= 0) close (stream->tcps);
+ stream->tcps = -1;
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* all we can do for now */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/os2/tcp_os2.h b/imap/src/osdep/os2/tcp_os2.h
new file mode 100644
index 00000000..2933a5ff
--- /dev/null
+++ b/imap/src/osdep/os2/tcp_os2.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS2 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long lookuphost (char **host,struct sockaddr_in *sin);
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/os2/tenexnt.c b/imap/src/osdep/os2/tenexnt.c
new file mode 100644
index 00000000..4805b7c5
--- /dev/null
+++ b/imap/src/osdep/os2/tenexnt.c
@@ -0,0 +1,1310 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 18 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *file);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ tenex_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_flags, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &tenexproto;
+ if (stream->local) fatal ("tenex recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = unix_crlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ mm_flags (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL;
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* Tenex mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive access */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &tenexproto;
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/os2/unixnt.c b/imap/src/osdep/os2/unixnt.c
new file mode 100644
index 00000000..2e92d39a
--- /dev/null
+++ b/imap/src/osdep/os2/unixnt.c
@@ -0,0 +1,2297 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "unixnt.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,char *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
+int unix_parse (MAILSTREAM *stream,char *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ int c,r;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
+ else { /* ignore leading whitespace */
+ for (s = tmp,c = '\n';
+ (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
+ c = *s++);
+ if (c == '\n') { /* at start of a line? */
+ VALID (s,t,r,c); /* yes, validate format */
+ if (r) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ }
+ }
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ /* yes, preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,NIL)) {
+ if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
+ if (s = strpbrk (tmp,"\r\n")) *s = '\0';
+ strcat (tmp,"\r\nDate: ");
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti,pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) {
+ close (fd); /* close file */
+ ret = T;
+ }
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ close (fd); /* close file before unlinking */
+ unlink (mbx); /* delete the file */
+ }
+ }
+ }
+ return ret;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ mm_critical (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!(s = dummy_file (tmp,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ else if ((ld = lockname (lock,file,NIL)) < 0)
+ sprintf (tmp,"Can't get lock for mailbox %.80s",old);
+
+ else { /* lock out other c-clients */
+ if (flock (ld,LOCK_EX|LOCK_NB)) {
+ close (ld); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ }
+ /* lock out non c-client applications */
+ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
+ LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,newname)) {
+ flock (ld,LOCK_UN);
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ mm_nocritical (stream);
+ return NIL; /* couldn't create superior */
+ }
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file)) /* want delete */
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ flock (ld,LOCK_UN); /* release c-client lock */
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ }
+ }
+ mm_nocritical (stream); /* no longer critical */
+ if (!ret) mm_log (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ int fd;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &unixproto;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+ if (!stream->rdonly) { /* make lock for read/write access */
+ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
+ mm_log ("Can't open mailbox lock, access is readonly",WARN);
+ /* can get the lock? */
+ else if (flock (fd,LOCK_EX|LOCK_NB)) {
+ if (!stream->silent)
+ mm_log ("Mailbox is open by another process, access is readonly",WARN);
+ close (fd);
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,tmp,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,tmp);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ /* have permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ mm_flags (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ return LOCAL->buf;
+ }
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ *s++ = c; /* copy it and any succeeding LF */
+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* and stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
+ if (!stream->silent) mm_log ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ char lock[MAILTMPLEN];
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ if (msg && !stream->silent) mm_log (msg,NIL);
+ }
+ else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ mm_critical (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
+ LOCAL->buf[j - 1] = '\r';
+ LOCAL->buf[j++] = '\n';
+ }
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,NIL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 4] == '\r')) j -= 2;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,NIL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ times.modtime = time (0); /* set mtime to now */
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = times.modtime - 1;
+
+ else times.actime = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ utime (file,&times); /* set the times */
+ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) mm_log (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
+ lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+ mm_critical (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ times.modtime = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ times.actime = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ ret = NIL; /* return error */
+ }
+ /* set atime to now-1 if successful copy */
+ else times.actime = times.modtime - 1;
+ utime (file,&times); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
+ if (lock && *lock) unlink (lock);
+ fclose (df); /* close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata */
+ if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
+ for (s = date; *s; *s++) switch (*s) {
+ default:
+ if (putc (*s,sf) == EOF) return NIL;
+ case '\r': case '\n':
+ break;
+ }
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing CRLF and return */
+ return (fputs ("\r\n",sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\r\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ if (!j) continue; /* do nothing if line emptied */
+ /* complete line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,char *lock,int op)
+{
+ int fd,ld,j;
+ int i = LOCKTIMEOUT * 60 - 1;
+ char tmp[MAILTMPLEN];
+ time_t t;
+ struct stat sb;
+ sprintf (lock,"%s.lock",file);/* build lock filename */
+ do { /* until OK or out of tries */
+ t = time (0); /* get the time now */
+ /* try to get the lock */
+ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
+ close (ld); /* got it, close the lock file! */
+ else if (errno != EEXIST) { /* miscellaneous error */
+ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
+ if (!(i%15)) mm_log (tmp,WARN);
+ }
+ /* lock exists, still active? */
+ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
+ ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
+ close (ld); /* got timed-out lock file */
+ else { /* active lock, try again */
+ if (!(i%15)) {
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait a second before next retry */
+ }
+ } while (*lock && ld < 0 && i--);
+ /* open file */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ struct utimbuf times;
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ times.actime = (times.modtime = (sbuf.st_mtime < now) ?
+ sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,&times))
+ LOCAL->filetime = times.modtime;
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ /* flush the lock file if any */
+ if (lock && *lock) unlink (lock);
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,char *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ mm_critical (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,
+ O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
+ NIL,lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ mm_log (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ mm_log (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ mm_log (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ mm_log (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in CRLF format newline */
+ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
+ /* header size */
+ elt->rfc822_size = elt->private.spare.data += k;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ mm_log (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data -= 2;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ if (s[i - 1] == '\n')
+ elt->rfc822_size +=
+ k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
+ else { /* file does not end with newline! */
+ elt->rfc822_size += i;
+ k = m = 0;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (k == 2) { /* last line was blank? */
+ elt->private.msg.text.text.size -= (m ? 1 : 2);
+ elt->rfc822_size -= 2;
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = (unsigned long) time (0);
+ if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ mm_log ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ mm_log ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,*t,tmp[MAILTMPLEN];
+ time_t now = time(0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (t += strlen (t)," %s",stream->user_flags[i]);
+ strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
+ for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
+ if (*s == '\n') *t++ = '\r';
+ *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
+ *t = '\0'; /* tie off pseudo header */
+ return t - hdr; /* return length of pseudo header */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ unsigned long pad = 50;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
+ *s++ = '\r'; *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\r'; *s++ = '\n';
+
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\r'; *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\r'; *s++ = '\n';
+ }
+ /* end of extended message status */
+ *s++ = '\r'; *s++ = '\n'; *s = '\0';
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ struct utimbuf times;
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 2;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ if (!size) { /* no messages and no pseudo, make one now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ LOCAL->pseudo = T;
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty ||
+ (((unsigned long) f.curpos) != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,NIL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
+ if (j != elt->private.spare.data) fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* can't happen it says here */
+ if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\r\n",2);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ times.modtime = (times.actime = time (0)) -1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
+ /* flush the lock file */
+ unix_unlock (LOCAL->fd,stream,lock);
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
+ size - ((unsigned long) LOCAL->filesize) : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (mm_diskerror (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ mm_log (tmp,ERROR);
+ mm_diskerror (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/os2/unixnt.h b/imap/src/osdep/os2/unixnt.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/os2/unixnt.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/os2/write.c b/imap/src/osdep/os2/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/os2/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/tops-20/build.ctl b/imap/src/osdep/tops-20/build.ctl
new file mode 100644
index 00000000..45a13450
--- /dev/null
+++ b/imap/src/osdep/tops-20/build.ctl
@@ -0,0 +1,63 @@
+! ========================================================================
+! Copyright 1988-2006 University of Washington
+!
+! Licensed under the Apache License, Version 2.0 (the "License");
+! you may not use this file except in compliance with the License.
+! You may obtain a copy of the License at
+!
+! http://www.apache.org/licenses/LICENSE-2.0
+!
+!
+! ========================================================================
+
+! Program: Portable C client build for TOPS-20
+!
+! Author: Mark Crispin
+! Networks and Distributed Computing
+! Computing & Communications
+! University of Washington
+! Administration Building, AG-44
+! Seattle, WA 98195
+! Internet: MRC@CAC.Washington.EDU
+!
+! Date: 11 May 1989
+! Last Edited: 30 August 2006
+
+!
+! Set environment
+@DEFINE SYS: PS:<KCC-6>,SYS:
+!
+! Build c-client library
+!
+@COPY OS_T20.* OSDEP.*
+@CCX -c -m -w=note MAIL.C DUMMYT20.C IMAP4R1.C SMTP.C NNTP.C POP3.C RFC822.C MISC.C OSDEP.C FLSTRING.C SMANAGER.C NEWSRC.C NETMSG.C UTF8.C UTF8AUX.C
+!
+! Build c-client library file. Should use MAKLIB, but MAKLIB barfs on MAIL.REL
+!
+@DELETE CCLINT.REL
+@APPEND MAIL.REL,DUMMYT20.REL,IMAP4R1.REL,SMTP.REL,NNTP.REL,POP3.REL,RFC822.REL,MISC.REL,OSDEP.REL,FLSTRING.REL,SMANAGER.REL,NEWSRC.REL,NETMSG.REL,UTF8.REL,UTF8AUX.REL CCLINT.REL
+!
+! Build MTEST library test program
+!
+@CCX -c -m -w=note MTEST.C
+@LINK
+*/SET:.HIGH.:200000
+*C:LIBCKX.REL
+*MTEST.REL
+*CCLINT.REL
+*MTEST/SAVE
+*/GO
+!
+! Build MAILUTIL
+!
+@CCX -c -m -w=note MAILUTIL.C
+@RENAME MAILUTIL.REL MUTIL.REL
+@LINK
+*/SET:.HIGH.:200000
+*C:LIBCKX.REL
+*MUTIL.REL
+*CCLINT.REL
+*MUTIL/SAVE
+*/GO
+@RESET
+@RENAME MUTIL.EXE MAILUTIL.EXE
diff --git a/imap/src/osdep/tops-20/dummy.h b/imap/src/osdep/tops-20/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/tops-20/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/tops-20/dummyt20.c b/imap/src/osdep/tops-20/dummyt20.c
new file mode 100644
index 00000000..1117ad45
--- /dev/null
+++ b/imap/src/osdep/tops-20/dummyt20.c
@@ -0,0 +1,294 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for TOPS-20
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/tops-20/env_t20.c b/imap/src/osdep/tops-20/env_t20.c
new file mode 100644
index 00000000..38403454
--- /dev/null
+++ b/imap/src/osdep/tops-20/env_t20.c
@@ -0,0 +1,226 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Environment routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ char *s;
+ int argblk[4];
+ argblk[1] = (int) (date-1);
+ argblk[2] = -1; /* time now */
+ argblk[3] = OT_822; /* want RFC [2]822 format */
+ jsys (ODTIM,argblk);
+ /* suppress time zone text if desired */
+ if (no822tztext && (s = strstr (date," ("))) *s = NIL;
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ int argblk[5];
+ argblk[1] = (int) (date-1);
+ argblk[2] = -1; /* time now */
+ argblk[3] = OT_4YR; /* output in 4-digit year format */
+ jsys (ODTIM,argblk);
+ argblk[2] = ' '; /* delimit with space */
+ jsys (BOUT,argblk);
+ argblk[2] = -1; /* time now */
+ argblk[4] = 0; /* no flags */
+ jsys (ODCNV,argblk); /* get time zone */
+ argblk[2] = ((argblk[4] & 077000000) >> 18) * -100;
+ /* add an hour if summer time */
+ if (argblk[4] & IC_ADS) argblk[2] += 100;
+ argblk[3] = 0340005000012;
+ jsys (NOUT,argblk);
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ if (!myUserName) { /* get user name if don't have it yet */
+ char tmp[MAILTMPLEN];
+ int argblk[5],i;
+ jsys (GJINF,argblk); /* get job poop */
+ if (!(i = argblk[1])) { /* remember user number */
+ if (flags) *flags = MU_NOTLOGGEDIN;
+ return "SYSTEM"; /* not logged in */
+ }
+ argblk[1] = (int) (tmp-1); /* destination */
+ argblk[2] = i; /* user number */
+ jsys (DIRST,argblk); /* get user name string */
+ myUserName = cpystr (tmp); /* copy user name */
+ argblk[1] = 0; /* no flags */
+ argblk[2] = i; /* user number */
+ argblk[3] = 0; /* no stepping */
+ jsys (RCDIR,argblk); /* get home directory */
+ argblk[1] = (int) (tmp-1); /* destination */
+ argblk[2] = argblk[3]; /* home directory number */
+ jsys (DIRST,argblk); /* get home directory string */
+ myHomeDir = cpystr (tmp); /* copy home directory */
+ if (!myNewsrc) { /* set news file name if not defined */
+ sprintf (tmp,"%sNEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ if (flags) *flags = MU_LOGGEDIN;
+ }
+ return myUserName;
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ if (!myLocalHost) { /* initialize if first time */
+ char tmp[MAILTMPLEN];
+ int argblk[5];
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ argblk[3] = -1; /* want local host */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL");
+ myLocalHost = cpystr (tmp);
+ }
+ return myLocalHost;
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ return NIL; /* no default prototype */
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/tops-20/env_t20.h b/imap/src/osdep/tops-20/env_t20.h
new file mode 100644
index 00000000..3031f4ed
--- /dev/null
+++ b/imap/src/osdep/tops-20/env_t20.h
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/tops-20/fs_t20.c b/imap/src/osdep/tops-20/fs_t20.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/tops-20/fs_t20.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/tops-20/ftl_t20.c b/imap/src/osdep/tops-20/ftl_t20.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/tops-20/ftl_t20.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/tops-20/linkage.c b/imap/src/osdep/tops-20/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/tops-20/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/tops-20/linkage.h b/imap/src/osdep/tops-20/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/tops-20/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/tops-20/log_t20.c b/imap/src/osdep/tops-20/log_t20.c
new file mode 100644
index 00000000..1c744d7f
--- /dev/null
+++ b/imap/src/osdep/tops-20/log_t20.c
@@ -0,0 +1,80 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 server login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
+{
+ int uid;
+ int argblk[5];
+ if (authuser && *authuser) { /* not available */
+ syslog (LOG_NOTICE|LOG_AUTH,
+ "Login %s failed: invalid authentication ID %s host=%.80s",
+ user,authuser,tcp_clienthost ());
+ sleep (3);
+ return NIL;
+ }
+ argblk[1] = RC_EMO; /* require exact match */
+ argblk[2] = (int) (user-1); /* user name */
+ argblk[3] = 0; /* no stepping */
+ if (!jsys (RCUSR,argblk)) return NIL;
+ uid = argblk[1] = argblk[3]; /* user number */
+ argblk[2] = (int) (pass-1); /* password */
+ argblk[3] = 0; /* no special account */
+ if (!jsys (LOGIN,argblk)) return NIL;
+ return T;
+}
+
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL; /* how to implement this on TOPS-20??? */
+}
diff --git a/imap/src/osdep/tops-20/nl_t20.c b/imap/src/osdep/tops-20/nl_t20.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/tops-20/nl_t20.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/tops-20/os_t20.c b/imap/src/osdep/tops-20/os_t20.c
new file mode 100644
index 00000000..de0479df
--- /dev/null
+++ b/imap/src/osdep/tops-20/os_t20.c
@@ -0,0 +1,106 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+#include "mail.h"
+#include <jsys.h> /* must be before tcp_t20.h */
+#include "tcp_t20.h" /* must be before osdep include tcp.h */
+#include <time.h>
+#include "osdep.h"
+#include <sys/time.h>
+#include "misc.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include "fs_t20.c"
+#include "ftl_t20.c"
+#include "nl_t20.c"
+#include "env_t20.c"
+#include "tcp_t20.c"
+#include "log_t20.c"
+
+#define MD5ENABLE "PS:<SYSTEM>CRAM-MD5.PWD"
+#include "auth_md5.c"
+#include "auth_ext.c"
+#include "auth_pla.c"
+#include "auth_log.c"
+
+/* Emulator for UNIX gethostid() call
+ * Returns: host id
+ */
+
+long gethostid ()
+{
+ int argblk[5];
+#ifndef _APRID
+#define _APRID 28
+#endif
+ argblk[1] = _APRID;
+ jsys (GETAB,argblk);
+ return (long) argblk[1];
+}
+
+
+/* Emulator for UNIX getpass() call
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ char *s;
+ static char pwd[PWDLEN];
+ int argblk[5],mode;
+ argblk[1] = (int) (prompt-1); /* prompt user */
+ jsys (PSOUT,argblk);
+ argblk[1] = _PRIIN; /* get current TTY mode */
+ jsys (RFMOD,argblk);
+ mode = argblk[2]; /* save for later */
+ argblk[2] &= ~06000;
+ jsys (SFMOD,argblk);
+ jsys (STPAR,argblk);
+ fgets (pwd,PWDLEN-1,stdin);
+ pwd[PWDLEN-1] = '\0';
+ if (s = strchr (pwd,'\n')) *s = '\0';
+ putchar ('\n');
+ argblk[2] = mode;
+ jsys (SFMOD,argblk);
+ jsys (STPAR,argblk);
+ return pwd;
+}
diff --git a/imap/src/osdep/tops-20/os_t20.h b/imap/src/osdep/tops-20/os_t20.h
new file mode 100644
index 00000000..4c397243
--- /dev/null
+++ b/imap/src/osdep/tops-20/os_t20.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 January 2007
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include "env_t20.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid (void);
+char *getpass (const char *prompt);
+
+#define strtok_r(a,b,c) strtok(a,b)
diff --git a/imap/src/osdep/tops-20/pmatch.c b/imap/src/osdep/tops-20/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/tops-20/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/tops-20/shortsym.h b/imap/src/osdep/tops-20/shortsym.h
new file mode 100644
index 00000000..2bd54649
--- /dev/null
+++ b/imap/src/osdep/tops-20/shortsym.h
@@ -0,0 +1,608 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Definitions for compilers with 6-letter symbol limits
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1995
+ * Last Edited: 1 January 2008
+ */
+
+#define auth_link a_link
+#define auth_log a_log
+#define auth_login_client al_cli
+#define auth_login_server al_ser
+#define auth_ext a_ext
+#define auth_external_client ae_cli
+#define auth_external_server ae_ser
+#define auth_md5 a_md5
+#define auth_md5_valid a5_val
+#define auth_md5_client a5_cli
+#define auth_md5_server a5_ser
+#define auth_pla a_pla
+#define auth_plain_client ap_cli
+#define auth_plain_server ap_ser
+#define authenticate a_auth
+#define authserver_login a_serv
+#define body_encodings bencds
+#define body_types btypes
+#define compare_csizedtext cm_szt
+#define compare_cstring cm_str
+#define compare_uchar cm_uch
+#define compare_ulong cm_uln
+#define default_proto d_prot
+#define dummy_append d_appn
+#define dummy_canonicalize d_cano
+#define dummy_check d_chck
+#define dummy_close d_clos
+#define dummy_copy d_copy
+#define dummy_create d_crea
+#define dummy_create_path d_crep
+#define dummy_delete d_del
+#define dummy_expunge d_exp
+#define dummy_file d_fil
+#define dummy_list d_list
+#define dummy_list_work d_lstw
+#define dummy_listed d_lstd
+#define dummy_lsub d_lsub
+#define dummy_open d_open
+#define dummy_parameters d_parm
+#define dummy_ping d_ping
+#define dummy_rename d_ren
+#define dummy_scan d_scan
+#define dummy_search d_srch
+#define dummy_subscribe d_subs
+#define dummy_valid d_val
+#define env_parameters e_parm
+#define fatal fatal
+#define file_string fl_str
+#define file_string_init fl_ini
+#define file_string_next fl_nxt
+#define file_string_setpos fl_sps
+#define fs_get f_get
+#define fs_give f_give
+#define fs_resize f_rsiz
+#define hash_create h_crea
+#define hash_destory h_dest
+#define hash_index h_indx
+#define hash_lookup h_lkup
+#define hash_add h_add
+#define hash_lookup_and_add h_lad
+#define imap_OK i_OK
+#define imap_acl_work i_aclw
+#define imap_append i_appn
+#define imap_append_single i_apps
+#define imap_anon i_anon
+#define imap_auth i_auth
+#define imap_cache i_cach
+#define imap_cap i_cap
+#define imap_capability i_capa
+#define imap_challenge i_chln
+#define imap_check i_chck
+#define imap_close i_clos
+#define imap_copy i_copy
+#define imap_create i_crea
+#define imap_delete i_del
+#define imap_deleteacl i_dacl
+#define imap_expunge i_expn
+#define imap_fake i_fake
+#define imap_fast i_fast
+#define imap_fetch i_fetc
+#define imap_flag i_flag
+#define imap_flags i_flgs
+#define imap_gc i_gc
+#define imap_gc_body ig_bdy
+#define imap_getacl i_gacl
+#define imap_getquota i_gqot
+#define imap_getquotaroot i_gqtr
+#define imap_host i_host
+#define imap_list i_list
+#define imap_listrights i_lrgh
+#define imap_list_work il_wrk
+#define imap_login i_logn
+#define imap_lsub i_lsub
+#define imap_manage i_man
+#define imap_msgdata i_msgd
+#define imap_msgno i_msgn
+#define imap_myrights i_mrgh
+#define imap_open i_open
+#define imap_parameters i_parm
+#define imap_parse_address ip_adr
+#define imap_parse_adrlist ip_adl
+#define imap_parse_astring ip_ast
+#define imap_parse_body ip_bdy
+#define imap_parse_body_parameter ipb_pa
+#define imap_parse_body_structure ipb_st
+#define imap_parse_capabilities ip_cap
+#define imap_parse_disposition ip_dsp
+#define imap_parse_envelope ip_env
+#define imap_parse_extension ip_ext
+#define imap_parse_flags ip_flg
+#define imap_parse_header ip_hdr
+#define imap_parse_language ip_lng
+#define imap_parse_namespace ip_nam
+#define imap_parse_reply ip_rep
+#define imap_parse_response ip_rsp
+#define imap_parse_string ip_str
+#define imap_parse_stringlist ip_stl
+#define imap_parse_thread ip_thr
+#define imap_parse_unsolicited ip_uns
+#define imap_parse_user_flag ipu_fl
+#define imap_ping i_ping
+#define imap_reform_sequence i_rfrs
+#define imap_rename i_ren
+#define imap_reply i_rep
+#define imap_response i_rspn
+#define imap_scan i_scan
+#define imap_search i_srch
+#define imap_send i_send
+#define imap_send_astring is_ast
+#define imap_send_literal is_lit
+#define imap_send_sdate iss_da
+#define imap_send_slist iss_sl
+#define imap_send_spgm iss_pg
+#define imap_send_spgm_trim iss_pt
+#define imap_send_sset iss_st
+#define imap_send_sset_work iss_sw
+#define imap_setacl i_sacl
+#define imap_setquota i_sqot
+#define imap_sort i_sort
+#define imap_sout i_sout
+#define imap_soutr i_sotr
+#define imap_status i_stat
+#define imap_structure i_stru
+#define imap_subscribe i_sub
+#define imap_thread i_thrd
+#define imap_thread_work i_thrw
+#define imap_uid i_uid
+#define imap_unsubscribe i_uns
+#define imap_valid i_val
+#define internal_date in_dat
+#define mail_append_full m_appn
+#define mail_append_multiple m_appm
+#define mail_append_set m_apps
+#define mail_auth m_auth
+#define mail_body m_body
+#define mail_cdate m_cdat
+#define mail_check m_chck
+#define mail_close_full m_clos
+#define mail_copy_full m_copy
+#define mail_create m_crea
+#define mail_criteria m_crit
+#define mail_criteria_date mc_dat
+#define mail_criteria_string mc_str
+#define mail_date m_date
+#define mail_debug m_dbug
+#define mail_delete m_del
+#define mail_dlog m_dlog
+#define mail_elt m_elt
+#define mail_exists m_exst
+#define mail_expunge_full m_expn
+#define mail_expunged m_expd
+#define mail_fetch_body fs_bdy
+#define mail_fetch_fast mf_fst
+#define mail_fetch_flags mf_flg
+#define mail_fetch_header mf_hdr
+#define mail_fetch_message mf_msg
+#define mail_fetch_mime mf_mim
+#define mail_fetch_overview mf_ovr
+#define mail_fetch_overview_sequence mf_ovs
+#define mail_fetch_overview_default mf_ovd
+#define mail_fetch_structure mf_str
+#define mail_fetch_text mf_txt
+#define mail_fetch_text_return mf_txr
+#define mail_fetch_string_return mf_tsr
+#define mail_fetchfrom mf_frm
+#define mail_fetchsubject mf_sub
+#define mail_filter m_filt
+#define mail_flag m_flag
+#define mail_free_acl mr_acl
+#define mail_free_address mr_add
+#define mail_free_body mr_bdy
+#define mail_free_body_data mrb_da
+#define mail_free_body_parameter mrb_pr
+#define mail_free_body_part mrb_pt
+#define mail_free_cache mr_cac
+#define mail_free_elt mr_elt
+#define mail_free_envelope mr_env
+#define mail_free_handle mr_han
+#define mail_free_namespace mr_nsp
+#define mail_free_quotalist mr_qtl
+#define mail_free_searchheader mrs_hd
+#define mail_free_searchor mrs_or
+#define mail_free_searchpgm mrs_pg
+#define mail_free_searchpgmlist mrs_pl
+#define mail_free_searchset mrs_st
+#define mail_free_sortpgm mr_spg
+#define mail_free_stringlist mr_sls
+#define mail_free_threadnode mr_thn
+#define mail_gc m_gc
+#define mail_gc_msg m_gcm
+#define mail_gc_body m_gcb
+#define mail_initbody m_ibdy
+#define mail_link m_link
+#define mail_list m_list
+#define mail_lock m_lock
+#define mail_longdate ml_lda
+#define mail_lookup_auth m_laut
+#define mail_lookup_auth_name m_latn
+#define mail_lsub m_lsub
+#define mail_makehandle m_mhdl
+#define mail_match_lines m_mlns
+#define mail_msgno m_msgn
+#define mail_newacl mn_acl
+#define mail_newaddr mn_add
+#define mail_newbody mn_bdy
+#define mail_newbody_parameter mnb_pr
+#define mail_newbody_part mnb_pt
+#define mail_newbody_message_part mnb_mp
+#define mail_new_cache_elt mn_elt
+#define mail_newenvelope mn_env
+#define mail_newmsg mn_msg
+#define mail_newquotalist mn_qtl
+#define mail_newsearchheader mns_hd
+#define mail_newsearchor mns_or
+#define mail_newsearchpgm mns_pg
+#define mail_newsearchpgmlist mns_pl
+#define mail_newsearchset mns_st
+#define mail_newsortpgm mn_spg
+#define mail_newstringlist mn_sls
+#define mail_newthreadnode mn_thr
+#define mail_nodebug m_ndbg
+#define mail_open m_open
+#define mail_parameters m_parm
+#define mail_parse_date mp_dat
+#define mail_parse_flags mp_flg
+#define mail_parse_set mp_set
+#define mail_partial_body mpt_bd
+#define mail_partial_text mpt_tx
+#define mail_ping m_ping
+#define mail_read m_read
+#define mail_recent m_rcent
+#define mail_rename m_ren
+#define mail_scan m_scan
+#define mail_search_addr ms_adr
+#define mail_search_body ms_bdy
+#define mail_search_default ms_def
+#define mail_search_full m_srch
+#define mail_search_gets ms_gts
+#define mail_search_header ms_hdr
+#define mail_search_header_text ms_hdt
+#define mail_search_keyword ms_key
+#define mail_search_msg ms_msg
+#define mail_search_string ms_str
+#define mail_search_string_work ms_stw
+#define mail_search_text ms_txt
+#define mail_sequence m_seq
+#define mail_shortdate m_shtd
+#define mail_skip_fwd msk_fw
+#define mail_skip_re msk_re
+#define mail_sort ml_srt
+#define mail_sort_cache ms_csh
+#define mail_sort_compare ms_cmp
+#define mail_sort_loadcache ms_lcs
+#define mail_sort_msgs ms_mgs
+#define mail_status m_stat
+#define mail_status_default m_stad
+#define mail_stream m_strm
+#define mail_string m_strg
+#define mail_string_init mt_ini
+#define mail_string_next mt_nxt
+#define mail_string_setpos mt_sps
+#define mail_strip_subject mst_sb
+#define mail_strip_subject_wsp mst_ws
+#define mail_strip_subject_blob mst_bl
+#define mail_subscribe m_sub
+#define mail_thread m_thr
+#define mail_threadlist mt_lst
+#define mail_thread_c2node mt_c2n
+#define mail_thread_check_child mt_ckc
+#define mail_thread_compare_date mtc_da
+#define mail_thread_loadcache mt_ldc
+#define mail_thread_msgs mt_mgs
+#define mail_thread_orderedsubject mt_osb
+#define mail_thread_parse_msgid mtp_mi
+#define mail_thread_parse_references mtp_rf
+#define mail_thread_prune_dummy mt_prd
+#define mail_thread_references mt_ref
+#define mail_thread_sort mt_srt
+#define mail_uid m_uid
+#define mail_uid_sequence mu_seq
+#define mail_unlock m_unl
+#define mail_unsubscribe m_uns
+#define mail_usable_network_stream m_usns
+#define mail_utf7_valid m_ut7v
+#define mail_valid m_val
+#define mail_valid_net mv_net
+#define mail_valid_net_parse mvn_pr
+#define mail_valid_net_parse_work mvn_pw
+#define mail_versioncheck m_vers
+#define mailboxfile mbxfil
+#define md5_init m5_ini
+#define md5_update m5_upd
+#define md5_final m5_fin
+#define mime2_decode m2_dec
+#define mime2_text m2_txt
+#define mime2_token m2_tok
+#define mm_cache mm_cac
+#define mm_critical mm_crt
+#define mm_diskerror mm_dse
+#define mm_dlog mm_dlg
+#define mm_exists mm_exs
+#define mm_expunged mm_exp
+#define mm_fatal mm_ftl
+#define mm_flags mm_flg
+#define mm_list mm_lst
+#define mm_log mm_log
+#define mm_login mm_lgi
+#define mm_lsub mm_lsb
+#define mm_mailbox mm_mbx
+#define mm_nocritical mm_ncr
+#define mm_notify mm_not
+#define mm_searched mm_src
+#define myhomedir myhome
+#define mylocalhost myhost
+#define myusername_full myuser
+#define net_aopen nt_aop
+#define net_close nt_cls
+#define net_getbuffer nt_gtb
+#define net_getdata nt_gtd
+#define net_getline nt_gtl
+#define net_host nt_hst
+#define net_localhost nt_lhs
+#define net_open nt_opn
+#define net_port nt_prt
+#define net_sout nt_sot
+#define net_soutr nt_str
+#define netmsg_read nm_rea
+#define netmsg_slurp nm_slr
+#define netmsg_slurp_text nm_slt
+#define newsrc_check_uid nsc_ui
+#define newsrc_create ns_crea
+#define newsrc_error ns_err
+#define newsrc_lsub ns_lsub
+#define newsrc_newmessages ns_nms
+#define newsrc_newstate ns_nst
+#define newsrc_read ns_rea
+#define newsrc_status ns_sta
+#define newsrc_update ns_upd
+#define newsrc_write ns_wri
+#define newsrc_write_error ns_wer
+#define nntp_append n_appn
+#define nntp_canonicalize n_cano
+#define nntp_check n_chck
+#define nntp_close n_clos
+#define nntp_copy n_copy
+#define nntp_create n_crea
+#define nntp_delete n_del
+#define nntp_expunge n_expn
+#define nntp_fake n_fake
+#define nntp_fetchfast nf_fst
+#define nntp_fetchflags nf_flg
+#define nntp_fetchmessage nf_msg
+#define nntp_flagmsg n_fmsg
+#define nntp_gc n_gc
+#define nntp_getmap n_gmap
+#define nntp_header n_head
+#define nntp_isvalid n_isvl
+#define nntp_list n_list
+#define nntp_lsub n_lsub
+#define nntp_mail n_mail
+#define nntp_mclose n_mcls
+#define nntp_mopen n_mopn
+#define nntp_open_full n_open
+#define nntp_over n_ovr
+#define nntp_overview n_over
+#define nntp_parameters n_parm
+#define nntp_parsestructure n_pars
+#define nntp_parse_overview n_povr
+#define nntp_ping n_ping
+#define nntp_rename n_ren
+#define nntp_reply n_repl
+#define nntp_scan n_scan
+#define nntp_search n_srch
+#define nntp_search_msg ns_msg
+#define nntp_send n_send
+#define nntp_send_auth ns_aut
+#define nntp_send_auth_work ns_atw
+#define nntp_send_work n_sndw
+#define nntp_sort n_sort
+#define nntp_sort_loadcache ns_lcs
+#define nntp_soutr n_sout
+#define nntp_status n_stat
+#define nntp_subscribe n_sub
+#define nntp_text n_text
+#define nntp_text_slurp nt_slp
+#define nntp_thread n_thrd
+#define nntp_unsubscribe n_uns
+#define nntp_valid n_val
+#define pop3_append p_appn
+#define pop3_auth p_auth
+#define pop3_cache p_cach
+#define pop3_challenge p_chal
+#define pop3_check p_chck
+#define pop3_close p_clos
+#define pop3_copy p_copy
+#define pop3_create p_crea
+#define pop3_delete p_del
+#define pop3_expunge p_exp
+#define pop3_fake p_fake
+#define pop3_fetchfast pf_fst
+#define pop3_fetchflags pf_flg
+#define pop3_fetchmessage pf_msg
+#define pop3_gc p_gc
+#define pop3_list p_list
+#define pop3_lsub p_lsub
+#define pop3_open p_open
+#define pop3_parameters p_parm
+#define pop3_parsestructure p_pars
+#define pop3_ping p_ping
+#define pop3_rename p_ren
+#define pop3_reply p_rep
+#define pop3_response p_resp
+#define pop3_scan p_scan
+#define pop3_send p_send
+#define pop3_send_num ps_num
+#define pop3_status p_stat
+#define pop3_subscribe p_sub
+#define pop3_unsubscribe p_uns
+#define pop3_valid p_val
+#define rfc822_8bit r
+#define rfc822_address r_addr
+#define rfc822_address_line ra_lin
+#define rfc822_base64 r_b64
+#define rfc822_binary r_bin
+#define rfc822_cat r_cat
+#define rfc822_contents r_cont
+#define rfc822_cpy r_cpy
+#define rfc822_cpy_adr rc_adr
+#define rfc822_date r_date
+#define rfc822_default_subtype rd_sub
+#define rfc822_encode_body_7bit reb_7b
+#define rfc822_encode_body_8bit reb_8b
+#define rfc822_header r_head
+#define rfc822_header_line rh_lin
+#define rfc822_output r_out
+#define rfc822_output_address ro_adr
+#define rfc822_output_address_line roa_ln
+#define rfc822_output_address_list roa_li
+#define rfc822_output_body ro_bdy
+#define rfc822_output_body_header rob_hd
+#define rfc822_output_full ro_ful
+#define rfc822_output_flush ro_flu
+#define rfc822_output_header ro_hdr
+#define rfc822_output_header_line roh_ln
+#define rfc822_output_cat ro_cat
+#define rfc822_output_parameter ro_par
+#define rfc822_output_stringlist ro_stl
+#define rfc822_output_text ro_txt
+#define rfc822_parse_address rp_adr
+#define rfc822_parse_addrspec rp_ads
+#define rfc822_parse_adrlist rp_adl
+#define rfc822_parse_content rp_cnt
+#define rfc822_parse_content_header rpc_hd
+#define rfc822_parse_group rp_grp
+#define rfc822_parse_mailbox rp_mbx
+#define rfc822_parse_msg_full rp_msg
+#define rfc822_parse_parameter rp_par
+#define rfc822_parse_phrase rp_phr
+#define rfc822_parse_routeaddr rp_rte
+#define rfc822_parse_word rp_wrd
+#define rfc822_phraseonly r_poly
+#define rfc822_qprint r_qpnt
+#define rfc822_quote r_quot
+#define rfc822_skip_comment rs_cmt
+#define rfc822_skipws rs_ws
+#define rfc822_timezone r_tz
+#define rfc822_write_address_full rw_adr
+#define rfc822_write_body_header rwbh_8
+#define server_input_wait s_iwat
+#define server_login s_log
+#define server_init s_init
+#define sm_read sm_rd
+#define sm_subscribe sm_sub
+#define sm_unsubscribe sm_uns
+#define smtp_auth s_auth
+#define smtp_challenge s_chal
+#define smtp_close s_clos
+#define smtp_ehlo s_ehlo
+#define smtp_fake s_fake
+#define smtp_mail s_mail
+#define smtp_open_full s_open
+#define smtp_rcpt s_rcpt
+#define smtp_reply s_repl
+#define smtp_response s_resp
+#define smtp_send s_send
+#define smtp_send_auth ss_aut
+#define smtp_send_auth_work ss_atw
+#define smtp_send_work ss_wrk
+#define smtp_soutr s_str
+#define strcrlfcpy sc_cpy
+#define strcrlflen sc_len
+#define tcp_aopen t_aopn
+#define tcp_canonical t_cnon
+#define tcp_clientaddr t_cadr
+#define tcp_clienthost t_chst
+#define tcp_clientport t_cprt
+#define tcp_close t_clos
+#define tcp_getbuffer tg_buf
+#define tcp_getdata tg_dat
+#define tcp_getline tg_lin
+#define tcp_host t_host
+#define tcp_localhost t_lhst
+#define tcp_open t_open
+#define tcp_parameters t_parameters
+#define tcp_port t_port
+#define tcp_remotehost t_rhst
+#define tcp_serveraddr t_sadr
+#define tcp_serverhost t_shst
+#define tcp_serverport t_sprt
+#define tcp_sout t_sout
+#define tcp_soutr t_str
+#define textcpy txcopy
+#define textcpystring txcpst
+#define textcpyoffstring txcpos
+#define ucs4_cs_get u4_csg
+#define ucs4_decompose u4_dcm
+#define ucs4_decompose_recursive u4_dcr
+#define ucs4_rmapbuf u4r_bf
+#define ucs4_rmaplen u4r_ln
+#define ucs4_rmaptext u4r_tx
+#define ucs4_titlecase u4_tcs
+#define ucs4_width u4_wid
+#define utf8_badcharset u8_bcs
+#define utf8_charset u8_chs
+#define utf8_cstext u8_cst
+#define utf8_cstocstext u8_cct
+#define utf8_from_mutf7 u8fmu7
+#define utf8_get u8_get
+#define utf8_get_raw u8_gtr
+#define utf8_iso2022text u8_i22
+#define utf8_mime2text u8_mi2
+#define utf8_put u8_put
+#define utf8_rmap u8_rmp
+#define utf8_rmap_cs u8r_cs
+#define utf8_rmap_gen u8r_gn
+#define utf8_rmapsize u8r_sz
+#define utf8_rmaptext u8r_tx
+#define utf8_script u8_scr
+#define utf8_searchpgm u8_spg
+#define utf8_size u8_siz
+#define utf8_stringlist u8_lst
+#define utf8_text u8_txt
+#define utf8_text_2022 u8t_22
+#define utf8_text_8859_1 u8t_we
+#define utf8_text_1byte0 u8t_10
+#define utf8_text_1byte u8t_1b
+#define utf8_text_1byte8 u8t_18
+#define utf8_text_cs ut8_cs
+#define utf8_text_euc u8t_eu
+#define utf8_text_dbyte u8t_db
+#define utf8_text_dbyte2 u8t_d2
+#define utf8_text_sjis u8t_sj
+#define utf8_text_ucs2 u8t_u2
+#define utf8_text_ucs4 ut8_u4
+#define utf8_text_utf7 ut8_u7
+#define utf8_text_utf8 ut8_u8
+#define utf8_text_utf16 ut8_16
+#define utf8_to_mutf7 u8tmu7
+#define utf8_validate u8_val
+#define utf8_textwidth u8_twd
diff --git a/imap/src/osdep/tops-20/tcp_t20.c b/imap/src/osdep/tops-20/tcp_t20.c
new file mode 100644
index 00000000..7b2a2580
--- /dev/null
+++ b/imap/src/osdep/tops-20/tcp_t20.c
@@ -0,0 +1,365 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ char *s,tmp[MAILTMPLEN];
+ TCPSTREAM *stream = NIL;
+ int argblk[5],jfn;
+ unsigned long i,j,k,l;
+ char file[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
+ argblk[3] = (i << 24) + (j << 16) + (k << 8) + l;
+ sprintf (tmp,"[%lu.%lu.%lu.%lu]",i,j,k,l);
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else { /* host name */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (host-1); /* pointer to host */
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (host-1);
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTDOM,argblk)){/* try the CHIVES domain way */
+ argblk[1] = _GTHSN; /* failed, do the host table then */
+ if (!jsys (GTHST,argblk)) {
+ sprintf (tmp,"No such host as %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ /* get the official name */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,host);
+ }
+ }
+ }
+
+ sprintf (file,"TCP:.%o-%d;PERSIST:30;CONNECTION:ACTIVE",argblk[3],port);
+ argblk[1] = GJ_SHT; /* short form GTJFN% */
+ argblk[2] = (int) (file-1); /* pointer to file name */
+ /* get JFN for TCP: file */
+ if (!jsys (GTJFN,argblk)) fatal ("Unable to create TCP JFN");
+ jfn = argblk[1]; /* note JFN for later */
+ /* want 8-bit bidirectional I/O */
+ argblk[2] = OF_RD|OF_WR|(FLD (8,monsym("OF%BSZ")));
+ if (!jsys (OPENF,argblk)) {
+ sprintf (file,"Can't connect to %s,%d server",tmp,port);
+ mm_log (file,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (tmp); /* copy official host name */
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ argblk[3] = -1; /* want local host */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL");
+ stream->localhost = cpystr (tmp);
+ stream->port = port; /* save port number */
+ stream->jfn = jfn; /* init JFN */
+ return stream;
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ int argblk[5];
+ unsigned long n,m;
+ char *ret,*stp,*st;
+ *contd = NIL; /* assume no continuation */
+ argblk[1] = stream->jfn; /* read from TCP */
+ /* pointer to buffer */
+ argblk[2] = (int) (stream->ibuf - 1);
+ argblk[3] = BUFLEN; /* max number of bytes to read */
+ argblk[4] = '\012'; /* terminate on LF */
+ if (!jsys (SIN,argblk)) return NIL;
+ n = BUFLEN - argblk[3]; /* number of bytes read */
+ /* got a complete line? */
+ if ((stream->ibuf[n - 2] == '\015') && (stream->ibuf[n - 1] == '\012')) {
+ memcpy ((ret = (char *) fs_get (n)),stream->ibuf,*size = n - 2);
+ ret[n - 2] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ /* copy partial string */
+ memcpy ((ret = (char *) fs_get (n)),stream->ibuf,*size = n);
+ /* special case of newline broken by buffer */
+ if ((stream->ibuf[n - 1] == '\015') && jsys (BIN,argblk) &&
+ (argblk[2] == '\012')) { /* was it? */
+ ret[n - 1] = '\0'; /* tie off string with null */
+ }
+ /* otherwise back up */
+ else if (!jsys (BKJFN,argblk)) fs_give ((void **) &ret);
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* read from TCP */
+ argblk[2] = (int) (buffer-1); /* pointer to buffer */
+ argblk[3] = -size; /* number of bytes to read */
+ if (!jsys (SIN,argblk)) return NIL;
+ buffer[size] = '\0'; /* tie off text */
+ return T;
+}
+
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* write to TCP */
+ argblk[2] = (int) (string-1); /* pointer to buffer */
+ argblk[3] = 0; /* write until NUL */
+ if (!jsys (SOUTR,argblk)) return NIL;
+ return T;
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* write to TCP */
+ argblk[2] = (int) (string-1); /* pointer to buffer */
+ argblk[3] = -size; /* write this many bytes */
+ if (!jsys (SOUTR,argblk)) return NIL;
+ return T;
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* close TCP */
+ jsys (CLOSF,argblk);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP return host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return remote host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP return local host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: local host name for this stream
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ int argblk[5];
+ static char tmp[MAILTMPLEN];
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (name-1); /* pointer to host */
+ argblk[4] = (int) (tmp-1); /* pointer to return destination */
+ if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (name-1);
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTDOM,argblk)) { /* try the CHIVES domain way */
+ argblk[1] = _GTHSN; /* failed, do the host table then */
+ if (!jsys (GTHST,argblk)) return name;
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ /* get the official name */
+ if (!jsys (GTHST,argblk)) return name;
+ }
+ }
+ return tmp;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/tops-20/tcp_t20.h b/imap/src/osdep/tops-20/tcp_t20.h
new file mode 100644
index 00000000..291465cf
--- /dev/null
+++ b/imap/src/osdep/tops-20/tcp_t20.h
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int jfn; /* jfn for connection */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* All of these should be in JSYS.H, but just in case... */
+
+#ifndef _GTHPN
+#define _GTHPN 12
+#endif
+
+#ifndef _GTDPN
+#define _GTDPN 12
+#endif
+
+#ifndef GTDOM
+#define GTDOM (FLD (1,JSYS_CLASS) | 501
+#endif
diff --git a/imap/src/osdep/unix/Makefile b/imap/src/osdep/unix/Makefile
new file mode 100644
index 00000000..78913acc
--- /dev/null
+++ b/imap/src/osdep/unix/Makefile
@@ -0,0 +1,1078 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: C client makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 17 December 2007
+
+
+# Command line build parameters
+
+EXTRAAUTHENTICATORS=
+EXTRADRIVERS=mbox
+PASSWDTYPE=std
+SSLTYPE=nopwd
+IP=4
+
+
+# The optimization level here for GCC ports is set here for a reason. It's
+# to get you to read this text.
+# The general concensus seems to be that -O2 is the one to use.
+# Over the years, I've been told to use many different settings, including -O6.
+# In recent versions of GCC [as of 2/2005], -O6 generates bad code that, among
+# other ill effects, causes infinite loops.
+# -O3 seems to be safe, but empirical observation from our local expert
+# indicates that in some (many?) cases -O3 code runs slower than -O2.
+
+GCCOPTLEVEL= -O2
+
+
+# Try to have some consistency in GCC builds. We want optimization, but we
+# also want to be able to debug.
+
+GCCCFLAGS= -g $(GCCOPTLEVEL) -pipe -fno-omit-frame-pointer
+GCC4CFLAGS= $(GCCCFLAGS) -Wno-pointer-sign
+
+
+# Extended flags needed for SSL. You may need to modify.
+
+SSLDIR=/usr/local/ssl
+SSLCERTS=$(SSLDIR)/certs
+SSLKEYS=$(SSLCERTS)
+SSLINCLUDE=$(SSLDIR)/include
+SSLLIB=$(SSLDIR)/lib
+
+SSLCRYPTO=-lcrypto
+
+# Older versions of MIT Kerberos also have a libcrypto. If so, you may need
+# to use this instead
+#SSLCRYPTO=$(SSLLIB)/libcrypto.a
+
+# RSA Security Inc. released the RSA public key encryption algorithm into
+# the public domain on September 6, 2000. There is no longer any need to
+# use RSAREF.
+SSLRSA= # -lRSAglue -lrsaref
+
+SSLCFLAGS= -I$(SSLINCLUDE) -I$(SSLINCLUDE)/openssl\
+ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" -DSSL_KEY_DIRECTORY=\"$(SSLKEYS)\"
+SSLLDFLAGS= -L$(SSLLIB) -lssl $(SSLCRYPTO) $(SSLRSA)
+
+
+# Extended flags needed for non-standard passwd types. You may need to modify.
+
+AFSDIR=/usr/afsws
+AFSCFLAGS=-I$(AFSDIR)/include
+AFSLIB=$(AFSDIR)/lib
+AFSLDFLAGS=-L$(AFSLIB)/afs -L$(AFSLIB) -L$(AFSDIR)/domestic/lib\
+ -lkauth -lprot -lubik -lauth -lrxkad -lrx -llwp -ldes -lcom_err\
+ $(AFSLIB)/afs/util.a -laudit -lsys
+# AFSLDFLAGS may also need -L/usr/ucblib -lucb
+DCECFLAGS= -DDCE_MINIMAL -DPASSWD_OVERRIDE=\"/opt/pop3/passwd/passwd\"
+DCELDFLAGS= -ldce
+PAMLDFLAGS= -lpam -ldl
+
+
+# Build parameters normally set by the individual port
+
+CHECKPW=std
+LOGINPW=std
+SIGTYPE=bsd
+CRXTYPE=std
+ACTIVEFILE=/usr/lib/news/active
+SPOOLDIR=/usr/spool
+MAILSPOOL=$(SPOOLDIR)/mail
+NEWSSPOOL=$(SPOOLDIR)/news
+RSHPATH=/usr/ucb/rsh
+MD5PWD=/etc/cram-md5.pwd
+# Tries one of the test alternatives below if not specified.
+LOCKPGM=
+# Test alternatives if LOCKPGM not specified
+LOCKPGM1=/usr/libexec/mlock
+LOCKPGM2=/usr/sbin/mlock
+LOCKPGM3=/etc/mlock
+
+
+# Default formats for creating new mailboxes and for empty mailboxes in the
+# default namespace; must be set to the associated driver's prototype.
+#
+# The CREATEPROTO is the default format for new mailbox creation.
+# The EMPTYPROTO is the default format for handling zero-byte files.
+#
+# Normally, this is set by the individual port.
+#
+# NOTE: namespace formats (e.g. mh and news) can not be set as a default format
+# since they do not exist in the default namespace. Also, it is meaningless to
+# set certain other formats (e.g. mbx, mx, and mix) as the EMPTYPROTO since
+# these formats can never be empty files.
+
+CREATEPROTO=unixproto
+EMPTYPROTO=unixproto
+
+
+# Commands possibly overriden by the individual port
+
+ARRC=ar rc
+CC=cc
+LN=ln -s
+RANLIB=ranlib
+
+
+# Standard distribution build parameters
+
+DEFAULTAUTHENTICATORS=ext md5 pla log
+#
+# mh needs to be after any other directory format drivers (such as mx or mix)
+# since otherwise mh will seize any directory that is under the mh path.
+# However, mh needs to be before any sysinbox formats (such as mmdf or unix)
+# since otherwise INBOX won't work correctly when mh_allow_inbox is set.
+#
+DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile
+CHUNKSIZE=65536
+
+# Normally no need to change any of these
+
+ARCHIVE=c-client.a
+BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \
+ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \
+ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \
+ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o
+CFLAGS=-g
+
+CAT=cat
+MAKE=make
+MV=mv
+RM=rm -rf
+SH=sh
+
+
+# Primary build command
+
+BUILD=$(MAKE) build EXTRACFLAGS='$(EXTRACFLAGS)'\
+ EXTRALDFLAGS='$(EXTRALDFLAGS)'\
+ EXTRADRIVERS='$(EXTRADRIVERS)' EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\
+ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)
+
+
+# Here if no make argument established
+
+missing: osdep.h
+ $(MAKE) all `$(CAT) SPECIALS`
+
+osdep.h:
+ @echo You must specify what type of system
+ @false
+
+
+# Current ports
+
+a32: # AIX 3.2 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD" \
+ BASELDFLAGS="-lbsd"
+
+a41: # AIX 4.1 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD -qro -qroconst" \
+ BASELDFLAGS="-ls"
+
+a52: # Attempt at AIX 5.2
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD -qro -qroconst" \
+ BASELDFLAGS="-ls"
+
+aix: # AIX/370
+ @echo You are building for AIX on an S/370 class machine
+ @echo If you want AIX on an RS/6000 you need to use a32 or a41 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g" \
+ BASELDFLAGS="-lbsd"
+
+aos: # AOS for RT
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+art: # AIX 2.2.1 for RT
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/control/active \
+ RSHPATH=/bin/rsh \
+ BASECFLAGS="-g -Dconst= -Dvoid=char" \
+ RANLIB=true
+
+asv: # Altos SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 LOGINPW=old \
+ ACTIVEFILE=/usr/spool/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-Dconst= -DSIGSTOP=SIGKILL" \
+ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \
+ RANLIB=true
+
+aux: # A/UX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ BASECFLAGS="-g -B/usr/lib/big/ -Dvoid=char -Dconst=" \
+ RANLIB=true ARRC="ar -rc"
+
+bs3: # BSD/i386 3.0 or higher
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ CHECKPW=bsi LOGINPW=bsi CRXTYPE=nfs \
+ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \
+ ACTIVEFILE=/var/news/etc/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" CC=shlicc
+
+bsd: # BSD UNIX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+bsf: # FreeBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+bsi: # BSD/i386
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \
+ ACTIVEFILE=/var/news/etc/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+bso: # OpenBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+cvx: # Convex
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-O -ext -Dconst="
+
+cyg: # Cygwin - note that most local file drivers don't work!!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \
+ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt" \
+ CC=gcc
+
+d-g: # Data General DG/UX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/local/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lnsl -lsocket" \
+ RANLIB=true
+
+d54: # Data General DG/UX 5.4
+ $(BUILD) `$(CAT) SPECIALS` OS=d-g \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/local/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lnsl -lsocket" \
+ RANLIB=true
+
+dpx: # Bull DPX/2
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT" \
+ BASELDFLAGS="-linet" \
+ RANLIB=true LN=ln
+
+drs: # ICL DRS/NX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-O" \
+ BASELDFLAGS="-lsocket -lgen" \
+ RANLIB=true
+
+do4: # Apollo Domain/OS sr10.4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-A systype,bsd4.3 -D_APOLLO_SOURCE" \
+ RANLIB=true
+
+dyn: # Dynix
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+epx: # EP/IX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/net/rsh \
+ BASECFLAGS="-g -systype svr4" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+ga4: # GCC AIX 4.1 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=a41 \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD" \
+ BASELDFLAGS="-ls"
+
+gas: # GCC Altos SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=asv \
+ SIGTYPE=sv4 LOGINPW=old \
+ ACTIVEFILE=/usr/spool/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-g -O -DALTOS_SYSTEM_V -DSIGSTOP=SIGKILL" \
+ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \
+ RANLIB=true CC=gcc
+
+gh9: # GCC HP-UX 9.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+ghp: # GCC HP-UX 10.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+ghs: # GCC HP-UX with Trusted Computer Base
+ $(BUILD) `$(CAT) SPECIALS` OS=shp \
+ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lnet -lV3 -lsec" \
+ RANLIB=true CC=gcc
+
+go5: # GCC 2.7.1 (95q4) SCO Open Server 5.0.x
+ $(BUILD) `$(CAT) SPECIALS` OS=sc5 \
+ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="$(GCCCFLAGS) -I/usr/include -L/lib" \
+ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \
+ RANLIB=true CC=gcc
+
+gsc: # Santa Cruz Operation
+ $(BUILD) `$(CAT) SPECIALS` OS=sco \
+ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \
+ RANLIB=true LN=ln CC=gcc
+
+gsg: # GCC Silicon Graphics
+ $(BUILD) `$(CAT) SPECIALS` OS=sgi \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+gso: os_sol.h # GCC Solaris
+ $(BUILD) `$(CAT) SPECIALS` OS=sol \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=gcc
+
+gsu: # GCC SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=sun \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-ldl" \
+ CC=gcc
+
+gul: # GCC Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=ult \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lauth -lc" \
+ CC=gcc
+
+h11: # HP-UX 11i
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ RANLIB=true
+
+hpp: # HP-UX 9.x
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Aa -D_HPUX_SOURCE" \
+ BASELDFLAGS="-lnet -lV3" \
+ RANLIB=true
+
+hpx: # HP-UX 10.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ BASELDFLAGS="-lnet -lV3" \
+ RANLIB=true
+
+isc: # Interactive
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/spool/news/active \
+ BASECFLAGS="-Xp -D_SYSV3" \
+ BASELDFLAGS="-linet -lnsl_s -lgen -lx -lsec -liberty" \
+ RANLIB=true
+
+lnp: # Linux Pluggable Authentication modules
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=pam CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="$(PAMLDFLAGS)"
+
+lnx: # Linux non-shadow passwords
+ @echo You are building for traditional Linux *without* shadow
+ @echo passwords and with the crypt function in the C library.
+ @echo If your system has shadow passwords, or if crypt is not
+ @echo in the C library, you must use slx, sl4, or sl5 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+lyn: # LynxOS
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ RSHPATH=/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS=-lbsd \
+ CC=gcc
+
+mct: # MachTen - CRXTYPE=nfs doesn't work (at least not on 2.2)
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+mnt: # Mint
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+neb: # NetBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/db/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+nec: # NEC UX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Kopt=2 -KOlimit=2000" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=/usr/abiccs/bin/cc
+
+nto: # QNX Neutrino RTP
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O"
+
+nxt: # NEXTSTEP
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+nx3: # NEXTSTEP 3.0 single threaded
+ $(BUILD) `$(CAT) SPECIALS` OS=nxt \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)"
+ echo "void malloc_singlethreaded (void);" >> linkage.h
+ echo " malloc_singlethreaded ();" >> linkage.c
+
+osf: # OSF/1
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="-g3 -w -O2 -Olimit 1500"
+
+# Note: sia_become_user() used by LOGINPW=os4 doesn't seem to work right. The
+# user doesn't get proper file access, and the process can't be killed.
+
+os4: # OSF/1 (Digital UNIX) 4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=os4 LOGINPW=os4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="-g3 -w -std0 -O2"
+
+osx: # Mac OS X
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCC4CFLAGS)"
+
+ptx: # PTX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=svo LOGINPW=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-Wc,-O3 -Wc,-seq -Dprivate=PRIVATE" \
+ BASELDFLAGS="-lseq -lsec -lsocket -linet -lnsl -lgen" \
+ RANLIB=true
+
+pyr: # Pyramid
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+qnx: # QNX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CHECKPW=psx LOGINPW=old \
+ RSHPATH=/usr/ucb/rsh \
+ BASECFLAGS="-Otax -g -Dunix=1 -D_POSIX_SOURCE" \
+ BASELDFLAGS="-g -N128k -llogin -lsocket -lunix"
+
+s40: # SUN-OS 4.0
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+sc5: # SCO Open Server 5.0
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-O3 -s -belf" \
+ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \
+ RANLIB=true
+
+sco: # Santa Cruz Operation
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-O3" \
+ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \
+ RANLIB=true LN=ln
+
+# Note: setting _POSIX_SOURCE doesn't seem to build it as of SGI IRIX 5.3
+
+sgi: # Silicon Graphics
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="-g3 -O2 -Olimit 8192" \
+ RANLIB=true
+
+sg6: # Silicon Graphics, IRIX 6.5
+ MAKEFLAGS= $(BUILD) `$(CAT) SPECIALS` OS=sgi \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="-g3 -O2 -OPT:Olimit=0 -woff 1110,1116" \
+ RANLIB=true
+
+# Note: Mark Kaesling says that setluid() isn't in HP-UX with SecureWare.
+
+shp: # HP-UX with Trusted Computer Base
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ BASELDFLAGS="-lnet -lV3 -lsec" \
+ RANLIB=true
+
+slx: # Secure Linux
+ @echo You are building for libc6/glibc versions of Secure Linux
+ @echo If you want libc5 versions you must use sl5 instead!
+ @echo If you want libc4 versions you must use sl4 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+sl4: # Secure Linux using libc4
+ @echo You are building for libc4 versions of Secure Linux
+ @echo If you want libc6/glibc versions you must use slx instead!
+ @echo If you want libc5 versions you must use sl5 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lshadow"
+
+sl5: # Secure Linux using libc5
+ @echo You are building for libc5 versions of Secure Linux
+ @echo If you want libc6/glibc versions you must use slx instead!
+ @echo If you want libc4 versions you must use sl4 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+snx: # Siemens Nixdorf SINIX and Reliant UNIX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=psx CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -D_SYS_CLOCK_H -Dconst=" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+# Sorry about the -w, but the cretinous SUN Workshop Pro C compiler barfs on
+# implicit casts between char and unsigned char.
+
+soc: os_sol.h # Solaris with cc
+ $(BUILD) `$(CAT) SPECIALS` OS=sol \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O -w" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=/opt/SUNWspro/bin/cc
+
+
+# Note: It is a long and disgusting story about why cc is set to ucbcc. You
+# need to invoke the C compiler so that it links with the SVR4 libraries and
+# not the BSD libraries, otherwise readdir() will return the wrong information.
+# Of all the names in the most common path, ucbcc is the only name to be found
+# (on /usr/ccs/bin) that points to a suitable compiler. cc is likely to be
+# /usr/ucb/cc which is absolutely not the compiler that you want. The real
+# SVR4 cc is probably something like /opt/SUNWspro/bin/cc which is rarely in
+# anyone's path.
+#
+# ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc, and is
+# the UCB C compiler using the SVR4 libraries.
+#
+# If ucbcc isn't on your system, then punt on the SUN C compiler and use gcc
+# instead (the gso port instead of the sol port).
+#
+# If, in spite of all the above warnings, you choose to use the "soc" port
+# instead of the "sol" port, be sure to check the behavior of the LIST command
+# in imapd. Also, note that the "soc" port uses -O. If you want to use the
+# real SVR4 compiler, you must use -O. If it works to compile with -O2, then
+# cc is probably using the UCB compiler with BSD libraries, and will not build
+# a good binary
+#
+# To recap:
+# 1) The sol port is designed to be built using the UCB compiler using the
+# SVR4 libraries. This compiler is "ucbcc", which is lunk to acc. You
+# use -O2 as one of the CFLAGS.
+# 2) If you build the sol port with the UCB compiler using the BSD libraries,
+# you will get no error messages but you will get bad binaries (the most
+# obvious symptom is dropping the first two characters return filenames
+# from the imapd LIST command. This compiler also uses -O2, and is very
+# often what the user gets from "cc". BEWARE!!!
+# 3) If you build the sol port with the real SVR4 compiler, which is often
+# hidden away or unavailable on many systems, then you will get errors
+# from -O2 and you need to change that to -O. But you will get a good
+# binary. However, you should try it with -O2 first, to make sure that
+# you got this compiler and not the UCB compiler using BSD libraries.
+
+sol: os_sol.h # Solaris
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O2" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=ucbcc
+
+sos: # Secure OSF/1
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sce LOGINPW=sec CRXTYPE=nfs \
+ BASECFLAGS="-g3 -w -O2 -Olimit 1500" \
+ BASELDFLAGS="-lsecurity -laud"
+
+ssn: # Secure SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=sun \
+ CHECKPW=ssn CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-ldl"
+
+sua: # Windows Vista Subsystem for UNIX Applications
+ $(BUILD) `$(CAT) SPECIALS` OS=sua \
+ SIGTYPE=psx CRXTYPE=nfs LOGINPW=old \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O2" \
+ BASELDFLAGS="-lcrypt"
+
+sun: # SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-ldl"
+
+sv2: # SVR2
+ @echo You are being *very* optimistic!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 LOGINPW=old \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT -I/usr/ethernet/include" \
+ BASELDFLAGS="-lnet" \
+ RANLIB=true LN=ln
+
+sv4: # SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+ult: # Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="-g3 -O2 -Olimit 1500 -Dconst=" \
+ BASELDFLAGS="-lauth -lc"
+
+uw2: # UnixWare SVR4.2
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+vul: # VAX Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=ult \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="-O2 -Dconst=" \
+ BASELDFLAGS="-lauth -lc"
+
+vu2: # VAX Ultrix 2.3, etc.
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-O2 -Dconst= -Dvoid=char"
+
+
+# Build it!
+
+build: clean once $(ARCHIVE)
+
+all: $(ARCHIVE)
+
+$(ARCHIVE): $(BINARIES)
+ sh -c '$(RM) $(ARCHIVE) || true'
+ @$(CAT) ARCHIVE
+ @$(SH) ARCHIVE
+
+.c.o:
+ `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` $*.c
+
+
+# Cleanup
+
+clean:
+ sh -c '$(RM) auths.c crexcl.c ip_unix.c linkage.[ch] siglocal.c osdep*.[ch] *.o ARCHIVE *FLAGS *TYPE $(ARCHIVE) || true'
+
+
+# Dependencies
+
+dummy.o: mail.h misc.h osdep.h dummy.h
+fdstring.o: mail.h misc.h osdep.h fdstring.h
+flstring.o: mail.h misc.h osdep.h flstring.h
+imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h
+mail.o: mail.h misc.h osdep.h rfc822.h linkage.h
+mbx.o: mail.h misc.h osdep.h dummy.h
+mh.o: mail.h misc.h osdep.h dummy.h
+mix.o: mail.h misc.h osdep.h dummy.h
+mx.o: mail.h misc.h osdep.h dummy.h
+misc.o: mail.h misc.h osdep.h
+mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h
+mtx.o: mail.h misc.h osdep.h dummy.h
+netmsg.o: mail.h misc.h osdep.h netmsg.h
+news.o: mail.h misc.h osdep.h
+newsrc.o: mail.h misc.h osdep.h newsrc.h
+nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h
+phile.o: mail.h misc.h osdep.h rfc822.h dummy.h
+pseudo.o: pseudo.h
+pop3.o: mail.h misc.h osdep.h rfc822.h
+smanager.o: mail.h misc.h osdep.h
+smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h
+rfc822.o: mail.h misc.h osdep.h rfc822.h
+tenex.o: mail.h misc.h osdep.h dummy.h
+unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h
+utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c
+utf8aux.o: mail.h misc.h osdep.h utf8.h
+
+
+# OS-dependent
+
+osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \
+ osdep.h env_unix.h tcp_unix.h \
+ osdep.c env_unix.c fs_unix.c ftl_unix.c nl_unix.c tcp_unix.c ip_unix.c\
+ auths.c crexcl.c flockcyg.c flocklnx.c flocksim.c fsync.c \
+ gethstid.c getspnam.c \
+ gr_wait.c gr_wait4.c gr_waitp.c \
+ kerb_mit.c \
+ auth_ext.c auth_gss.c auth_log.c auth_md5.c auth_pla.c \
+ pmatch.c scandir.c setpgrp.c strerror.c truncate.c write.c \
+ memmove.c memmove2.c memset.c \
+ tz_bsd.c tz_nul.c tz_sv4.c \
+ write.c sslstdio.c \
+ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \
+ OSCFLAGS
+ @echo Building OS-dependent module
+ @echo If you get No such file error messages for files x509.h, ssl.h,
+ @echo pem.h, buffer.h, bio.h, and crypto.h, that means that OpenSSL
+ @echo is not installed on your system. Either install OpenSSL first
+ @echo or build with command: make `$(CAT) OSTYPE` SSLTYPE=none
+ `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` `$(CAT) OSCFLAGS` -c osdep.c
+
+osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c
+ $(CAT) osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c
+
+osdepbas.c:
+ @echo osdepbas.c not found...try make clean and new make
+ @false
+
+osdepckp.c:
+ @echo osdepckp.c not found...try make clean and new make
+ @false
+
+osdeplog.c:
+ @echo osdeplog.c not found...try make clean and new make
+ @false
+
+osdepssl.c:
+ @echo osdepssl.c not found...try make clean and new make
+ @false
+
+siglocal.c:
+ @echo siglocal.c not found...try make clean and new make
+ @false
+
+crexcl.c:
+ @echo crexcl.c not found...do make clean and new make
+ @false
+
+ip_unix.c:
+ @echo ip_unix.c not found...do make clean and new make
+ @false
+
+os_sol.h:
+ sh -c 'if [ -f /lib/libc.a ]; then (strings /lib/libc.a | grep getpassphrase > /dev/null) && $(LN) os_soln.h os_sol.h || $(LN) os_solo.h os_sol.h ; else $(LN) os_soln.h os_sol.h ; fi'
+
+
+# Once-only environment setup
+
+once: onceenv ckp$(PASSWDTYPE) ssl$(SSLTYPE) osdep.c
+
+onceenv:
+ @echo Once-only environment setup...
+ echo $(CC) > CCTYPE
+ echo $(BASECFLAGS) '$(EXTRACFLAGS)' -DCHUNKSIZE=$(CHUNKSIZE) > CFLAGS
+ echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \
+ -DMD5ENABLE=\"$(MD5PWD)\" -DMAILSPOOL=\"$(MAILSPOOL)\" \
+ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" \
+ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \
+ -DRSHPATH=\"$(RSHPATH)\" -DLOCKPGM=\"$(LOCKPGM)\" \
+ -DLOCKPGM1=\"$(LOCKPGM1)\" -DLOCKPGM2=\"$(LOCKPGM2)\" \
+ -DLOCKPGM3=\"$(LOCKPGM3)\" > OSCFLAGS
+ echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS
+ echo "$(ARRC) $(ARCHIVE) $(BINARIES);$(RANLIB) $(ARCHIVE)" > ARCHIVE
+ echo $(OS) > OSTYPE
+ ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy
+ ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ echo " mail_versioncheck (CCLIENTVERSION);" >> linkage.c
+ $(LN) os_$(OS).h osdep.h
+ $(LN) os_$(OS).c osdepbas.c
+ $(LN) log_$(LOGINPW).c osdeplog.c
+ $(LN) sig_$(SIGTYPE).c siglocal.c
+ $(LN) crx_$(CRXTYPE).c crexcl.c
+ $(LN) ip$(IP)_unix.c ip_unix.c
+ sh -c '(test $(OS) = sc5 -o $(OS) = sco -o ! -f /usr/include/sys/statvfs.h) && echo -DNOFSTATVFS >> OSCFLAGS || fgrep statvfs64 /usr/include/sys/statvfs.h > /dev/null || echo -DNOFSTATVFS64 >> OSCFLAGS'
+
+
+# Password checkers
+
+ckpafs: # AFS
+ @echo AFS password authentication
+ echo $(AFSCFLAGS) >> OSCFLAGS
+# echo $(AFSLDFLAGS) >> LDFLAGS
+# Note: Steve Roseman says that AFS libraries have to be lunk before SSL
+ echo $(AFSLDFLAGS) `$(CAT) LDFLAGS` > LDFLAGS.tmp
+ mv LDFLAGS.tmp LDFLAGS
+ $(LN) ckp_afs.c osdepckp.c
+
+ckpdce: # DCE
+ @echo DCE password authentication
+ echo $(DCECFLAGS) >> OSCFLAGS
+ echo $(DCELDFLAGS) >> LDFLAGS
+ $(LN) ckp_dce.c osdepckp.c
+
+ckpgss: # Kerberos V (must have gss EXTRAAUTHENTICATOR as well)
+ @echo Kerberos V password authentication
+ $(LN) ckp_gss.c osdepckp.c
+
+ckpnul: # NUL authenticator (disables all plaintext authentication)
+ @echo Plaintext authentication prohibited
+ echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 1);" >> linkage.c
+ $(LN) ckp_nul.c osdepckp.c
+
+ckppam: # Pluggable Authentication Modules authenticator
+ @echo PAM password authentication
+ echo $(PAMLDFLAGS) >> LDFLAGS
+ $(LN) ckp_pam.c osdepckp.c
+
+ckppmb: # Broken (e.g. SUN) Pluggable Authentication Modules authenticator
+ @echo Broken PAM password authentication
+ echo $(PAMLDFLAGS) >> LDFLAGS
+ $(LN) ckp_pmb.c osdepckp.c
+
+ckpstd: # Port standard
+ @echo Standard password authentication
+ $(LN) ckp_$(CHECKPW).c osdepckp.c
+
+ckptwo: # Something plus standard
+ @echo $(CHECKPWALT) password authentication first, then standard
+ $(CAT) ckp_1st.c ckp_$(CHECKPWALT).c ckp_2nd.c ckp_$(CHECKPW).c \
+ ckp_3rd.c > osdepckp.c
+
+
+# SSL support
+
+sslnone:# No SSL
+ @echo Building without SSL support
+ $(LN) ssl_none.c osdepssl.c
+
+sslnopwd: sslunix snopwd
+
+sslunix.nopwd: sslnopwd
+
+sslsco.nopwd: sslsco snopwd
+
+sslunix: sbasic sldunix
+
+sslsco: sbasic sldsco
+
+sbasic: # UNIX OpenSSL
+ @echo Building with SSL
+ $(LN) ssl_unix.c osdepssl.c
+ echo $(SSLCFLAGS) >> OSCFLAGS
+ echo " ssl_onceonlyinit ();" >> linkage.c
+
+snopwd: # Plaintext disable
+ @echo Building with SSL and plaintext passwords disabled unless SSL/TLS
+ echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);" >> linkage.c
+
+sldunix:# Normal UNIX SSL load flags
+ echo $(SSLLDFLAGS) >> LDFLAGS
+
+
+sldsco: # SCO SSL load flags
+# Note: Tim Rice says that SSL has to be lunk before other libraries on SCO.
+ echo $(SSLLDFLAGS) `cat LDFLAGS` > LDFLAGS.tmp
+ mv LDFLAGS.tmp LDFLAGS
+
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/unix/Makefile.gss b/imap/src/osdep/unix/Makefile.gss
new file mode 100644
index 00000000..440647e9
--- /dev/null
+++ b/imap/src/osdep/unix/Makefile.gss
@@ -0,0 +1,39 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: GSSAPI makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 4 April 2007
+
+
+# Extended flags needed for additional authenticators. You may need to modify.
+
+GSSDIR=/usr/local
+GSSINCLUDE=$(GSSDIR)/include
+GSSLIB=$(GSSDIR)/lib
+GSSCFLAGS= -I$(GSSINCLUDE) -DGSS_C_NT_HOSTBASED_SERVICE=gss_nt_service_name -DKRB5_DEPRECATED=1
+GSSOLDLDFLAGS= -L$(GSSLIB) -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err
+GSSNEWLDFLAGS= -L$(GSSLIB) -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err
+
+gss: # GSSAPI Kerberos V flags
+ echo $(GSSCFLAGS) >> OSCFLAGS
+ sh -c '(test -f $(GSSLIB)/libk5crypto.a) && echo $(GSSNEWLDFLAGS) || echo $(GSSOLDLDFLAGS)' >> LDFLAGS
+ echo "#include \"kerb_mit.c\"" >> auths.c
diff --git a/imap/src/osdep/unix/ckp_1st.c b/imap/src/osdep/unix/ckp_1st.c
new file mode 100644
index 00000000..d7769b9d
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_1st.c
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 1
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+struct passwd *checkpw_alt(struct passwd *pw,char *pass,int argc,char *argv[]);
+struct passwd *checkpw_std(struct passwd *pw,char *pass,int argc,char *argv[]);
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct passwd *ret;
+ /* in case first checker smashes it */
+ char *user = cpystr (pw->pw_name);
+ if (!(ret = checkpw_alt (pw,pass,argc,argv)))
+ ret = checkpw_std (getpwnam (user),pass,argc,argv);
+ fs_give ((void **) &user);
+ return ret;
+}
+
+/* Redefine alt checker's routine name */
+
+#define checkpw checkpw_alt
diff --git a/imap/src/osdep/unix/ckp_2nd.c b/imap/src/osdep/unix/ckp_2nd.c
new file mode 100644
index 00000000..5207b5d6
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_2nd.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Undefine routine name */
+
+#undef checkpw
+
+/* Redefine std checker's routine name */
+
+#define checkpw checkpw_std
diff --git a/imap/src/osdep/unix/ckp_3rd.c b/imap/src/osdep/unix/ckp_3rd.c
new file mode 100644
index 00000000..c441771a
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_3rd.c
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 3
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Undefine routine name */
+
+#undef checkpw
diff --git a/imap/src/osdep/unix/ckp_a41.c b/imap/src/osdep/unix/ckp_a41.c
new file mode 100644
index 00000000..3018c24c
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_a41.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: AIX 4.1 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <login.h>
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ int reenter = 0;
+ char *msg = NIL;
+ char *user = cpystr (pw->pw_name);
+ /* validate password */
+ struct passwd *ret = (pw && !loginrestrictions (user,S_RLOGIN,NIL,&msg) &&
+ !authenticate (user,pass,&reenter,&msg)) ?
+ getpwnam (user) : NIL;
+ /* clean up any message returned */
+ if (msg) fs_give ((void **) &msg);
+ if (user) fs_give ((void **) &user);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_afs.c b/imap/src/osdep/unix/ckp_afs.c
new file mode 100644
index 00000000..2f3fa6ba
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_afs.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: AFS check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* AFS cleanup
+ * Accepts: data
+ */
+
+void checkpw_cleanup (void *data)
+{
+ ktc_ForgetAllTokens ();
+}
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#undef INIT
+#define min AFS_MIN
+#define max AFS_MAX
+#include <afs/param.h>
+#include <afs/kautils.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char *reason;
+ /* faster validation for POP servers */
+ if (!strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),"pop")) {
+ struct ktc_encryptionKey key;
+ struct ktc_token token;
+ /* just check the password */
+ ka_StringToKey (pass,NIL,&key);
+ if (ka_GetAdminToken (pw->pw_name,"","",&key,600,&token,1)) return NIL;
+ }
+ /* check password and get AFS token */
+ else if (ka_UserAuthenticateGeneral
+ (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,pw->pw_name,NIL,NIL,
+ pass,0,0,0,&reason)) return NIL;
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_bsi.c b/imap/src/osdep/unix/ckp_bsi.c
new file mode 100644
index 00000000..128f23ab
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_bsi.c
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSI check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#include <login_cap.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char *s,tmp[MAILTMPLEN];
+ login_cap_t *lc;
+ /* make service name */
+ sprintf (tmp,"auth-%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL));
+ /* validate password */
+ return ((lc = login_getclass (pw->pw_class)) &&
+ (s = login_getstyle (lc,NIL,tmp)) &&
+ (auth_response (pw->pw_name,lc->lc_class,s,"response",NIL,"",pass)
+ > 0)) ? pw : NIL;
+}
diff --git a/imap/src/osdep/unix/ckp_cyg.c b/imap/src/osdep/unix/ckp_cyg.c
new file mode 100644
index 00000000..28e07394
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_cyg.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Cygwin check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* This module is against my better judgement. If you want to run an imapd
+ * or ipop[23]d you should use the native NT or W2K ports intead of this
+ * Cygwin port. There is no surety that this module works right or will work
+ * right in the future.
+ */
+
+#undef ERROR
+#define NOCRYPT
+#include <windows.h>
+#include <sys/cygwin.h>
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+static char *cyg_user = NIL;
+static HANDLE cyg_hdl = NIL;
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ /* flush last pw-checked user */
+ if (cyg_user) fs_give ((void **) &cyg_user);
+ /* forbid if UID 0 or SYSTEM uid */
+ if (!pw->pw_uid || (pw->pw_uid == SYSTEMUID) ||
+ ((cyg_hdl = cygwin_logon_user (pw,pass)) == INVALID_HANDLE_VALUE))
+ return NIL; /* bad UID or password */
+ /* remember user for this handle */
+ cyg_user = cpystr (pw->pw_name);
+ return pw;
+}
+
diff --git a/imap/src/osdep/unix/ckp_dce.c b/imap/src/osdep/unix/ckp_dce.c
new file mode 100644
index 00000000..2a3aed0e
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_dce.c
@@ -0,0 +1,83 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DCE check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#include <dce/rpc.h>
+#include <dce/sec_login.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ sec_passwd_rec_t pwr;
+ sec_login_handle_t lhdl;
+ boolean32 rstpwd;
+ sec_login_auth_src_t asrc;
+ error_status_t status;
+ FILE *fd;
+ /* easy case */
+ if (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) return pw;
+ /* try DCE password cache file */
+ if (fd = fopen (PASSWD_OVERRIDE,"r")) {
+ char *usr = cpystr (pw->pw_name);
+ while ((pw = fgetpwent (fd)) && strcmp (usr,pw->pw_name));
+ fclose (fd); /* finished with cache file */
+ /* validate cached password */
+ if (pw && pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ fs_give ((void **) &usr);
+ return pw;
+ }
+ if (!pw) pw = getpwnam (usr);
+ fs_give ((void **) &usr);
+ }
+ if (pw) { /* try S-L-O-W DCE... */
+ sec_login_setup_identity ((unsigned_char_p_t) pw->pw_name,
+ sec_login_no_flags,&lhdl,&status);
+ if (status == error_status_ok) {
+ pwr.key.tagged_union.plain = (idl_char *) pass;
+ pwr.key.key_type = sec_passwd_plain;
+ pwr.pepper = NIL;
+ pwr.version_number = sec_passwd_c_version_none;
+ /* validate password with login context */
+ sec_login_validate_identity (lhdl,&pwr,&rstpwd,&asrc,&status);
+ if (!rstpwd && (asrc == sec_login_auth_src_network) &&
+ (status == error_status_ok)) {
+ sec_login_purge_context (&lhdl,&status);
+ if (status == error_status_ok) return pw;
+ }
+ }
+ }
+ return NIL; /* password validation failed */
+}
diff --git a/imap/src/osdep/unix/ckp_gss.c b/imap/src/osdep/unix/ckp_gss.c
new file mode 100644
index 00000000..0ef9388b
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_gss.c
@@ -0,0 +1,90 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Kerberos 5 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 11 October 2007
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char svrnam[MAILTMPLEN],cltnam[MAILTMPLEN];
+ krb5_context ctx;
+ krb5_timestamp now;
+ krb5_principal service;
+ krb5_ccache ccache;
+ krb5_error_code code;
+ krb5_creds *crd = (krb5_creds *) memset (fs_get (sizeof (krb5_creds)),0,
+ sizeof (krb5_creds));
+ struct passwd *ret = NIL;
+ if (*pass) { /* only if password non-empty */
+ /* make service name */
+ sprintf (svrnam,"%.80s@%.512s",
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ tcp_serverhost ());
+ /* make client name with principal */
+ sprintf (cltnam,"%.80s/%.80s",pw->pw_name,
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL));
+ /* get a context */
+ if (!krb5_init_context (&ctx)) {
+ /* get time, client and server principals */
+ if (!krb5_timeofday (ctx,&now) &&
+ /* Normally, kerb_cp_svr_name (defined/set in env_unix.c) is NIL, so
+ * only the user name is used as a client principal. A few sites want
+ * to have separate client principals for different services, but many
+ * other sites vehemently object...
+ */
+ !krb5_parse_name (ctx,kerb_cp_svr_name ? cltnam : pw->pw_name,
+ &crd->client) &&
+ !krb5_parse_name (ctx,svrnam,&service) &&
+ !krb5_build_principal_ext(ctx,&crd->server,
+ krb5_princ_realm (ctx,crd->client)->length,
+ krb5_princ_realm (ctx,crd->client)->data,
+ KRB5_TGS_NAME_SIZE,KRB5_TGS_NAME,
+ krb5_princ_realm (ctx,crd->client)->length,
+ krb5_princ_realm (ctx,crd->client)->data,
+ 0)) {
+ /* expire in 3 minutes */
+ crd->times.endtime = now + (3 * 60);
+ if (krb5_cc_resolve (ctx,"MEMORY:pwk",&ccache) ||
+ krb5_cc_initialize (ctx,ccache,crd->client)) ccache = 0;
+ if (!krb5_get_in_tkt_with_password (ctx,NIL,NIL,NIL,NIL,pass,ccache,
+ crd,0) &&
+ !krb5_verify_init_creds (ctx,crd,service,0,ccache ? &ccache : 0,0))
+ ret = pw;
+ krb5_free_creds (ctx,crd);/* flush creds and service principal */
+ krb5_free_principal (ctx,service);
+ }
+ krb5_free_context (ctx); /* don't need context any more */
+ }
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_nul.c b/imap/src/osdep/unix/ckp_nul.c
new file mode 100644
index 00000000..153ee0ed
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_nul.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Null check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 July 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/unix/ckp_os4.c b/imap/src/osdep/unix/ckp_os4.c
new file mode 100644
index 00000000..765bb146
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_os4.c
@@ -0,0 +1,78 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OSF/1 (Digital UNIX) 4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Dummy collection routine
+ * Accepts: how long to wait for user
+ * how to run parameter collection
+ * title
+ * number of prompts
+ * prompts
+ * Returns: collection status
+ *
+ * Because Spider Boardman, who wrote SIA, says that it's needed for buggy SIA
+ * mechanisms, that's why.
+ */
+
+static int checkpw_collect (int timeout,int rendition,uchar_t *title,
+ int nprompts,prompt_t *prompts)
+{
+ switch (rendition) {
+ case SIAONELINER: case SIAINFO: case SIAWARNING: return SIACOLSUCCESS;
+ }
+ return SIACOLABORT; /* another else is bogus */
+}
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ int i;
+ char *s;
+ char *name = cpystr (pw->pw_name);
+ char *host = cpystr (tcp_clienthost ());
+ struct passwd *ret = NIL;
+ /* tie off address */
+ if (s = strchr (host,' ')) *s = '\0';
+ if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */
+ memmove (host,host+1,i = strlen (host + 2));
+ host[i] = '\0';
+ }
+ /* validate password */
+ if (sia_validate_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,pass)
+ == SIASUCCESS) ret = getpwnam (name);
+ fs_give ((void **) &name);
+ fs_give ((void **) &host);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_pam.c b/imap/src/osdep/unix/ckp_pam.c
new file mode 100644
index 00000000..60c6c1f7
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_pam.c
@@ -0,0 +1,136 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pluggable Authentication Modules login services
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 August 2006
+ */
+
+
+#ifdef MAC_OSX_KLUDGE /* why can't Apple be compatible? */
+#include <pam/pam_appl.h>
+#else
+#include <security/pam_appl.h>
+#endif
+
+struct checkpw_cred {
+ char *uname; /* user name */
+ char *pass; /* password */
+};
+
+/* PAM conversation function
+ * Accepts: number of messages
+ * vector of messages
+ * pointer to response return
+ * application data
+ * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR
+ */
+
+static int checkpw_conv (int num_msg,const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ struct checkpw_cred *cred = (struct checkpw_cred *) appdata_ptr;
+ struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg);
+ for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_ON: /* assume want user name */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (cred->uname);
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* assume want password */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (cred->pass);
+ break;
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = NULL;
+ break;
+ default: /* unknown message style */
+ fs_give ((void **) &reply);
+ return PAM_CONV_ERR;
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+
+/* PAM cleanup
+ * Accepts: handle
+ */
+
+static void checkpw_cleanup (pam_handle_t *hdl)
+{
+#if 0 /* see checkpw() for why this is #if 0 */
+ pam_close_session (hdl,NIL); /* close session [uw]tmp */
+#endif
+ pam_setcred (hdl,PAM_DELETE_CRED);
+ pam_end (hdl,PAM_SUCCESS);
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * Returns: T if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ pam_handle_t *hdl;
+ struct pam_conv conv;
+ struct checkpw_cred cred;
+ char *name = cpystr (pw->pw_name);
+ conv.conv = &checkpw_conv;
+ conv.appdata_ptr = &cred;
+ cred.uname = name;
+ cred.pass = pass;
+ if (pw = ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ pw->pw_name,&conv,&hdl) == PAM_SUCCESS) &&
+ (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) == PAM_SUCCESS) &&
+ (pam_authenticate (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_acct_mgmt (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_setcred (hdl,PAM_ESTABLISH_CRED) == PAM_SUCCESS)) ?
+ getpwnam (name) : NIL) {
+#if 0
+ /*
+ * Some people have reported that this causes a SEGV in strncpy() from
+ * pam_unix.so.1
+ */
+ /*
+ * This pam_open_session() call is inconsistant with how we handle other
+ * platforms, where we don't write [uw]tmp records. However, unlike our
+ * code on other platforms, pam_acct_mgmt() will check those records for
+ * inactivity and deny the authentication.
+ */
+ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */
+#endif
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl);
+ }
+ else checkpw_cleanup (hdl); /* clean up */
+ fs_give ((void **) &name);
+ /* reset log facility in case PAM broke it */
+ if (myServerName) openlog (myServerName,LOG_PID,syslog_facility);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_pmb.c b/imap/src/osdep/unix/ckp_pmb.c
new file mode 100644
index 00000000..c8a935cd
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_pmb.c
@@ -0,0 +1,128 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pluggable Authentication Modules login services, buggy systems
+ * (use this instead of ckp_pam.c on Solaris)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 August 2006
+ */
+
+
+#include <security/pam_appl.h>
+
+static char *pam_uname; /* user name */
+static char *pam_pass; /* password */
+
+/* PAM conversation function
+ * Accepts: number of messages
+ * vector of messages
+ * pointer to response return
+ * application data
+ * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR
+ */
+
+static int checkpw_conv (int num_msg,const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg);
+ for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_ON: /* assume want user name */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (pam_uname);
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* assume want password */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (pam_pass);
+ break;
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = NULL;
+ break;
+ default: /* unknown message style */
+ fs_give ((void **) &reply);
+ return PAM_CONV_ERR;
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+
+/* PAM cleanup
+ * Accepts: handle
+ */
+
+static void checkpw_cleanup (pam_handle_t *hdl)
+{
+#if 0 /* see checkpw() for why this is #if 0 */
+ pam_close_session (hdl,NIL); /* close session [uw]tmp */
+#endif
+ pam_setcred (hdl,PAM_DELETE_CRED);
+ pam_end (hdl,PAM_SUCCESS);
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * Returns: T if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ pam_handle_t *hdl;
+ struct pam_conv conv;
+ char *name = cpystr (pw->pw_name);
+ conv.conv = &checkpw_conv;
+ pam_uname = pw->pw_name;
+ pam_pass = pass;
+ if (pw = ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ pw->pw_name,&conv,&hdl) == PAM_SUCCESS) &&
+ (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) == PAM_SUCCESS) &&
+ (pam_authenticate (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_acct_mgmt (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_setcred (hdl,PAM_ESTABLISH_CRED) == PAM_SUCCESS)) ?
+ getpwnam (name) : NIL) {
+#if 0
+ /*
+ * Some people have reported that this causes a SEGV in strncpy() from
+ * pam_unix.so.1
+ */
+ /*
+ * This pam_open_session() call is inconsistant with how we handle other
+ * platforms, where we don't write [uw]tmp records. However, unlike our
+ * code on other platforms, pam_acct_mgmt() will check those records for
+ * inactivity and deny the authentication.
+ */
+ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */
+#endif
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl);
+ }
+ else checkpw_cleanup (hdl); /* clean up */
+ fs_give ((void **) &name);
+ /* reset log facility in case PAM broke it */
+ if (myServerName) openlog (myServerName,LOG_PID,syslog_facility);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_psx.c b/imap/src/osdep/unix/ckp_psx.c
new file mode 100644
index 00000000..d8ebdaad
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_psx.c
@@ -0,0 +1,101 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: POSIX check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * warn number of days of password expiration warning
+ * expire date account expires if positive
+ * inact number of days an accout can be inactive (not checked!)
+ * The expiration day is the *last* day that the password or account
+ * is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ ((sp->sp_expire <= 0) || (sp->sp_expire >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ if ((sp->sp_expire > 0) && ((left = sp->sp_expire - now) < 28)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Account expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Account expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_sce.c b/imap/src/osdep/unix/ckp_sce.c
new file mode 100644
index 00000000..cff95dda
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sce.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct es_passwd *pp;
+ set_auth_parameters (argc,argv);
+ if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pp = getespwnam (pw->pw_name)) && !pp->ufld->fd_lock &&
+ !pp->ufld->fd_psw_chg_reqd &&
+ pp->ufld->fd_encrypt && pp->ufld->fd_encrypt[0] &&
+ pp->ufld->fd_encrypt[1] &&
+ !strcmp (pp->ufld->fd_encrypt,bigcrypt (pass,pp->ufld->fd_encrypt))))
+ endprpwent (); /* don't need shadow password data any more */
+ else pw = NIL; /* password failed */
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_sec.c b/imap/src/osdep/unix/ckp_sec.c
new file mode 100644
index 00000000..feddaaf3
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sec.c
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct pr_passwd *pp;
+ set_auth_parameters (argc,argv);
+ if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pp = getprpwnam (pw->pw_name)) && !pp->ufld.fd_lock &&
+ pp->ufld.fd_encrypt && pp->ufld.fd_encrypt[0] &&
+ pp->ufld.fd_encrypt[1] &&
+ !strcmp (pp->ufld.fd_encrypt,bigcrypt (pass,pp->ufld.fd_encrypt))))
+ endprpwent (); /* don't need shadow password data any more */
+ else pw = NIL; /* password failed */
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_ssn.c b/imap/src/osdep/unix/ckp_ssn.c
new file mode 100644
index 00000000..edd7d632
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_ssn.c
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Secure SUN-OS check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct passwd_adjunct *pa;
+ char *user = cpystr (pw->pw_name);
+ /* validate user and password */
+ struct passwd *ret =
+ ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pa = getpwanam (pw->pw_name)) &&
+ pa->pwa_passwd && pa->pwa_passwd[0] && pa->pwa_passwd[1] &&
+ !strcmp (pa->pwa_passwd,(char *) crypt (pass,pa->pwa_passwd)))) ?
+ getpwnam (user) : NIL;
+ if (user) fs_give ((void **) &user);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_std.c b/imap/src/osdep/unix/ckp_std.c
new file mode 100644
index 00000000..36255cc1
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_std.c
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ?
+ pw : NIL;
+}
diff --git a/imap/src/osdep/unix/ckp_sv4.c b/imap/src/osdep/unix/ckp_sv4.c
new file mode 100644
index 00000000..e4b8ab3b
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sv4.c
@@ -0,0 +1,90 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * warn number of days of password expiration warning
+ * The expiration day is the *last* day that the password is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_svo.c b/imap/src/osdep/unix/ckp_svo.c
new file mode 100644
index 00000000..ffb19a64
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_svo.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Old SVR4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * The expiration day is the *last* day that the password is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= 28)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_ult.c b/imap/src/osdep/unix/ckp_ult.c
new file mode 100644
index 00000000..94e3b0b6
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_ult.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: ULTRIX check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (authenticate_user (pw,pass,NIL) >= 0) ? pw : NIL;
+}
diff --git a/imap/src/osdep/unix/crx_nfs.c b/imap/src/osdep/unix/crx_nfs.c
new file mode 100644
index 00000000..7e60e635
--- /dev/null
+++ b/imap/src/osdep/unix/crx_nfs.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Exclusive create of a file, NFS kludge version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 17 December 1999
+ * Last Edited: 30 August 2006
+ */
+
+
+/* SUN-OS had an NFS, As kludgy as an albatross;
+ * And everywhere that it was installed, It was a total loss.
+ * -- MRC 9/25/91
+ */
+
+/* Exclusive create of a file
+ * Accepts: file name
+ * Return: T if success, NIL if failed, -1 if retry
+ */
+
+long crexcl (char *name)
+{
+ long ret = -1;
+ int i;
+ char hitch[MAILTMPLEN];
+ struct stat sb;
+ int mask = umask (0);
+ /* build hitching post file name */
+ sprintf (hitch,"%s.%lu.%d.",name,(unsigned long) time (0),getpid ());
+ i = strlen (hitch); /* append local host name */
+ gethostname (hitch + i,(MAILTMPLEN - i) - 1);
+ /* try to get hitching-post file */
+ if ((i = open (hitch,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) >= 0) {
+ close (i); /* close the hitching-post */
+ /* Note: link() may return an error even if it actually succeeded. So we
+ * always check for success via the link count, and ignore the error if
+ * the link count is right.
+ */
+ /* tie hitching-post to lock */
+ i = link (hitch,name) ? errno : 0;
+ /* success if link count now 2 */
+ if (!stat (hitch,&sb) && (sb.st_nlink == 2)) ret = LONGT;
+ else if (i == EPERM) { /* links not allowed? */
+ /* Probably a FAT filesystem on Linux. It can't be NFS, so try creating
+ * the lock file directly.
+ */
+ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) >= 0){
+ close (i); /* close the file */
+ ret = LONGT; /* success */
+ }
+ /* fail unless error is EEXIST */
+ else if (errno != EEXIST) ret = NIL;
+ }
+ unlink (hitch); /* flush hitching post */
+ }
+ /* fail unless error is EEXIST */
+ else if (errno != EEXIST) ret = NIL;
+ umask (mask); /* restore previous mask */
+ return ret;
+}
diff --git a/imap/src/osdep/unix/crx_std.c b/imap/src/osdep/unix/crx_std.c
new file mode 100644
index 00000000..633137b8
--- /dev/null
+++ b/imap/src/osdep/unix/crx_std.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Exclusive create of a file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 17 December 1999
+ * Last Edited: 30 August 2006
+ */
+
+/* Exclusive create of a file
+ * Accepts: file name
+ * Return: T if success, NIL if failed, -1 if retry
+ */
+
+long crexcl (char *name)
+{
+ int i;
+ int mask = umask (0);
+ long ret = LONGT;
+ /* try to get the lock */
+ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) < 0)
+ ret = (errno == EEXIST) ? -1 : NIL;
+ else close (i); /* made the file, now close it */
+ umask (mask); /* restore previous mask */
+ return LONGT; /* success */
+}
diff --git a/imap/src/osdep/unix/drivers b/imap/src/osdep/unix/drivers
new file mode 100755
index 00000000..bd49365f
--- /dev/null
+++ b/imap/src/osdep/unix/drivers
@@ -0,0 +1,36 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Driver Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 October 1989
+# Last Edited: 30 August 2006
+
+
+# Erase old driver linkage
+rm -f linkage.[ch]
+
+# Now define the new list
+for driver
+ do
+ echo "extern DRIVER "$driver"driver;" >> linkage.h
+ echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c
+done
diff --git a/imap/src/osdep/unix/dummy.c b/imap/src/osdep/unix/dummy.c
new file mode 100644
index 00000000..b003a0ba
--- /dev/null
+++ b/imap/src/osdep/unix/dummy.c
@@ -0,0 +1,809 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate clearbox INBOX */
+ if (!*s) return &dummydriver;
+ else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ return &dummydriver;
+ }
+ /* blackbox INBOX does not exist yet */
+ else if (!compare_cstring (name,"INBOX")) return &dummydriver;
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ DRIVER *drivers;
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'/',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,i = s - test);
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ if (s = strrchr (file,'/')){/* find directory name */
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if ((file[0] == '~') || (file[0] == '#')) s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ /* always an INBOX */
+ if (pmatch ("INBOX",ucase (test))) {
+ /* done if have a dirfmt INBOX */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ drivers && !(!(drivers->flags & DR_DISABLE) &&
+ (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) ("INBOX")); drivers = drivers->next);
+ /* list INBOX appropriately */
+ dummy_listed (stream,drivers ? '/' : NIL,"INBOX",
+ drivers ? NIL : LATT_NOINFERIORS,contents);
+ }
+ }
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (!compare_cstring (s,"INBOX") &&
+ pmatch ("INBOX",ucase (strcpy (tmp,test))))
+ mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ else while (showuppers && (t = strrchr (s,'/'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf))
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* allow but snarl */
+ sprintf (tmp,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
+ mailbox);
+ MM_NOTIFY (stream,tmp,WARN);
+ case S_IFREG:
+ return sm_subscribe (mailbox);
+ }
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ DRIVER *drivers;
+ dirfmttest_t dt;
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN],path[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ if (dp = opendir (tmp)) { /* do nothing if can't open directory */
+ /* see if a non-namespace directory format */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ dir && !dt && drivers; drivers = drivers->next)
+ if (!(drivers->flags & DR_DISABLE) && (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) (dir))
+ dt = mail_parameters ((*drivers->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'/') && !pmatch (dir,"INBOX"))
+ dummy_listed (stream,'/',dir,dt ? NIL : LATT_NOSELECT,contents);
+
+ /* scan directory, ignore . and .. */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '/') while (d = readdir (dp))
+ if ((!(dt && (*dt) (d->d_name))) &&
+ ((d->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]))))) &&
+ ((len + strlen (d->d_name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,d->d_name);
+ else strcpy (tmp,d->d_name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (strcpy (path,tmp),pat,'/') ||
+ pmatch_full (strcat (path,"/"),pat,'/') ||
+ dmatch (path,pat,'/')) &&
+ mailboxdir (path,dir,"x") && (len = strlen (path)) &&
+ strcpy (path+len-1,d->d_name) && !stat (path,&sbuf)) {
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ /* form with trailing / */
+ sprintf (path,"%s/",tmp);
+ /* skip listing if INBOX */
+ if (!pmatch (tmp,"INBOX")) {
+ if (pmatch_full (tmp,pat,'/')) {
+ if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents))
+ break;
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (path,pat,'/') &&
+ !dummy_listed (stream,'/',path,LATT_NOSELECT,contents))
+ break;
+ }
+ if (dmatch (path,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,path,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ /* Must use ctime for systems that don't update mtime properly */
+ if (pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX"))
+ dummy_listed (stream,'/',tmp,LATT_NOINFERIORS +
+ ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))?
+ LATT_MARKED : LATT_UNMARKED),contents);
+ break;
+ }
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* Scan file for contents
+ * Accepts: driver to use
+ * file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ scancontents_t sc = dtb ?
+ (scancontents_t) (*dtb->parameters) (GET_SCANCONTENTS,NIL) : NIL;
+ return (*(sc ? sc : dummy_scan_contents)) (name,contents,csiz,fsiz);
+}
+
+
+/* Scan file for contents
+ * Accepts: file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ int fd;
+ unsigned long ssiz,bsiz;
+ char *buf;
+ /* forget it if can't select or open */
+ if ((fd = open (name,O_RDONLY,NIL)) >= 0) {
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (fsiz) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ fsiz -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (fsiz) return T; /* found */
+ }
+ return NIL; /* not found */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: NIL if should abort hierarchy search, else T (currently always)
+ */
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ DRIVER *d;
+ DIR *dp;
+ struct direct *dr;
+ dirfmttest_t dt;
+ unsigned long csiz;
+ struct stat sbuf;
+ int nochild;
+ char *s,tmp[MAILTMPLEN];
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ (dp = opendir (tmp))) { /* if not \NoInferiors */
+ /* locate dirfmttest if any */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ !dt && d; d = d->next)
+ if (!(d->flags & DR_DISABLE) && (d->flags & DR_DIRFMT) &&
+ (*d->valid) (name))
+ dt = mail_parameters ((*d->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* scan directory for children */
+ for (nochild = T; nochild && (dr = readdir (dp)); )
+ if ((!(dt && (*dt) (dr->d_name))) &&
+ ((dr->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (dr->d_name[1] && ((dr->d_name[1] != '.') || dr->d_name[2])))))
+ nochild = NIL;
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ closedir (dp); /* all done, flush directory */
+ }
+ d = NIL; /* don't \NoSelect dir if it has a driver */
+ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) &&
+ (d != &dummydriver)) attributes &= ~LATT_NOSELECT;
+ if (!contents || /* notify main program */
+ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) &&
+ (s = mailboxfile (tmp,name)) &&
+ (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) &&
+ !stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) &&
+ SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size)))
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = NIL;
+ /* validate name */
+ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create the name, done if made directory */
+ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&&
+ (s = strrchr (s,'/')) && !s[1]) return T;
+ return ret ? set_mbx_protections (mailbox,tmp) : NIL;
+}
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'/');
+ int wantdir = t && !t[1];
+ int mask = umask (0);
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ if (s = strrchr (path,'/')) { /* found superior to this name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) {
+ umask (mask); /* restore mask */
+ return NIL;
+ }
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path,(int) dirmode);
+ *t = '/'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >=0)
+ ret = !close (fd);
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ umask (mask); /* restore mask */
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ }
+ /* no trailing / (workaround BSD kernel bug) */
+ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ /* no trailing / allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ MM_LOG (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ else { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,mbx)) return NIL;
+ *s = c; /* restore full name */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+ sprintf (err,"Can't open %.80s: not a selectable mailbox",
+ stream->mailbox);
+ else if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ MM_LOG (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ if (time (0) >= /* time to do another test? */
+ ((time_t) (stream->gensym +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ /* append to INBOX? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ /* yes, if no empty proto try creating */
+ if (!ts && !(*(ts = default_proto (NIL))->dtb->create) (ts,"INBOX"))
+ ts = NIL;
+ }
+ else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ MM_LOG (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ else if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s;
+ if (ref) { /* preliminary reference check */
+ if (*ref == '{') return NIL;/* remote reference not allowed */
+ else if (!*ref) ref = NIL; /* treat empty reference as no reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace name */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '/': /* rooted name */
+ case '~': /* home directory name */
+ if (!ref || (*ref != '#')) {/* non-namespace reference? */
+ strcpy (tmp,pat); /* yes, ignore */
+ break;
+ }
+ /* fall through */
+ default: /* apply reference for all other names */
+ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */
+ else if ((*ref != '#') || mailboxfile (tmp,ref)) {
+ /* wants root of name? */
+ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat);
+ /* otherwise just append */
+ else sprintf (tmp,"%s%s",ref,pat);
+ }
+ else return NIL; /* unknown namespace */
+ }
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/unix/dummy.h b/imap/src/osdep/unix/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/unix/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/unix/env_unix.c b/imap/src/osdep/unix/env_unix.c
new file mode 100644
index 00000000..6b2c4471
--- /dev/null
+++ b/imap/src/osdep/unix/env_unix.c
@@ -0,0 +1,1847 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 23 February 2009
+ */
+
+#include <grp.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+
+/* in case stat.h is ancient */
+
+#ifndef S_IRUSR
+#define S_IRUSR S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR S_IWRITE
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR S_IEXEC
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP (S_IREAD >> 3)
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP (S_IWRITE >> 3)
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP (S_IEXEC >> 3)
+#endif
+#ifndef S_IROTH
+#define S_IROTH (S_IREAD >> 6)
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH (S_IWRITE >> 6)
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH (S_IEXEC >> 6)
+#endif
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myServerName = NIL;/* server name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *mailsubdir = NIL; /* mailbox subdirectory name */
+static char *sysInbox = NIL; /* system inbox name */
+static char *newsActive = NIL; /* news active file */
+static char *newsSpool = NIL; /* news spool */
+static char *blackBoxDir = NIL; /* black box directory name */
+ /* black box default home directory */
+static char *blackBoxDefaultHome = NIL;
+static char *sslCApath = NIL; /* non-standard CA path */
+static short anonymous = NIL; /* is anonymous */
+static short blackBox = NIL; /* is a black box */
+static short closedBox = NIL; /* is a closed box (uses chroot() jail) */
+static short restrictBox = NIL; /* is a restricted box */
+static short has_no_life = NIL; /* is a cretin with no life */
+ /* block environment init */
+static short block_env_init = NIL;
+static short hideDotFiles = NIL;/* hide files whose names start with . */
+ /* advertise filesystem root */
+static short advertisetheworld = NIL;
+ /* only advertise own mailboxes and #shared */
+static short limitedadvertise = NIL;
+ /* disable automatic shared namespaces */
+static short noautomaticsharedns = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* client principals include service name */
+static short kerb_cp_svr_name = NIL;
+static long locktimeout = 5; /* default lock timeout in minutes */
+ /* default prototypes */
+static MAILSTREAM *createProto = NIL;
+static MAILSTREAM *appendProto = NIL;
+ /* default user flags */
+static char *userFlags[NUSERFLAGS] = {NIL};
+static NAMESPACE *nslist[3]; /* namespace list */
+static int logtry = 3; /* number of server login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+ /* logout function */
+static logouthook_t maillogouthook = NIL;
+ /* logout data */
+static void *maillogoutdata = NIL;
+ /* allow user config files */
+static short allowuserconfig = NIL;
+ /* 1 = disable plaintext, 2 = if not SSL */
+static long disablePlaintext = NIL;
+static long list_max_level = 20;/* maximum level of list recursion */
+ /* facility for syslog */
+static int syslog_facility = LOG_MAIL;
+
+/* Path of the privileged system lock program (mlock). Normally set by
+ * logic test.
+ */
+
+static char *lockpgm = LOCKPGM;
+
+/* Directory used for shared locks. MUST be the same for all users of the
+ * system, and MUST be protected 1777. /var/tmp may be preferable on some
+ * systems.
+ */
+
+static const char *tmpdir = "/tmp";
+
+/* Do not change shlock_mode. Doing so can cause mailbox corruption and
+ * denial of service. It also defeats the entire purpose of the shared
+ * lock mechanism. The right way to avoid shared locks is to set up a
+ * closed box (see the closedBox setting).
+ */
+
+ /* shared lock mode */
+static const int shlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+
+
+/* It is STRONGLY recommended that you do not change dotlock_mode. Doing so
+ * can cause denial of service with old dot-lock files left lying around.
+ * However, since dot-locks are only used with traditional UNIX and MMDF
+ * formats which are not normally shared, it is much less harmful to tamper
+ * with this than with shlock_mode.
+ */
+
+ /* dot-lock mode */
+static long dotlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+
+/* File/directory access and protection policies */
+
+/* Unlike shlock_mode, the ????_protection modes are intended to be fully
+ * customizable according to site policy. The values here are recommended
+ * settings, based upon the documented purposes of the namespaces.
+ */
+
+ /* user space - only owner can read/write */
+static char *myMailboxDir = NIL;/* user space directory name */
+ /* default file protection */
+static long mbx_protection = S_IRUSR|S_IWUSR;
+ /* default directory protection */
+static long dir_protection = S_IRUSR|S_IWUSR|S_IXUSR;
+
+ /* user space for user "anonymous" */
+ /* anonymous home directory */
+static char *anonymousHome = NIL;
+
+ /* #ftp - everybody can read, only owner can write */
+static char *ftpHome = NIL; /* ftp export home directory */
+ /* default ftp file protection */
+static long ftp_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+static long ftp_dir_protection =/* default ftp directory protection */
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+
+ /* #public - everybody can read/write */
+static char *publicHome = NIL; /* public home directory */
+static long public_protection = /* default public file protection */
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ /* default public directory protection */
+static long public_dir_protection =
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
+
+ /* #shared/ - owner and group members can read/write */
+static char *sharedHome = NIL; /* shared home directory */
+ /* default shared file protection */
+static long shared_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+ /* default shared directory protection */
+static long shared_dir_protection =
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP;
+
+/* OS bug workarounds - should be avoided at all cost */
+
+
+/* Don't set fcntlhangbug unless you really have to, since it risks mailbox
+ * corruption. The flocksim.c mechanism is designed to detect NFS access
+ * and no-op in that cases only, so this flag should be unnecessary.
+ */
+
+static short fcntlhangbug = NIL;/* flock() emulator using fcntl() is a no-op */
+
+
+/* Don't set netfsstatbug unless you really have to, since it dramatically
+ * slows down traditional UNIX and MMDF mailbox performance.
+ */
+
+static short netfsstatbug = NIL;/* compensate for broken stat() on network
+ * filesystems (AFS and old NFS)
+ */
+
+
+/* Note: setting disableLockWarning means that you assert that the
+ * so-modified copy of this software will NEVER be used:
+ * 1) in conjunction with any software which expects .lock files
+ * 2) to access NFS-mounted files and directories
+ *
+ * Unless both of these conditions apply, then do not set this flag.
+ * Instead, read the FAQ (item 7.10) and either use 1777 protection
+ * on the mail spool, or install mlock.
+ *
+ * In addition, by setting this flag you also agree that you are fully
+ * legally and morally responsible when (not if) mail files are damaged
+ * as the result of your choice.
+ *
+ * The mlock tool exists for a reason. Use it.
+ */
+ /* disable warning if can't make .lock file */
+static short disableLockWarning = NIL;
+
+/* UNIX Namespaces */
+
+ /* personal mh namespace */
+static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
+static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
+ /* home namespace */
+static NAMESPACE nshome = {"",'/',NIL,&nsmh};
+ /* UNIX other user namespace */
+static NAMESPACE nsunixother = {"~",'/',NIL,NIL};
+ /* black box other user namespace */
+static NAMESPACE nsblackother = {"/",'/',NIL,NIL};
+ /* public (anonymous OK) namespace */
+static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
+ /* netnews namespace */
+static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
+ /* FTP export namespace */
+static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
+ /* shared (no anonymous) namespace */
+static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
+ /* world namespace */
+static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
+ /* only shared and public namespaces */
+static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic};
+
+
+
+#include "write.c" /* include safe writing routines */
+#include "crexcl.c" /* include exclusive create */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_NEWSACTIVE:
+ if (newsActive) fs_give ((void **) &newsActive);
+ newsActive = cpystr ((char *) value);
+ case GET_NEWSACTIVE:
+ ret = (void *) newsActive;
+ break;
+ case SET_NEWSSPOOL:
+ if (newsSpool) fs_give ((void **) &newsSpool);
+ newsSpool = cpystr ((char *) value);
+ case GET_NEWSSPOOL:
+ ret = (void *) newsSpool;
+ break;
+
+ case SET_ANONYMOUSHOME:
+ if (anonymousHome) fs_give ((void **) &anonymousHome);
+ anonymousHome = cpystr ((char *) value);
+ case GET_ANONYMOUSHOME:
+ if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
+ ret = (void *) anonymousHome;
+ break;
+ case SET_FTPHOME:
+ if (ftpHome) fs_give ((void **) &ftpHome);
+ ftpHome = cpystr ((char *) value);
+ case GET_FTPHOME:
+ ret = (void *) ftpHome;
+ break;
+ case SET_PUBLICHOME:
+ if (publicHome) fs_give ((void **) &publicHome);
+ publicHome = cpystr ((char *) value);
+ case GET_PUBLICHOME:
+ ret = (void *) publicHome;
+ break;
+ case SET_SHAREDHOME:
+ if (sharedHome) fs_give ((void **) &sharedHome);
+ sharedHome = cpystr ((char *) value);
+ case GET_SHAREDHOME:
+ ret = (void *) sharedHome;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_SSLCAPATH: /* this can be set null */
+ if (sslCApath) fs_give ((void **) &sslCApath);
+ sslCApath = value ? cpystr ((char *) value) : value;
+ break;
+ case GET_SSLCAPATH:
+ ret = (void *) sslCApath;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+
+ case SET_MBXPROTECTION:
+ mbx_protection = (long) value;
+ case GET_MBXPROTECTION:
+ ret = (void *) mbx_protection;
+ break;
+ case SET_DIRPROTECTION:
+ dir_protection = (long) value;
+ case GET_DIRPROTECTION:
+ ret = (void *) dir_protection;
+ break;
+ case SET_LOCKPROTECTION:
+ dotlock_mode = (long) value;
+ case GET_LOCKPROTECTION:
+ ret = (void *) dotlock_mode;
+ break;
+ case SET_FTPPROTECTION:
+ ftp_protection = (long) value;
+ case GET_FTPPROTECTION:
+ ret = (void *) ftp_protection;
+ break;
+ case SET_PUBLICPROTECTION:
+ public_protection = (long) value;
+ case GET_PUBLICPROTECTION:
+ ret = (void *) public_protection;
+ break;
+ case SET_SHAREDPROTECTION:
+ shared_protection = (long) value;
+ case GET_SHAREDPROTECTION:
+ ret = (void *) shared_protection;
+ break;
+ case SET_FTPDIRPROTECTION:
+ ftp_dir_protection = (long) value;
+ case GET_FTPDIRPROTECTION:
+ ret = (void *) ftp_dir_protection;
+ break;
+ case SET_PUBLICDIRPROTECTION:
+ public_dir_protection = (long) value;
+ case GET_PUBLICDIRPROTECTION:
+ ret = (void *) public_dir_protection;
+ break;
+ case SET_SHAREDDIRPROTECTION:
+ shared_dir_protection = (long) value;
+ case GET_SHAREDDIRPROTECTION:
+ ret = (void *) shared_dir_protection;
+ break;
+
+ case SET_LOCKTIMEOUT:
+ locktimeout = (long) value;
+ case GET_LOCKTIMEOUT:
+ ret = (void *) locktimeout;
+ break;
+ case SET_DISABLEFCNTLLOCK:
+ fcntlhangbug = value ? T : NIL;
+ case GET_DISABLEFCNTLLOCK:
+ ret = (void *) (fcntlhangbug ? VOIDT : NIL);
+ break;
+ case SET_LOCKEACCESERROR:
+ disableLockWarning = value ? NIL : T;
+ case GET_LOCKEACCESERROR:
+ ret = (void *) (disableLockWarning ? NIL : VOIDT);
+ break;
+ case SET_HIDEDOTFILES:
+ hideDotFiles = value ? T : NIL;
+ case GET_HIDEDOTFILES:
+ ret = (void *) (hideDotFiles ? VOIDT : NIL);
+ break;
+ case SET_DISABLEPLAINTEXT:
+ disablePlaintext = (long) value;
+ case GET_DISABLEPLAINTEXT:
+ ret = (void *) disablePlaintext;
+ break;
+ case SET_CHROOTSERVER:
+ closedBox = value ? T : NIL;
+ case GET_CHROOTSERVER:
+ ret = (void *) (closedBox ? VOIDT : NIL);
+ break;
+ case SET_ADVERTISETHEWORLD:
+ advertisetheworld = value ? T : NIL;
+ case GET_ADVERTISETHEWORLD:
+ ret = (void *) (advertisetheworld ? VOIDT : NIL);
+ break;
+ case SET_LIMITEDADVERTISE:
+ limitedadvertise = value ? T : NIL;
+ case GET_LIMITEDADVERTISE:
+ ret = (void *) (limitedadvertise ? VOIDT : NIL);
+ break;
+ case SET_DISABLEAUTOSHAREDNS:
+ noautomaticsharedns = value ? T : NIL;
+ case GET_DISABLEAUTOSHAREDNS:
+ ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+
+ case SET_USERHASNOLIFE:
+ has_no_life = value ? T : NIL;
+ case GET_USERHASNOLIFE:
+ ret = (void *) (has_no_life ? VOIDT : NIL);
+ break;
+ case SET_KERBEROS_CP_SVR_NAME:
+ kerb_cp_svr_name = value ? T : NIL;
+ case GET_KERBEROS_CP_SVR_NAME:
+ ret = (void *) (kerb_cp_svr_name ? VOIDT : NIL);
+ break;
+ case SET_NETFSSTATBUG:
+ netfsstatbug = value ? T : NIL;
+ case GET_NETFSSTATBUG:
+ ret = (void *) (netfsstatbug ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ case SET_LOGOUTHOOK:
+ maillogouthook = (logouthook_t) value;
+ case GET_LOGOUTHOOK:
+ ret = maillogouthook;
+ break;
+ case SET_LOGOUTDATA:
+ maillogoutdata = (void *) value;
+ case GET_LOGOUTDATA:
+ ret = maillogoutdata;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ /* append timezone suffix if desired */
+ if (suffix) rfc822_timezone (date,(void *) t);
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ int onceonly = server && service && sslservice;
+ if (onceonly) { /* set server name in syslog */
+ int mask;
+ openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
+ fclose (stderr); /* possibly save a process ID */
+ dorc (NIL,NIL); /* do systemwide configuration */
+ switch (mask = umask (022)){/* check old umask */
+ case 0: /* definitely unreasonable */
+ case 022: /* don't need to change it */
+ break;
+ default: /* already was a reasonable value */
+ umask (mask); /* so change it back */
+ }
+ }
+ arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
+ arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
+ arm_signal (SIGHUP,hupint); /* prepare for hangup */
+ arm_signal (SIGPIPE,hupint); /* alternative hangup */
+ arm_signal (SIGTERM,trmint); /* prepare for termination */
+ /* status dump */
+ if (staint) arm_signal (SIGUSR1,staint);
+ if (onceonly) { /* set up network and maybe SSL */
+ long port;
+ struct servent *sv;
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ }
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Return UNIX password entry for user name
+ * Accepts: user name string
+ * Returns: password entry
+ *
+ * Tries all-lowercase form of user name if given user name fails
+ */
+
+static struct passwd *pwuser (unsigned char *user)
+{
+ unsigned char *s;
+ struct passwd *pw = getpwnam (user);
+ if (!pw) { /* failed, see if any uppercase characters */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ if (*s) { /* yes, try all lowercase form */
+ pw = getpwnam (s = lcase (cpystr (user)));
+ fs_give ((void **) &s);
+ }
+ }
+ return pw;
+}
+
+
+/* Validate password for user name
+ * Accepts: user name string
+ * password string
+ * argument count
+ * argument vector
+ * Returns: password entry if validated
+ *
+ * Tries password+1 if password fails and starts with space
+ */
+
+static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
+{
+ char *s;
+ struct passwd *pw;
+ struct passwd *ret = NIL;
+ if (auth_md5.server) { /* using CRAM-MD5 authentication? */
+ if (s = auth_md5_pwd (user)) {
+ if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
+ ret = pwuser (user); /* validated, get passwd entry for user */
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s);
+ }
+ }
+ else if (pw = pwuser (user)) {/* can get user? */
+ s = cpystr (pw->pw_name); /* copy returned name in case we need it */
+ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
+ (*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
+ ret = checkpw (pw,pwd+1,argc,argv);
+ fs_give ((void **) &s); /* don't need copy of name any more */
+ }
+ return ret;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
+{
+ struct passwd *pw = NIL;
+ int level = LOG_NOTICE;
+ char *err = "failed";
+ /* cretins still haven't given up */
+ if ((strlen (user) >= NETMAXUSER) ||
+ (authuser && (strlen (authuser) >= NETMAXUSER))) {
+ level = LOG_ALERT; /* escalate this alert */
+ err = "SYSTEM BREAK-IN ATTEMPT";
+ logtry = 0; /* render this session useless */
+ }
+ else if (logtry-- <= 0) err = "excessive login failures";
+ else if (disablePlaintext) err = "disabled";
+ else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
+ else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
+ if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
+ syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
+ user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ /* log in Mr. A. N. Onymous */
+ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
+ (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
+ argc,argv);
+}
+
+/* Finish log in and environment initialization
+ * Accepts: passwd struct for loginpw()
+ * optional authentication user name
+ * user name (NIL for anonymous)
+ * home directory (NIL to use directory from passwd struct)
+ * argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[])
+{
+ struct group *gr;
+ char **t;
+ long ret = NIL;
+ if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */
+ /* make safe copies of user and home */
+ if (user) user = cpystr (pw->pw_name);
+ home = cpystr (home ? home : pw->pw_dir);
+ /* authorization ID .NE. authentication ID? */
+ if (user && auser && *auser && compare_cstring (auser,user)) {
+ /* scan list of mail administrators */
+ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
+ if (!compare_cstring (auser,*t++))
+ ret = pw_login (pw,NIL,user,home,argc,argv);
+ syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
+ ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
+ }
+ else if (closedBox) { /* paranoid site, lock out other directories */
+ if (chdir (home) || chroot (home))
+ syslog (LOG_NOTICE|LOG_AUTH,
+ "Login %s failed: unable to set chroot=%.80s host=%.80s",
+ pw->pw_name,home,tcp_clienthost ());
+ else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL);
+ else fatal ("Login failed after chroot");
+ }
+ /* normal login */
+ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
+ (ret = env_init (user,home))) chdir (myhomedir ());
+ fs_give ((void **) &home); /* clean up */
+ if (user) fs_give ((void **) &user);
+ }
+ endpwent (); /* in case shadow passwords in pw data */
+ return ret; /* return status */
+}
+
+/* Initialize environment
+ * Accepts: user name (NIL for anonymous)
+ * home directory name
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ struct passwd *pw;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ /* initially nothing in namespace list */
+ nslist[0] = nslist[1] = nslist[2] = NIL;
+ /* myUserName must be set before dorc() call */
+ myUserName = cpystr (user ? user : ANONYMOUSUSER);
+ /* force default prototypes to be set */
+ if (!createProto) createProto = &CREATEPROTO;
+ if (!appendProto) appendProto = &EMPTYPROTO;
+ dorc (NIL,NIL); /* do systemwide configuration */
+ if (!home) { /* closed box server */
+ /* standard user can only reference home */
+ if (user) nslist[0] = &nshome;
+ else { /* anonymous user */
+ nslist[0] = &nsblackother; /* set root */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (""); /* home directory is root */
+ sysInbox = cpystr ("INBOX");/* make system INBOX */
+ }
+ else { /* open or black box */
+ closedBox = NIL; /* definitely not a closed box */
+ if (user) { /* remember user name and home directory */
+ if (blackBoxDir) { /* build black box directory name */
+ sprintf (tmp,"%s/%s",blackBoxDir,myUserName);
+ /* must exist */
+ if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) ||
+ (blackBoxDefaultHome &&
+ !stat (home = blackBoxDefaultHome,&sbuf) &&
+ (sbuf.st_mode & S_IFDIR)))) fatal ("no home");
+ sysInbox = (char *) fs_get (strlen (home) + 7);
+ /* set system INBOX */
+ sprintf (sysInbox,"%s/INBOX",home);
+ blackBox = T; /* mark that it's a black box */
+ /* mbox meaningless if black box */
+ mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox");
+ }
+ nslist[0] = &nshome; /* home namespace */
+ /* limited advertise namespaces */
+ if (limitedadvertise) nslist[2] = &nslimited;
+ else if (blackBox) { /* black box namespaces */
+ nslist[1] = &nsblackother;
+ nslist[2] = &nsshared;
+ }
+ else { /* open box namespaces */
+ nslist[1] = &nsunixother;
+ nslist[2] = advertisetheworld ? &nsworld : &nsshared;
+ }
+ }
+ else {
+ nslist[2] = &nsftp; /* anonymous user */
+ sprintf (tmp,"%s/INBOX",
+ home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
+ sysInbox = cpystr (tmp); /* make system INBOX */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (home); /* set home directory */
+ }
+
+ if (allowuserconfig) { /* allow user config files */
+ dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T);
+ dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL);
+ }
+ if (!closedBox && !noautomaticsharedns) {
+ /* #ftp namespace */
+ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
+ /* #public namespace */
+ if (!publicHome && (pw = getpwnam ("imappublic")))
+ publicHome = cpystr (pw->pw_dir);
+ /* #shared namespace */
+ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
+ sharedHome = cpystr (pw->pw_dir);
+ }
+ if (!myLocalHost) mylocalhost ();
+ if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
+ if (!newsActive) newsActive = cpystr (ACTIVEFILE);
+ if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
+ /* re-do open action to get flags */
+ (*createProto->dtb->open) (NIL);
+ endpwent (); /* close pw database */
+ return T;
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ struct passwd *pw;
+ struct stat sbuf;
+ char *s;
+ unsigned long euid;
+ char *ret = UNLOGGEDUSER;
+ /* no user name yet and not root? */
+ if (!myUserName && (euid = geteuid ())) {
+ /* yes, look up getlogin() user name or EUID */
+ if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
+ (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
+ (pw = getpwuid (euid))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return pw->pw_name;
+ }
+ env_init (pw->pw_name,
+ ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
+ !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
+ s : pw->pw_dir);
+ }
+ else fatal ("Unable to look up user name");
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ if (!myLocalHost) {
+ char *s,tmp[MAILTMPLEN];
+ char *t = "unknown";
+ tmp[0] = tmp[MAILTMPLEN-1] = '\0';
+ if (!gethostname (tmp,MAILTMPLEN-1) && tmp[0]) {
+ /* sanity check of name */
+ for (s = tmp; (*s > 0x20) && (*s < 0x7f); ++s);
+ if (!*s) t = tcp_canonical (tmp);
+ }
+ myLocalHost = cpystr (t);
+ }
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return my home mailbox name
+ * Returns: my home directory name
+ */
+
+static char *mymailboxdir ()
+{
+ char *home = myhomedir ();
+ /* initialize if first time */
+ if (!myMailboxDir && myHomeDir) {
+ if (mailsubdir) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s/%s",home,mailsubdir);
+ myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
+ }
+ else myMailboxDir = cpystr (home);
+ }
+ return myMailboxDir ? myMailboxDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ /* no arguments, wants mailbox directory */
+ else strcpy (dst,mymailboxdir ());
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ struct passwd *pw;
+ char *s;
+ if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
+ ((anonymous || blackBox || restrictBox || (*name == '#')) &&
+ (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
+ dst = NIL; /* invalid name */
+ else switch (*name) { /* determine mailbox type based upon name */
+ case '#': /* namespace name */
+ /* #ftp/ namespace */
+ if (((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) &&
+ (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
+ /* #public/ and #shared/ namespaces */
+ else if ((((name[1] == 'p') || (name[1] == 'P')) &&
+ ((name[2] == 'u') || (name[2] == 'U')) &&
+ ((name[3] == 'b') || (name[3] == 'B')) &&
+ ((name[4] == 'l') || (name[4] == 'L')) &&
+ ((name[5] == 'i') || (name[5] == 'I')) &&
+ ((name[6] == 'c') || (name[6] == 'C')) &&
+ (name[7] == '/') && (s = publicHome)) ||
+ (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) &&
+ ((name[3] == 'a') || (name[3] == 'A')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) &&
+ ((name[5] == 'e') || (name[5] == 'E')) &&
+ ((name[6] == 'd') || (name[6] == 'D')) &&
+ (name[7] == '/') && (s = sharedHome)))
+ sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ?
+ name+8 : "INBOX");
+ else dst = NIL; /* unknown namespace */
+ break;
+
+ case '/': /* root access */
+ if (anonymous) dst = NIL; /* anonymous forbidden to do this */
+ else if (blackBox) { /* other user access if blackbox */
+ if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
+ /* see if other user INBOX */
+ else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) {
+ *s = '\0'; /* temporarily tie off string */
+ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1);
+ *s = '/'; /* in case caller cares */
+ }
+ else sprintf (dst,"%s/%s",blackBoxDir,name+1);
+ }
+ else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))
+ dst = NIL; /* restricted and not access to sysinbox */
+ else strcpy (dst,name); /* unrestricted, copy root name */
+ break;
+ case '~': /* other user access */
+ /* bad syntax or anonymous can't win */
+ if (!*++name || anonymous) dst = NIL;
+ /* ~/ equivalent to ordinary name */
+ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
+ /* other user forbidden if closed/restricted */
+ else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL;
+ else if (blackBox) { /* black box form of other user */
+ /* see if other user INBOX */
+ if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) {
+ *s = '\0'; /* temporarily tie off string */
+ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name);
+ *s = '/'; /* in case caller cares */
+ }
+ else sprintf (dst,"%s/%s",blackBoxDir,name);
+ }
+ else { /* clear box other user */
+ /* copy user name */
+ for (s = dst; *name && (*name != '/'); *s++ = *name++);
+ *s++ = '\0'; /* tie off user name, look up in passwd file */
+ if ((pw = getpwnam (dst)) && pw->pw_dir) {
+ if (*name) name++; /* skip past the slash */
+ /* canonicalize case of INBOX */
+ if (!compare_cstring (name,"INBOX")) name = "INBOX";
+ /* remove trailing / from directory */
+ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
+ /* don't allow ~root/ if restricted root */
+ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
+ /* build final name w/ subdir if needed */
+ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
+ else sprintf (dst,"%s/%s",pw->pw_dir,name);
+ }
+ else dst = NIL; /* no such user */
+ }
+ break;
+
+ case 'I': case 'i': /* possible INBOX */
+ if (!compare_cstring (name+1,"NBOX")) {
+ /* if restricted, use INBOX in mailbox dir */
+ if (anonymous || blackBox || closedBox)
+ sprintf (dst,"%s/INBOX",mymailboxdir ());
+ else *dst = '\0'; /* otherwise driver selects the name */
+ break;
+ }
+ /* drop into to ordinary name case */
+ default: /* ordinary name is easy */
+ sprintf (dst,"%s/%s",mymailboxdir (),name);
+ break;
+ }
+ return dst; /* return final name */
+}
+
+/* Dot-lock file locker
+ * Accepts: file name to lock
+ * destination buffer for lock file name
+ * open file description on file name to lock
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_lock (char *file,DOTLOCK *base,int fd)
+{
+ int i = locktimeout * 60;
+ int j,mask,retry,pi[2],po[2];
+ char *s,tmp[MAILTMPLEN];
+ struct stat sb;
+ /* flush absurd file name */
+ if (strlen (file) > 512) return NIL;
+ /* build lock filename */
+ sprintf (base->lock,"%s.lock",file);
+ /* assume no pipe */
+ base->pipei = base->pipeo = -1;
+ do { /* make sure not symlink */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ /* time out if file older than 5 minutes */
+ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
+ /* try to create the lock */
+ switch (retry = crexcl (base->lock)) {
+ case -1: /* OK to retry */
+ if (!(i%15)) { /* time to notify? */
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ MM_LOG (tmp,WARN);
+ }
+ sleep (1); /* wait 1 second before next try */
+ break;
+ case NIL: /* failure, can't retry */
+ i = 0;
+ break;
+ case T: /* success, make sure others can break lock */
+ chmod (base->lock,(int) dotlock_mode);
+ return LONGT;
+ }
+ } while (i--); /* until out of retries */
+ if (retry < 0) { /* still returning retry after locktimeout? */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
+ sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
+ (long) (time (0) - sb.st_ctime));
+ MM_LOG (tmp,WARN);
+ }
+ mask = umask (0); /* want our lock protection */
+ unlink (base->lock); /* try to remove the old file */
+ /* seize the lock */
+ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) dotlock_mode)) >= 0) {
+ close (i); /* don't need descriptor any more */
+ sprintf (tmp,"Mailbox %.80s lock overridden",file);
+ MM_LOG (tmp,NIL);
+ chmod (base->lock,(int) dotlock_mode);
+ umask (mask); /* restore old umask */
+ return LONGT;
+ }
+ umask (mask); /* restore old umask */
+ }
+
+ if (fd >= 0) switch (errno) {
+ case EACCES: /* protection failure? */
+ MM_CRITICAL (NIL); /* go critical */
+ if (closedBox || !lockpgm); /* can't do on closed box or disabled */
+ else if ((*lockpgm && stat (lockpgm,&sb)) ||
+ (!*lockpgm && stat (lockpgm = LOCKPGM1,&sb) &&
+ stat (lockpgm = LOCKPGM2,&sb) && stat (lockpgm = LOCKPGM3,&sb)))
+ lockpgm = NIL; /* disable if can't find lockpgm */
+ else if (pipe (pi) >= 0) { /* make command pipes */
+ long cf;
+ char *argv[4],arg[20];
+ /* if input pipes usable create output pipes */
+ if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
+ /* make sure output pipes are usable */
+ if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
+ /* all is good, make inferior process */
+ else if (!(j = fork ())) {
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ /* prepare argument vector */
+ sprintf (arg,"%d",fd);
+ argv[0] = lockpgm; argv[1] = arg;
+ argv[2] = file; argv[3] = NIL;
+ /* set parent's I/O to my O/I */
+ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
+ /* close all unnecessary descriptors */
+ for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
+ cf >= 3; --cf) if (cf != fd) close (cf);
+ /* be our own process group */
+ setpgrp (0,getpid ());
+ /* now run it */
+ _exit (execv (argv[0],argv));
+ }
+ _exit (1); /* child is done */
+ }
+ else if (j > 0) { /* parent process */
+ fd_set rfd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_SET (pi[0],&rfd);
+ tmo.tv_sec = locktimeout * 60;
+ grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
+ /* read response from locking program */
+ if (select (pi[0]+1,&rfd,0,0,&tmo) &&
+ (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
+ /* success, record pipes */
+ base->pipei = pi[0]; base->pipeo = po[1];
+ /* close child's side of the pipes */
+ close (pi[1]); close (po[0]);
+ MM_NOCRITICAL (NIL);/* no longer critical */
+ return LONGT;
+ }
+ }
+ close (po[0]); close (po[1]);
+ }
+ close (pi[0]); close (pi[1]);
+ }
+
+ MM_NOCRITICAL (NIL); /* no longer critical */
+ /* find directory/file delimiter */
+ if (s = strrchr (base->lock,'/')) {
+ *s = '\0'; /* tie off at directory */
+ sprintf(tmp, /* generate default message */
+ "Mailbox vulnerable - directory %.80s must have 1777 protection",
+ base->lock);
+ /* definitely not 1777 if can't stat */
+ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
+ *s = '/'; /* restore lock name */
+ if (mask != 1777) { /* default warning if not 1777 */
+ if (!disableLockWarning) MM_LOG (tmp,WARN);
+ break;
+ }
+ }
+ default:
+ sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
+ base->lock,strerror (errno));
+ if (!disableLockWarning) MM_LOG (tmp,WARN);
+ break;
+ }
+ base->lock[0] = '\0'; /* don't use lock files */
+ return NIL;
+}
+
+/* Dot-lock file unlocker
+ * Accepts: lock file name
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_unlock (DOTLOCK *base)
+{
+ long ret = LONGT;
+ if (base && base->lock[0]) {
+ if (base->pipei >= 0) { /* if running through a pipe unlocker */
+ ret = (write (base->pipeo,"+",1) == 1);
+ /* nuke the pipes */
+ close (base->pipei); close (base->pipeo);
+ }
+ else ret = !unlink (base->lock);
+ }
+ return ret;
+}
+
+/* Lock file name
+ * Accepts: scratch buffer
+ * file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockname (char *lock,char *fname,int op,long *pid)
+{
+ struct stat sbuf;
+ *pid = 0; /* no locker PID */
+ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
+}
+
+
+/* Lock file descriptor
+ * Accepts: file descriptor
+ * lock file name buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockfd (int fd,char *lock,int op)
+{
+ struct stat sbuf;
+ return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
+}
+
+/* Lock file name worker
+ * Accepts: lock file name
+ * pointer to stat() buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lock_work (char *lock,void *sb,int op,long *pid)
+{
+ struct stat lsb,fsb;
+ struct stat *sbuf = (struct stat *) sb;
+ char tmp[MAILTMPLEN];
+ long i;
+ int fd;
+ int mask = umask (0);
+ if (pid) *pid = 0; /* initialize return PID */
+ /* make temporary lock file name */
+ sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : tmpdir,
+ (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
+ while (T) { /* until get a good lock */
+ do switch ((int) chk_notsymlink (lock,&lsb)) {
+ case 1: /* exists just once */
+ if (((fd = open (lock,O_RDWR,shlock_mode)) >= 0) ||
+ (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
+ case -1: /* name doesn't exist */
+ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,shlock_mode);
+ break;
+ default: /* multiple hard links */
+ MM_LOG ("hard link to lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
+ case 0: /* symlink (already did syslog) */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: no lock file */
+ } while ((fd < 0) && (errno == EEXIST));
+ if (fd < 0) { /* failed to get file descriptor */
+ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
+ strerror (errno));
+ if (!closedBox) { /* more explicit snarl for bad configuration */
+ if (stat (tmpdir,&lsb))
+ syslog (LOG_CRIT,"SYSTEM ERROR: no %s: %s",tmpdir,strerror (errno));
+ else if ((lsb.st_mode & 01777) != 01777) {
+ sprintf (tmp,"Can't lock for write: %.80s must have 1777 protection",
+ tmpdir);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't open lock file */
+ }
+
+ /* non-blocking form */
+ if (op & LOCK_NB) i = flock (fd,op);
+ else { /* blocking form */
+ (*mailblocknotify) (BLOCK_FILELOCK,NIL);
+ i = flock (fd,op);
+ (*mailblocknotify) (BLOCK_NONE,NIL);
+ }
+ if (i) { /* failed, get other process' PID */
+ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
+ (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
+ *pid = i;
+ close (fd); /* failed, give up on lock */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't lock */
+ }
+ /* make sure this lock is good for us */
+ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
+ !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
+ (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
+ close (fd); /* lock not right, drop fd and try again */
+ }
+ chmod (lock,shlock_mode); /* make sure mode OK (don't use fchmod()) */
+ umask (mask); /* restore old mask */
+ return fd; /* success */
+}
+
+/* Check to make sure not a symlink
+ * Accepts: file name
+ * stat buffer
+ * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
+ */
+
+long chk_notsymlink (char *name,void *sb)
+{
+ struct stat *sbuf = (struct stat *) sb;
+ /* name exists? */
+ if (lstat (name,sbuf)) return -1;
+ /* forbid symbolic link */
+ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
+ MM_LOG ("symbolic link on lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
+ name);
+ return NIL;
+ }
+ return (long) sbuf->st_nlink; /* return number of hard links */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ /* delete the file if no sharers */
+ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+/* Set proper file protection for mailbox
+ * Accepts: mailbox name
+ * actual file path name
+ * Returns: T, always
+ */
+
+long set_mbx_protections (char *mailbox,char *path)
+{
+ struct stat sbuf;
+ int mode = (int) mbx_protection;
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) mode = (int) ftp_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) mode = (int) public_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) mode = (int) shared_protection;
+ }
+ /* if a directory */
+ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* set owner search if allow read or write */
+ if (mode & 0600) mode |= 0100;
+ if (mode & 060) mode |= 010;/* set group search if allow read or write */
+ if (mode & 06) mode |= 01; /* set world search if allow read or write */
+ /* preserve directory SGID bit */
+ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
+ }
+ chmod (path,mode); /* set the new protection, ignore failure */
+ return LONGT;
+}
+
+/* Get proper directory protection
+ * Accepts: mailbox name
+ * Returns: directory mode, always
+ */
+
+long get_dir_protection (char *mailbox)
+{
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) return ftp_dir_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) return public_dir_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) return shared_dir_protection;
+ }
+ return dir_protection;
+}
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ myusername (); /* make sure initialized */
+ /* return default driver's prototype */
+ return type ? appendProto : createProto;
+}
+
+
+/* Set up user flags for stream
+ * Accepts: MAIL stream
+ * Returns: MAIL stream with user flags set up
+ */
+
+MAILSTREAM *user_flags (MAILSTREAM *stream)
+{
+ int i;
+ myusername (); /* make sure initialized */
+ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
+ return stream;
+}
+
+
+/* Return nth user flag
+ * Accepts: user flag number
+ * Returns: flag
+ */
+
+char *default_user_flag (unsigned long i)
+{
+ myusername (); /* make sure initialized */
+ return userFlags[i];
+}
+
+/* Process rc file
+ * Accepts: file name
+ * .mminit flag
+ * Don't use this feature.
+ */
+
+void dorc (char *file,long flag)
+{
+ int i;
+ char *s,*t,*k,*r,tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ DRIVER *d;
+ FILE *f;
+ if ((f = fopen (file ? file : SYSCONFIG,"r")) &&
+ (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))) do {
+ *t++ = '\0'; /* tie off line, find second space */
+ if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
+ *k++ = '\0'; /* tie off two words */
+ if (!compare_cstring (s,"set keywords") && !userFlags[0]) {
+ /* yes, get first keyword */
+ k = strtok_r (k,", ",&r);
+ /* copy keyword list */
+ for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) {
+ if (userFlags[i]) fs_give ((void **) &userFlags[i]);
+ userFlags[i] = cpystr (k);
+ k = strtok_r (NIL,", ",&r);
+ }
+ if (flag) break; /* found "set keywords" in .mminit */
+ }
+
+ else if (!flag) { /* none of these valid in .mminit */
+ if (myUserName) { /* only valid if logged in */
+ if (!compare_cstring (s,"set new-mailbox-format") ||
+ !compare_cstring (s,"set new-folder-format")) {
+ if (!compare_cstring (k,"same-as-inbox")) {
+ if (d = mail_valid (NIL,"INBOX",NIL)) {
+ if (!compare_cstring (d->name,"mbox"))
+ d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,
+ (void *) "unix");
+ else if (!compare_cstring (d->name,"dummy")) d = NIL;
+ }
+ createProto = d ? ((*d->open) (NIL)) : &CREATEPROTO;
+ }
+ else if (!compare_cstring (k,"system-standard"))
+ createProto = &CREATEPROTO;
+ else { /* canonicalize mbox to unix */
+ if (!compare_cstring (k,"mbox")) k = "unix";
+ /* see if a driver name */
+ if (d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,(void *) k))
+ createProto = (*d->open) (NIL);
+ else { /* duh... */
+ sprintf (tmpx,"Unknown new mailbox format in %s: %s",
+ file ? file : SYSCONFIG,k);
+ MM_LOG (tmpx,WARN);
+ }
+ }
+ }
+ if (!compare_cstring (s,"set empty-mailbox-format") ||
+ !compare_cstring (s,"set empty-folder-format")) {
+ if (!compare_cstring (k,"invalid")) appendProto = NIL;
+ else if (!compare_cstring (k,"same-as-inbox"))
+ appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
+ compare_cstring (d->name,"dummy")) ?
+ ((*d->open) (NIL)) : &EMPTYPROTO;
+ else if (!compare_cstring (k,"system-standard"))
+ appendProto = &EMPTYPROTO;
+ else { /* see if a driver name */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ d && compare_cstring (d->name,k); d = d->next);
+ if (d) appendProto = (*d->open) (NIL);
+ else { /* duh... */
+ sprintf (tmpx,"Unknown empty mailbox format in %s: %s",
+ file ? file : SYSCONFIG,k);
+ MM_LOG (tmpx,WARN);
+ }
+ }
+ }
+ }
+
+ if (!compare_cstring (s,"set local-host")) {
+ fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set news-active-file")) {
+ fs_give ((void **) &newsActive);
+ newsActive = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set news-spool-directory")) {
+ fs_give ((void **) &newsSpool);
+ newsSpool = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set mh-path"))
+ mail_parameters (NIL,SET_MHPATH,(void *) k);
+ else if (!compare_cstring (s,"set mh-allow-inbox"))
+ mail_parameters (NIL,SET_MHALLOWINBOX,(void *) atol (k));
+ else if (!compare_cstring (s,"set news-state-file")) {
+ fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set ftp-export-directory")) {
+ fs_give ((void **) &ftpHome);
+ ftpHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set public-home-directory")) {
+ fs_give ((void **) &publicHome);
+ publicHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set shared-home-directory")) {
+ fs_give ((void **) &sharedHome);
+ sharedHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set system-inbox")) {
+ fs_give ((void **) &sysInbox);
+ sysInbox = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set mail-subdirectory")) {
+ fs_give ((void **) &mailsubdir);
+ mailsubdir = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set from-widget"))
+ mail_parameters (NIL,SET_FROMWIDGET,
+ compare_cstring (k,"header-only") ?
+ VOIDT : NIL);
+
+ else if (!compare_cstring (s,"set rsh-command"))
+ mail_parameters (NIL,SET_RSHCOMMAND,(void *) k);
+ else if (!compare_cstring (s,"set rsh-path"))
+ mail_parameters (NIL,SET_RSHPATH,(void *) k);
+ else if (!compare_cstring (s,"set ssh-command"))
+ mail_parameters (NIL,SET_SSHCOMMAND,(void *) k);
+ else if (!compare_cstring (s,"set ssh-path"))
+ mail_parameters (NIL,SET_SSHPATH,(void *) k);
+ else if (!compare_cstring (s,"set tcp-open-timeout"))
+ mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set tcp-read-timeout"))
+ mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set tcp-write-timeout"))
+ mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set rsh-timeout"))
+ mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set ssh-timeout"))
+ mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set maximum-login-trials"))
+ mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k));
+ else if (!compare_cstring (s,"set lookahead"))
+ mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k));
+ else if (!compare_cstring (s,"set prefetch"))
+ mail_parameters (NIL,SET_PREFETCH,(void *) atol (k));
+ else if (!compare_cstring (s,"set close-on-error"))
+ mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k));
+ else if (!compare_cstring (s,"set imap-port"))
+ mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k));
+ else if (!compare_cstring (s,"set pop3-port"))
+ mail_parameters (NIL,SET_POP3PORT,(void *) atol (k));
+ else if (!compare_cstring (s,"set uid-lookahead"))
+ mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k));
+ else if (!compare_cstring (s,"set try-ssl-first"))
+ mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k));
+
+ else if (!compare_cstring (s,"set mailbox-protection"))
+ mbx_protection = atol (k);
+ else if (!compare_cstring (s,"set directory-protection"))
+ dir_protection = atol (k);
+ else if (!compare_cstring (s,"set lock-protection"))
+ dotlock_mode = atol (k);
+ else if (!compare_cstring (s,"set ftp-protection"))
+ ftp_protection = atol (k);
+ else if (!compare_cstring (s,"set public-protection"))
+ public_protection = atol (k);
+ else if (!compare_cstring (s,"set shared-protection"))
+ shared_protection = atol (k);
+ else if (!compare_cstring (s,"set ftp-directory-protection"))
+ ftp_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set public-directory-protection"))
+ public_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set shared-directory-protection"))
+ shared_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set dot-lock-file-timeout"))
+ locktimeout = atoi (k);
+ else if (!compare_cstring (s,"set disable-fcntl-locking"))
+ fcntlhangbug = atoi (k);
+ else if (!compare_cstring (s,"set disable-lock-warning"))
+ disableLockWarning = atoi (k);
+ else if (!compare_cstring (s,"set disable-unix-UIDs-and-keywords"))
+ has_no_life = atoi (k);
+ else if (!compare_cstring (s,"set hide-dot-files"))
+ hideDotFiles = atoi (k);
+ else if (!compare_cstring (s,"set list-maximum-level"))
+ list_max_level = atol (k);
+ else if (!compare_cstring (s,"set trust-dns"))
+ mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k));
+ else if (!compare_cstring (s,"set sasl-uses-ptr-name"))
+ mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k));
+ else if (!compare_cstring (s,"set network-filesystem-stat-bug"))
+ netfsstatbug = atoi (k);
+ else if (!compare_cstring (s,"set nntp-range"))
+ mail_parameters (NIL,SET_NNTPRANGE,(void *) atol (k));
+
+ else if (!file) { /* only allowed in system init */
+ if (!compare_cstring (s,"set black-box-directory") &&
+ !blackBoxDir) blackBoxDir = cpystr (k);
+ else if (!compare_cstring(s,"set black-box-default-home-directory")&&
+ blackBoxDir && !blackBoxDefaultHome)
+ blackBoxDefaultHome = cpystr (k);
+ else if (!compare_cstring (s,"set anonymous-home-directory") &&
+ !anonymousHome) anonymousHome = cpystr (k);
+ /* It's tempting to allow setting the CA path
+ * in a user init. However, that opens up a
+ * vector of attack big enough to drive a
+ * truck through... Resist the temptation.
+ */
+ else if (!compare_cstring (s,"set CA-certificate-path"))
+ sslCApath = cpystr (k);
+ else if (!compare_cstring (s,"set disable-plaintext"))
+ disablePlaintext = atoi (k);
+ else if (!compare_cstring (s,"set allowed-login-attempts"))
+ logtry = atoi (k);
+ else if (!compare_cstring (s,"set chroot-server"))
+ closedBox = atoi (k);
+ else if (!compare_cstring (s,"set restrict-mailbox-access"))
+ for (k = strtok_r (k,", ",&r); k; k = strtok_r (NIL,", ",&r)) {
+ if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT;
+ else if (!compare_cstring (k,"otherusers"))
+ restrictBox |= RESTRICTOTHERUSER;
+ else if (!compare_cstring (k,"all")) restrictBox = -1;
+ }
+ else if (!compare_cstring (s,"set advertise-the-world"))
+ advertisetheworld = atoi (k);
+ else if (!compare_cstring (s,"set limited-advertise"))
+ limitedadvertise = atoi (k);
+ else if (!compare_cstring
+ (s,"set disable-automatic-shared-namespaces"))
+ noautomaticsharedns = atoi (k);
+ else if (!compare_cstring (s,"set allow-user-config"))
+ allowuserconfig = atoi (k);
+ else if (!compare_cstring (s,"set allow-reverse-dns"))
+ mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k));
+ else if (!compare_cstring (s,"set k5-cp-uses-service-name"))
+ kerb_cp_svr_name = atoi (k);
+ /* must appear in file after any
+ * "set disable-plaintext" command! */
+ else if (!compare_cstring (s,"set plaintext-allowed-clients")) {
+ for (k = strtok_r (k,", ",&r); k && !tcp_isclienthost (k);
+ k = strtok_r (NIL,", ",&r));
+ if (k) disablePlaintext = 0;
+ }
+ }
+ }
+ }
+ } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n')));
+ if (f) fclose (f); /* flush the file */
+}
+
+/* INBOX create function for tmail/dmail use only
+ * Accepts: mail stream
+ * path name buffer, preloaded with driver-dependent path
+ * Returns: T on success, NIL on failure
+ *
+ * This routine is evil and a truly incredible kludge. It is private for
+ * tmail/dmail and is not supported for any other application.
+ */
+
+long path_create (MAILSTREAM *stream,char *path)
+{
+ long ret;
+ short rsave = restrictBox;
+ restrictBox = NIL; /* can't restrict */
+ if (blackBox) { /* if black box */
+ /* toss out driver dependent names */
+ sprintf (path,"%s/INBOX",mymailboxdir ());
+ blackBox = NIL; /* well that's evil - evil is going on */
+ ret = mail_create (stream,path);
+ blackBox = T; /* restore the box */
+ }
+ /* easy thing otherwise */
+ else ret = mail_create (stream,path);
+ restrictBox = rsave; /* restore restrictions */
+ return ret;
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) (unsigned long) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned long) data) alarm ((unsigned long) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/env_unix.h b/imap/src/osdep/unix/env_unix.h
new file mode 100644
index 00000000..365e5128
--- /dev/null
+++ b/imap/src/osdep/unix/env_unix.h
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+typedef struct dotlock_base {
+ char lock[MAILTMPLEN];
+ int pipei;
+ int pipeo;
+} DOTLOCK;
+
+
+/* Bits that can be set in restrictBox */
+
+#define RESTRICTROOT 0x1 /* restricted box doesn't allow root */
+#define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */
+
+/* Subscription definitions for UNIX */
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ())
+
+
+/* dorc() options */
+
+#define SYSCONFIG "/etc/c-client.cf"
+
+
+/* Special users */
+
+#define ANONYMOUSUSER "nobody" /* anonymous user */
+#define UNLOGGEDUSER "root" /* unlogged-in user */
+#define ADMINGROUP "mailadm" /* mail administrator group */
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+long dotlock_lock (char *file,DOTLOCK *base,int fd);
+long dotlock_unlock (DOTLOCK *base);
+int lockname (char *lock,char *fname,int op,long *pid);
+int lockfd (int fd,char *lock,int op);
+int lock_work (char *lock,void *sbuf,int op,long *pid);
+long chk_notsymlink (char *name,void *sbuf);
+void unlockfd (int fd,char *lock);
+long set_mbx_protections (char *mailbox,char *path);
+long get_dir_protection (char *mailbox);
+MAILSTREAM *user_flags (MAILSTREAM *stream);
+char *default_user_flag (unsigned long i);
+void dorc (char *file,long flag);
+long path_create (MAILSTREAM *stream,char *mailbox);
+void grim_pid_reap_status (int pid,int killreq,void *status);
+#define grim_pid_reap(pid,killreq) \
+ grim_pid_reap_status (pid,killreq,NIL)
+long safe_write (int fd,char *buf,long nbytes);
+void *arm_signal (int sig,void *action);
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]);
+long loginpw (struct passwd *pw,int argc,char *argv[]);
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[]);
+void *mm_blocknotify (int reason,void *data);
diff --git a/imap/src/osdep/unix/fdstring.c b/imap/src/osdep/unix/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/unix/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/unix/fdstring.h b/imap/src/osdep/unix/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/unix/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/unix/flockcyg.c b/imap/src/osdep/unix/flockcyg.c
new file mode 100644
index 00000000..46281829
--- /dev/null
+++ b/imap/src/osdep/unix/flockcyg.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Cygwin does not seem to have the design flaw in fcntl() locking that
+ * most other systems do (see flocksim.c for details). If some cretin
+ * decides to implement that design flaw, then Cygwin will have to use
+ * flocksim. Also, we don't test NFS either.
+ *
+ * However, Cygwin does have the Windows misfeature (introduced in NT 4.0)
+ * that you can not write to any segment which has a shared lock, and you
+ * can't lock a zero-byte segment either. This screws up the shared-write
+ * mailbox drivers (mbx, mtx, mx, and tenex). As a workaround, we'll only
+ * lock the first byte of the file, meaning that you can't write that byte
+ * shared. It's been suggested to lock the maximum off_t type, but that
+ * risks having a future version of Windows (or Cygwin) deciding that this
+ * also means "no lock".
+ */
+
+#undef flock /* name is used as a struct for fcntl */
+
+/* Emulator for flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure under BSD conditions
+ */
+
+int flocksim (int fd,int op)
+{
+ char tmp[MAILTMPLEN];
+ int logged = 0;
+ struct flock fl;
+ /* lock one bytes at byte 0 */
+ fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
+ case LOCK_EX: /* exclusive */
+ fl.l_type = F_WRLCK;
+ break;
+ case LOCK_SH: /* shared */
+ fl.l_type = F_RDLCK;
+ break;
+ case LOCK_UN: /* unlock */
+ fl.l_type = F_UNLCK;
+ break;
+ default: /* default */
+ errno = EINVAL;
+ return -1;
+ }
+ while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
+ if (errno != EINTR) {
+ /* Can't use switch here because these error codes may resolve to the
+ * same value on some systems.
+ */
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
+ sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno));
+ /* give the user a warning of what happened */
+ MM_NOTIFY (NIL,tmp,WARN);
+ if (!logged++) syslog (LOG_ERR,"%s",tmp);
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow things down for loops */
+ }
+ /* return failure for non-blocking lock */
+ else if (op & LOCK_NB) return -1;
+ }
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/flockcyg.h b/imap/src/osdep/unix/flockcyg.h
new file mode 100644
index 00000000..0bc53623
--- /dev/null
+++ b/imap/src/osdep/unix/flockcyg.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock() emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Cygwin does not seem to have the design flaw in fcntl() locking that
+ * most other systems do (see flocksim.c for details). If some cretin
+ * decides to implement that design flaw, then Cygwin will have to use
+ * flocksim. Also, we don't test NFS either
+ */
+
+
+#define flock flocksim /* use ours instead of theirs */
+
+#undef LOCK_SH
+#define LOCK_SH 1 /* shared lock */
+#undef LOCK_EX
+#define LOCK_EX 2 /* exclusive lock */
+#undef LOCK_NB
+#define LOCK_NB 4 /* no blocking */
+#undef LOCK_UN
+#define LOCK_UN 8 /* unlock */
+
+
+/* Function prototypes */
+
+int flocksim (int fd,int operation);
diff --git a/imap/src/osdep/unix/flocklnx.c b/imap/src/osdep/unix/flocklnx.c
new file mode 100644
index 00000000..ca0112ac
--- /dev/null
+++ b/imap/src/osdep/unix/flocklnx.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Safe File Lock for Linux
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 April 2005
+ * Last Edited: 30 August 2006
+ */
+
+#undef flock
+
+#include <sys/vfs.h>
+#ifndef NFS_SUPER_MAGIC
+#define NFS_SUPER_MAGIC 0x6969
+#endif
+
+int safe_flock (int fd,int op)
+{
+ struct statfs sfbuf;
+ char tmp[MAILTMPLEN];
+ int e;
+ int logged = 0;
+ /* Check for NFS because Linux 2.6 broke flock() on NFS. Instead of being
+ * a no-op, flock() on NFS now returns ENOLCK. Read
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=123415
+ * for the gruesome details.
+ */
+ /* check filesystem type */
+ while ((e = fstatfs (fd,&sfbuf)) && (errno == EINTR));
+ if (!e) switch (sfbuf.f_type) {
+ case NFS_SUPER_MAGIC: /* always a fast no-op on NFS */
+ break;
+ default: /* allow on other filesystem types */
+ /* do the lock */
+ while (flock (fd,op)) switch (errno) {
+ case EINTR: /* interrupt */
+ break;
+ case ENOLCK: /* lock table is full */
+ sprintf (tmp,"File locking failure: %s",strerror (errno));
+ mm_log (tmp,WARN); /* give the user a warning of what happened */
+ if (!logged++) syslog (LOG_ERR,tmp);
+ /* return failure if non-blocking lock */
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow down in case it loops */
+ break;
+ case EWOULDBLOCK: /* file is locked, LOCK_NB should be set */
+ if (op & LOCK_NB) return -1;
+ case EBADF: /* not valid open file descriptor */
+ case EINVAL: /* invalid operator */
+ default: /* other error code? */
+ sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno));
+ fatal (tmp);
+ }
+ break;
+ }
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/flocksim.c b/imap/src/osdep/unix/flocksim.c
new file mode 100644
index 00000000..82f07837
--- /dev/null
+++ b/imap/src/osdep/unix/flocksim.c
@@ -0,0 +1,920 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 11 October 2007
+ */
+
+#undef flock /* name is used as a struct for fcntl */
+
+#ifndef NOFSTATVFS /* thank you, SUN. NOT! */
+# ifndef NOFSTATVFS64
+# ifndef _LARGEFILE64_SOURCE
+# define _LARGEFILE64_SOURCE
+# endif /* _LARGEFILE64_SOURCE */
+# endif /* NOFSTATVFFS64 */
+#include <sys/statvfs.h>
+#endif /* NOFSTATVFS */
+
+#ifndef NSIG /* don't know if this can happen */
+#define NSIG 32 /* a common maximum */
+#endif
+
+/* Emulator for flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure under BSD conditions
+ */
+
+int flocksim (int fd,int op)
+{
+ char tmp[MAILTMPLEN];
+ int logged = 0;
+ struct stat sbuf;
+ struct ustat usbuf;
+ struct flock fl;
+ /* lock zero bytes at byte 0 */
+ fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
+ case LOCK_EX: /* exclusive */
+ fl.l_type = F_WRLCK;
+ break;
+ case LOCK_SH: /* shared */
+ fl.l_type = F_RDLCK;
+ break;
+ case LOCK_UN: /* unlock */
+ fl.l_type = F_UNLCK;
+ break;
+ default: /* default */
+ errno = EINVAL;
+ return -1;
+ }
+ /* always return success if disabled */
+ if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;
+
+ /* Make fcntl() locking of NFS files be a no-op the way it is with flock()
+ * on BSD. This is because the rpc.statd/rpc.lockd daemons don't work very
+ * well and cause cluster-wide hangs if you exercise them at all. The
+ * result of this is that you lose the ability to detect shared mail_open()
+ * on NFS-mounted files. If you are wise, you'll use IMAP instead of NFS
+ * for mail files.
+ *
+ * Sun alleges that it doesn't matter, and that they have fixed all the
+ * rpc.statd/rpc.lockd bugs. As of October 2006, that is still false.
+ *
+ * We need three tests for three major historical variants in SVR4:
+ * 1) In NFSv2, ustat() would return -1 in f_tinode for NFS.
+ * 2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".
+ * 3) When 64-bit filesystems were introduced, fstatvfs() would return
+ * EOVERFLOW; you have to use fstatvfs64() even though you don't care
+ * about any of the affected values.
+ *
+ * We can't use fstatfs() because fstatfs():
+ * . is documented as being deprecated in SVR4.
+ * . has inconsistent calling conventions (there are two additional int
+ * arguments on Solaris and I don't know what they do).
+ * . returns inconsistent statfs structs. On Solaris, the file system type
+ * is a short called f_fstyp. On AIX, it's an int called f_type that is
+ * documented as always being 0!
+ *
+ * For what it's worth, here's the scoop on fstatfs() elsewhere:
+ *
+ * On Linux, the file system type is a long called f_type that has a file
+ * system type code. A different module (flocklnx.c) uses this because
+ * some knothead "improved" flock() to return ENOLCK on NFS files instead
+ * of being a successful no-op. This "improvement" apparently has been
+ * reverted, but not before it got to many systems in the field.
+ *
+ * On BSD, it's a short called either f_otype or f_type that is documented
+ * as always being zero. Fortunately, BSD has flock() the way it's supposed
+ * to be, and none of this nonsense is necessary.
+ */
+ if (!fstat (fd,&sbuf)) { /* no hope of working if can't fstat()! */
+ /* Any base type that begins with "nfs" or "afs" is considered to be a
+ * network filesystem.
+ */
+#ifndef NOFSTATVFS
+ struct statvfs vsbuf;
+#ifndef NOFSTATVFS64
+ struct statvfs64 vsbuf64;
+ if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&
+ (vsbuf64.f_basetype[2] == 's') &&
+ ((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))
+ return 0;
+#endif /* NOFSTATVFS64 */
+ if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&
+ (vsbuf.f_basetype[2] == 's') &&
+ ((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))
+ return 0;
+#endif /* NOFSTATVFS */
+ if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;
+ }
+
+ /* do the lock */
+ while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
+ if (errno != EINTR) {
+ /* Can't use switch here because these error codes may resolve to the
+ * same value on some systems.
+ */
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
+ sprintf (tmp,"Unexpected file locking failure: %.100s",
+ strerror (errno));
+ /* give the user a warning of what happened */
+ MM_NOTIFY (NIL,tmp,WARN);
+ if (!logged++) syslog (LOG_ERR,"%s",tmp);
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow things down for loops */
+ }
+ /* return failure for non-blocking lock */
+ else if (op & LOCK_NB) return -1;
+ }
+ return 0; /* success */
+}
+
+/* Master/slave procedures for safe fcntl() locking.
+ *
+ * The purpose of this nonsense is to work around a bad bug in fcntl()
+ * locking. The cretins who designed it decided that a close() should
+ * release any locks made by that process on the file opened on that
+ * file descriptor. Never mind that the lock wasn't made on that file
+ * descriptor, but rather on some other file descriptor.
+ *
+ * This bug is on every implementation of fcntl() locking that I have
+ * tested. Fortunately, on BSD systems, OSF/1, and Linux, we can use the
+ * flock() system call which doesn't have this bug.
+ *
+ * Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
+ * locking and the working flock() locking.
+ *
+ * The program below can be used to demonstrate this problem. Be sure to
+ * let it run long enough for all the sleep() calls to finish.
+ */
+
+#if 0
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/file.h>
+
+main ()
+{
+ struct flock fl;
+ int fd,fd2;
+ char *file = "a.a";
+ if ((fd = creat (file,0666)) < 0)
+ perror ("TEST FAILED: can't create test file"),_exit (errno);
+ close (fd);
+ if (fork ()) { /* parent */
+ if ((fd = open (file,O_RDWR,0)) < 0) abort();
+ /* lock applies to entire file */
+ fl.l_whence = fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ fl.l_type = F_RDLCK;
+ if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
+ sleep (5);
+ if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
+ sleep (1);
+ puts ("parent test ready -- will hang here if locking works correctly");
+ close (fd2);
+ wait (0);
+ puts ("OS BUG: child terminated");
+ _exit (0);
+ }
+ else { /* child */
+ sleep (2);
+ if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
+ puts ("child test ready -- child will hang if no bug");
+ /* lock applies to entire file */
+ fl.l_whence = fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ fl.l_type = F_WRLCK;
+ if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
+ puts ("OS BUG: child got lock");
+ }
+}
+#endif
+
+/* Beware of systems such as AIX which offer flock() as a compatibility
+ * function that is just a jacket into fcntl() locking. The program below
+ * is a variant of the program above, only using flock(). It can be used
+ * to test to see if your system has real flock() or just a jacket into
+ * fcntl().
+ *
+ * Be sure to let it run long enough for all the sleep() calls to finish.
+ * If the program hangs, then flock() works and you can dispense with the
+ * use of this module (you lucky person!).
+ */
+
+#if 0
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/file.h>
+
+main ()
+{
+ int fd,fd2;
+ char *file = "a.a";
+ if ((fd = creat (file,0666)) < 0)
+ perror ("TEST FAILED: can't create test file"),_exit (errno);
+ close (fd);
+ if (fork ()) { /* parent */
+ if ((fd = open (file,O_RDWR,0)) < 0) abort();
+ if (flock (fd,LOCK_SH) == -1) abort ();
+ sleep (5);
+ if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
+ sleep (1);
+ puts ("parent test ready -- will hang here if flock() works correctly");
+ close (fd2);
+ wait (0);
+ puts ("OS BUG: child terminated");
+ _exit (0);
+ }
+ else { /* child */
+ sleep (2);
+ if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
+ puts ("child test ready -- child will hang if no bug");
+ if (flock (fd,LOCK_EX) == -1) abort ();
+ puts ("OS BUG: child got lock");
+ }
+}
+#endif
+
+/* Master/slave details
+ *
+ * On broken systems, we invoke an inferior fork to execute any driver
+ * dispatches which are likely to tickle this bug; specifically, any
+ * dispatch which may fiddle with a mailbox that is already selected. As
+ * of this writing, these are: delete, rename, status, scan, copy, and append.
+ *
+ * Delete and rename are pretty marginal, yet there are certain clients
+ * (e.g. Outlook Express) that really want to delete or rename the selected
+ * mailbox. The same is true of status, but there are people (such as the
+ * authors of Entourage) who don't understand why status of the selected
+ * mailbox is bad news.
+ *
+ * However, in copy and append it is reasonable to do this to a selected
+ * mailbox. Although scanning the selected mailbox isn't particularly
+ * sensible, it's hard to avoid due to wildcards.
+ *
+ * It is still possible for an application to trigger the bug by doing
+ * mail_open() on the same mailbox twice. Don't do it.
+ *
+ * Once the slave is invoked, the master only has to read events from the
+ * slave's output (see below for these events) and translate these events
+ * to the appropriate c-client callback. When end of file occurs on the pipe,
+ * the master reads the slave's exit status and uses that as the function
+ * return. The append master is slightly more complicated because it has to
+ * send data back to the slave (see below).
+ *
+ * The slave takes callback events from the driver which otherwise would
+ * pass to the main program. Only those events which a slave can actually
+ * encounter are covered here; for example mm_searched() and mm_list() are
+ * not covered since a slave never does the operations that trigger these.
+ * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded
+ * by the slave since the master will generate these events for itself.
+ *
+ * The other events cause the slave to write a newline-terminated string to
+ * its output. The first character of string indicates the event: S for
+ * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for
+ * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append
+ * argument callback. Most of these events also carry data, which carried as
+ * text space-delimited in the string.
+ *
+ * Append argument callback requires the master to provide the slave with
+ * data in the slave's input. The first thing that the master provides is
+ * either a "+" (master has data for the slave) or a "-" (master has no data).
+ * If the master has data, it will then send the flags, internal date, and
+ * message text, each as <text octet count><SPACE><text>.
+ */
+
+/* It should be alright for lockslavep to be a global, since it will always
+ * be zero in the master (which is where threads would be). The slave won't
+ * ever thread, since any driver which threads in its methods probably can't
+ * use fcntl() locking so won't have DR_LOCKING in its driver flags
+ *
+ * lockslavep can not be a static, since it's used by the dispatch macros.
+ */
+
+int lockslavep = 0; /* non-zero means slave process for locking */
+static int lockproxycopy = 0; /* non-zero means redo copy as proxy */
+FILE *slavein = NIL; /* slave input */
+FILE *slaveout = NIL; /* slave output */
+
+
+/* Common master
+ * Accepts: permitted stream
+ * append callback (append calls only, else NIL)
+ * data for callback (append calls only, else NIL)
+ * Returns: (master) T if slave succeeded, NIL if slave failed
+ * (slave) NIL always, with lockslavep non-NIL
+ */
+
+static long master (MAILSTREAM *stream,append_t af,void *data)
+{
+ MAILSTREAM *st;
+ MAILSTATUS status;
+ STRING *message;
+ FILE *pi,*po;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ long ret = NIL;
+ unsigned long i,j;
+ int c,pid,pipei[2],pipeo[2];
+ char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];
+ lockproxycopy = NIL; /* not doing a lock proxycopy */
+ /* make pipe from slave */
+ if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);
+ else if (pipe (pipeo) < 0) {
+ mm_log ("Can't create output pipe",ERROR);
+ close (pipei[0]); close (pipei[1]);
+ }
+ else if ((pid = fork ()) < 0) {/* make slave */
+ mm_log ("Can't create execution process",ERROR);
+ close (pipei[0]); close (pipei[1]);
+ close (pipeo[0]); close (pipeo[1]);
+ }
+ else if (lockslavep = !pid) { /* are we slave or master? */
+ alarm (0); /* slave doesn't have alarms or signals */
+ for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);
+ if (!(slavein = fdopen (pipeo[0],"r")) ||
+ !(slaveout = fdopen (pipei[1],"w")))
+ fatal ("Can't do slave pipe buffered I/O");
+ close (pipei[0]); /* close parent's side of the pipes */
+ close (pipeo[1]);
+ }
+
+ else { /* master process */
+ void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);
+ close (pipei[1]); /* close slave's side of the pipes */
+ close (pipeo[0]);
+ if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))
+ fatal ("Can't do master pipe buffered I/O");
+ /* do slave events until EOF */
+ /* read event */
+ while (fgets (event,MAILTMPLEN-1,pi)) {
+ if (!(s = strchr (event,'\n'))) {
+ sprintf (tmp,"Execution process event string too long: %.500s",event);
+ fatal (tmp);
+ }
+ *s = '\0'; /* tie off event at end of line */
+ switch (event[0]) { /* analyze event */
+ case 'A': /* append callback */
+ if ((*af) (NIL,data,&s,&t,&message)) {
+ if (i = message ? SIZE (message) : 0) {
+ if (!s) s = ""; /* default values */
+ if (!t) t = "";
+ }
+ else s = t = ""; /* no flags or date if no message */
+ errno = NIL; /* reset last error */
+ /* build response */
+ if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)
+ fatal ("Failed to pipe append command");
+ /* write message text */
+ if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {
+ sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",
+ i,message->size,c,strerror (errno));
+ fatal (tmp);
+ } while (--i);
+ }
+ else putc ('-',po); /* append error */
+ fflush (po);
+ break;
+ case '&': /* slave wants a proxycopy? */
+ lockproxycopy = T;
+ break;
+
+ case 'L': /* mm_log() */
+ i = strtoul (event+1,&s,10);
+ if (!s || (*s++ != ' ')) {
+ sprintf (tmp,"Invalid log event arguments: %.500s",event);
+ fatal (tmp);
+ }
+ mm_log (s,i);
+ break;
+ case 'N': /* mm_notify() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ i = strtoul (s,&s,10);/* get severity */
+ if (s && (*s++ == ' ')) {
+ mm_notify ((st == stream) ? stream : NIL,s,i);
+ break;
+ }
+ }
+ sprintf (tmp,"Invalid notify event arguments: %.500s",event);
+ fatal (tmp);
+
+ case 'S': /* mm_status() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ status.flags = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.messages = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.recent = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.unseen = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.uidnext = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.uidvalidity = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ mm_status ((st == stream) ? stream : NIL,s,&status);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ sprintf (tmp,"Invalid status event arguments: %.500s",event);
+ fatal (tmp);
+ case 'C': /* mm_critical() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ mm_critical ((st == stream) ? stream : NIL);
+ break;
+ case 'X': /* mm_nocritical() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ mm_nocritical ((st == stream) ? stream : NIL);
+ break;
+
+ case 'D': /* mm_diskerror() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ i = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ j = (long) strtoul (s,NIL,10);
+ if (st == stream) /* let's hope it's on usable stream */
+ putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po);
+ else if (j) { /* serious diskerror on slave-created stream */
+ mm_log ("Retrying disk write to avoid mailbox corruption!",WARN);
+ sleep (5); /* give some time for it to clear up */
+ putc ('-',po); /* don't abort */
+ }
+ else { /* recoverable on slave-created stream */
+ mm_log ("Error on disk write",ERROR);
+ putc ('+',po); /* so abort it */
+ }
+ fflush (po); /* force it out either way */
+ break;
+ }
+ }
+ sprintf (tmp,"Invalid diskerror event arguments: %.500s",event);
+ fatal (tmp);
+ case 'F': /* mm_fatal() */
+ mm_fatal (event+1);
+ break;
+ default: /* random lossage */
+ sprintf (tmp,"Unknown event from execution process: %.500s",event);
+ fatal (tmp);
+ }
+ }
+ fclose (pi); fclose (po); /* done with the pipes */
+ /* get slave status */
+ grim_pid_reap_status (pid,NIL,&ret);
+ if (ret & 0177) { /* signal or stopped */
+ sprintf (tmp,"Execution process terminated abnormally (%lx)",ret);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ else ret >>= 8; /* return exit code */
+ (*bn) (BLOCK_NONSENSITIVE,blockdata);
+ }
+ return ret; /* return status */
+}
+
+/* Safe driver calls */
+
+
+/* Safely delete mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx));
+ return ret;
+}
+
+
+/* Safely rename mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname));
+ return ret;
+}
+
+
+/* Safely get status of mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->status) (stream,mbx,flags));
+ return ret;
+}
+
+
+/* Scan file for contents
+ * Accepts: driver to call under slave
+ * file name
+ * desired contents
+ * length of contents
+ * length of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ long ret = master (NIL,NIL,NIL);
+ if (lockslavep) exit (scan_contents (dtb,name,contents,csiz,fsiz));
+ return ret;
+}
+
+/* Safely copy message to mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) {
+ /* don't do proxycopy in slave */
+ if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy);
+ exit ((*dtb->copy) (stream,seq,mbx,flags));
+ }
+ /* do any proxycopy in master */
+ if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags);
+ return ret;
+}
+
+
+/* Append package for slave */
+
+typedef struct append_data {
+ int first; /* flag indicating first message */
+ char *flags; /* message flags */
+ char *date; /* message date */
+ char *msg; /* message text */
+ STRING message; /* message stringstruct */
+} APPENDDATA;
+
+
+/* Safely append message to mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
+ void *data)
+{
+ long ret = master (stream,af,data);
+ if (lockslavep) {
+ APPENDDATA ad;
+ ad.first = T; /* initialize initial append package */
+ ad.flags = ad.date = ad.msg = NIL;
+ exit ((*dtb->append) (stream,mbx,slave_append,&ad));
+ }
+ return ret;
+}
+
+/* Slave callbacks */
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ int i,c;
+ fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu ",
+ (unsigned long) stream,status->flags,status->messages,status->recent,
+ status->unseen,status->uidnext,status->uidvalidity,mailbox);
+ /* yow! are we paranoid enough yet? */
+ for (i = 0; (i < 500) && (c = *mailbox++); ++i) switch (c) {
+ case '\r': case '\n': /* newline in a mailbox name? */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void slave_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ int i,c;
+ fprintf (slaveout,"N%lx %lu ",(unsigned long) stream,errflg);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* or embedded newline */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void slave_log (char *string,long errflg)
+{
+ int i,c;
+ fprintf (slaveout,"L%lu ",errflg);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* or embedded newline */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void slave_critical (MAILSTREAM *stream)
+{
+ fprintf (slaveout,"C%lx\n",(unsigned long) stream);
+ fflush (slaveout);
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void slave_nocritical (MAILSTREAM *stream)
+{
+ fprintf (slaveout,"X%lx\n",(unsigned long) stream);
+ fflush (slaveout);
+}
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long slave_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ char tmp[MAILTMPLEN];
+ int c;
+ long ret = NIL;
+ fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious);
+ fflush (slaveout);
+ switch (c = getc (slavein)) {
+ case EOF: /* pipe broken */
+ slave_fatal ("Pipe broken reading diskerror response");
+ case '+': /* user wants to abort */
+ ret = LONGT;
+ case '-': /* no abort */
+ break;
+ default:
+ sprintf (tmp,"Unknown master response for diskerror: %c",c);
+ slave_fatal (tmp);
+ }
+ return ret;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ * Does not return
+ */
+
+void slave_fatal (char *string)
+{
+ int i,c;
+ syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.500s",string);
+ putc ('F',slaveout);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* newline in a mailbox name? */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+ abort (); /* die */
+}
+
+/* Append read buffer
+ * Accepts: number of bytes to read
+ * error message if fails
+ * Returns: read-in string
+ */
+
+static char *slave_append_read (unsigned long n,char *error)
+{
+#if 0
+ unsigned long i;
+#endif
+ int c;
+ char *t,tmp[MAILTMPLEN];
+ char *s = (char *) fs_get (n + 1);
+ s[n] = '\0';
+#if 0
+ /* This doesn't work on Solaris with GCC. I think that it's a C library
+ * bug, since the problem only shows up if the application does fread()
+ * on some other file
+ */
+ for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i);
+#else
+ for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n);
+#endif
+ if (n) {
+ sprintf(tmp,"Pipe broken reading %.100s with %lu bytes remaining",error,n);
+ slave_fatal (tmp);
+ }
+ return s;
+}
+
+/* Append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ char tmp[MAILTMPLEN];
+ unsigned long n;
+ int c;
+ APPENDDATA *ad = (APPENDDATA *) data;
+ /* flush text of previous message */
+ if (ad->flags) fs_give ((void **) &ad->flags);
+ if (ad->date) fs_give ((void **) &ad->date);
+ if (ad->msg) fs_give ((void **) &ad->msg);
+ *flags = *date = NIL; /* assume no flags or date */
+ fputs ("A\n",slaveout); /* tell master we're doing append callback */
+ fflush (slaveout);
+ switch (c = getc (slavein)) { /* what did master say? */
+ case '+': /* have message, get size of flags */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after flag size %lu",n);
+ sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) *flags = ad->flags = slave_append_read (n,"flags");
+ /* get size of date */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after date size %lu",n);
+ else sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) *date = ad->date = slave_append_read (n,"date");
+ /* get size of message */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after message size %lu",n);
+ sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) { /* make buffer for message */
+ ad->msg = slave_append_read (n,"message");
+ /* initialize stringstruct */
+ INIT (&ad->message,mail_string,(void *) ad->msg,n);
+ ad->first = NIL; /* no longer first message */
+ *message = &ad->message; /* return message */
+ }
+ else *message = NIL; /* empty message */
+ return LONGT;
+ case '-': /* error */
+ *message = NIL; /* set stop */
+ break;
+ case EOF: /* end of file */
+ slave_fatal ("Pipe broken reading append response");
+ default: /* unknown event */
+ sprintf (tmp,"Unknown master response for append: %c",c);
+ slave_fatal (tmp);
+ }
+ return NIL; /* return failure */
+}
+
+/* Proxy copy across mailbox formats
+ * Accepts: mail stream
+ * sequence to copy on this stream
+ * destination mailbox
+ * option flags
+ * Returns: T if success, else NIL
+ */
+
+long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options)
+{
+ fputs ("&\n",slaveout); /* redo copy as append */
+ fflush (slaveout);
+ return NIL; /* failure for now */
+}
diff --git a/imap/src/osdep/unix/flocksim.h b/imap/src/osdep/unix/flocksim.h
new file mode 100644
index 00000000..719eba46
--- /dev/null
+++ b/imap/src/osdep/unix/flocksim.h
@@ -0,0 +1,117 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock() emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 20 December 2006
+ */
+
+
+#include "dummy.h" /* get scan_contents() prototype */
+
+#define flock flocksim /* use ours instead of theirs */
+
+#undef LOCK_SH
+#define LOCK_SH 1 /* shared lock */
+#undef LOCK_EX
+#define LOCK_EX 2 /* exclusive lock */
+#undef LOCK_NB
+#define LOCK_NB 4 /* no blocking */
+#undef LOCK_UN
+#define LOCK_UN 8 /* unlock */
+
+/* Safe locking definitions */
+
+extern int lockslavep; /* non-zero means slave process */
+
+#undef SAFE_DELETE
+#define SAFE_DELETE(dtb,stream,mbx) (dtb->flags & DR_LOCKING) ? \
+ safe_delete (dtb,stream,mbx) : (*dtb->mbxdel) (stream,mbx)
+#undef SAFE_RENAME
+#define SAFE_RENAME(dtb,stream,old,newname) (dtb->flags & DR_LOCKING) ? \
+ safe_rename (dtb,stream,old,newname) : (*dtb->mbxren) (stream,old,newname)
+#undef SAFE_STATUS
+#define SAFE_STATUS(dtb,stream,mbx,bits) (dtb->flags & DR_LOCKING) ? \
+ safe_status (dtb,stream,mbx,bits) : (*dtb->status) (stream,mbx,bits)
+#undef SAFE_SCAN_CONTENTS
+#define SAFE_SCAN_CONTENTS(dtb,name,contents,csiz,fsiz) \
+ (!dtb || (dtb->flags & DR_LOCKING)) ? \
+ safe_scan_contents (dtb,name,contents,csiz,fsiz) : \
+ scan_contents (dtb,name,contents,csiz,fsiz)
+#undef SAFE_COPY
+#define SAFE_COPY(dtb,stream,seq,mbx,bits) (dtb->flags & DR_LOCKING) ? \
+ safe_copy (dtb,stream,seq,mbx,bits) : (*dtb->copy) (stream,seq,mbx,bits)
+#undef SAFE_APPEND
+#define SAFE_APPEND(dtb,stream,mbx,af,data) (dtb->flags & DR_LOCKING) ? \
+ safe_append (dtb,stream,mbx,af,data) : (*dtb->append) (stream,mbx,af,data)
+
+#undef MM_EXISTS
+#define MM_EXISTS (lockslavep ? slave_exists : mm_exists)
+#undef MM_EXPUNGED
+#define MM_EXPUNGED (lockslavep ? slave_expunged : mm_expunged)
+#undef MM_FLAGS
+#define MM_FLAGS (lockslavep ? slave_flags : mm_flags)
+#undef MM_NOTIFY
+#define MM_NOTIFY (lockslavep ? slave_notify : mm_notify)
+#undef MM_STATUS
+#define MM_STATUS (lockslavep ? slave_status : mm_status)
+#undef MM_LOG
+#define MM_LOG (lockslavep ? slave_log : mm_log)
+#undef MM_CRITICAL
+#define MM_CRITICAL (lockslavep ? slave_critical : mm_critical)
+#undef MM_NOCRITICAL
+#define MM_NOCRITICAL (lockslavep ? slave_nocritical : mm_nocritical)
+#undef MM_DISKERROR
+#define MM_DISKERROR (lockslavep ? slave_diskerror : mm_diskerror)
+#undef MM_FATAL
+#define MM_FATAL (lockslavep ? slave_fatal : mm_fatal)
+#undef MM_APPEND
+#define MM_APPEND(af) (lockslavep ? slave_append : (*af))
+
+/* Function prototypes */
+
+int flocksim (int fd,int operation);
+
+long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx);
+long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname);
+long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags);
+long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags);
+long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
+ void *data);
+
+void slave_exists (MAILSTREAM *stream,unsigned long number);
+void slave_expunged (MAILSTREAM *stream,unsigned long number);
+void slave_flags (MAILSTREAM *stream,unsigned long number);
+void slave_notify (MAILSTREAM *stream,char *string,long errflg);
+void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+void slave_log (char *string,long errflg);
+void slave_critical (MAILSTREAM *stream);
+void slave_nocritical (MAILSTREAM *stream);
+long slave_diskerror (MAILSTREAM *stream,long errcode,long serious);
+void slave_fatal (char *string);
+long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
diff --git a/imap/src/osdep/unix/fs_unix.c b/imap/src/osdep/unix/fs_unix.c
new file mode 100644
index 00000000..b433e1b4
--- /dev/null
+++ b/imap/src/osdep/unix/fs_unix.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ free (*block);
+ *block = NIL;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
diff --git a/imap/src/osdep/unix/fsync.c b/imap/src/osdep/unix/fsync.c
new file mode 100644
index 00000000..7a63b0f8
--- /dev/null
+++ b/imap/src/osdep/unix/fsync.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File sync emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD fsync() call
+ * Accepts: file name
+ * Returns: 0 if successful, -1 if failure
+ */
+
+int fsync (int fd)
+{
+ sync ();
+ return 0;
+}
diff --git a/imap/src/osdep/unix/ftl_unix.c b/imap/src/osdep/unix/ftl_unix.c
new file mode 100644
index 00000000..e927360d
--- /dev/null
+++ b/imap/src/osdep/unix/ftl_unix.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ MM_FATAL (string); /* pass up the string */
+ syslog (LOG_ALERT,"IMAP toolkit crash: %.100s",string);
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/unix/gethstid.c b/imap/src/osdep/unix/gethstid.c
new file mode 100644
index 00000000..01516976
--- /dev/null
+++ b/imap/src/osdep/unix/gethstid.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Get host ID emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ /* No gethostid() here, so just fake it and hope things turn out okay. */
+ return 0xdeadface;
+}
diff --git a/imap/src/osdep/unix/getspnam.c b/imap/src/osdep/unix/getspnam.c
new file mode 100644
index 00000000..cc01daf8
--- /dev/null
+++ b/imap/src/osdep/unix/getspnam.c
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: 64-bit getsockname()/getpeername() emulator
+ *
+ * Author: Mark Crispin from code contributed by Chris Ross
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 November 2004
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Jacket into getpeername()
+ * Accepts: socket
+ * pointer to socket address
+ * void pointer to len
+ * Returns: 0 if success, -1 if error
+ */
+
+int Getpeername (int s,struct sockaddr *name,size_t *namelen)
+{
+ int ret;
+ socklen_t len = (socklen_t) *namelen;
+ ret = getpeername (s,name,&len);
+ *namelen = (size_t) len;
+ return ret;
+}
+
+
+/* Jacket into getsockname()
+ * Accepts: socket
+ * pointer to socket address
+ * void pointer to len
+ * Returns: 0 if success, -1 if error
+ */
+
+int Getsockname (int s,struct sockaddr *name,size_t *namelen)
+{
+ int ret;
+ socklen_t len = (socklen_t) *namelen;
+ ret = getsockname (s,name,&len);
+ *namelen = (size_t) len;
+ return ret;
+}
+
+
+#define getpeername Getpeername
+#define getsockname Getsockname
diff --git a/imap/src/osdep/unix/gr_wait.c b/imap/src/osdep/unix/gr_wait.c
new file mode 100644
index 00000000..626c57e8
--- /dev/null
+++ b/imap/src/osdep/unix/gr_wait.c
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- wait() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ int r;
+ if (killreq) {
+ kill (pid,SIGHUP); /* kill if not already dead */
+ alarm (10); /* in case we get hosed */
+ while (((r = wait (NIL)) != pid) &&
+ ((r > 0) || ((errno != ECHILD) && (errno != EINTR))));
+ alarm (0); /* cancel the alarm */
+ }
+ else while (((r = wait (status)) != pid) && ((r > 0) || (errno != ECHILD)));
+}
diff --git a/imap/src/osdep/unix/gr_wait4.c b/imap/src/osdep/unix/gr_wait4.c
new file mode 100644
index 00000000..f394b84c
--- /dev/null
+++ b/imap/src/osdep/unix/gr_wait4.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- wait4() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((wait4 (pid,status,NIL,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/unix/gr_waitp.c b/imap/src/osdep/unix/gr_waitp.c
new file mode 100644
index 00000000..8b230f9d
--- /dev/null
+++ b/imap/src/osdep/unix/gr_waitp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- waitpid() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/unix/ip4_unix.c b/imap/src/osdep/unix/ip4_unix.c
new file mode 100644
index 00000000..23d399e1
--- /dev/null
+++ b/imap/src/osdep/unix/ip4_unix.c
@@ -0,0 +1,184 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char **adl,tmp[MAILTMPLEN];
+ struct hostent *he;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ adl = he->h_addr_list;
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = (void *) adl;
+ }
+ else { /* error */
+ adl = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (adl = (char **) *next)) *next = ++adl;
+ else adl = NIL; /* failure */
+ return adl ? (void *) *adl : NIL;
+}
diff --git a/imap/src/osdep/unix/ip6_unix.c b/imap/src/osdep/unix/ip6_unix.c
new file mode 100644
index 00000000..c15dfbc0
--- /dev/null
+++ b/imap/src/osdep/unix/ip6_unix.c
@@ -0,0 +1,288 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv6 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+
+/*
+ * There is some amazingly bad design in IPv6 sockets.
+ *
+ * Supposedly, the new getnameinfo() and getaddrinfo() functions create an
+ * abstraction that is not dependent upon IPv4 or IPv6. However, the
+ * definition of getnameinfo() requires that the caller pass the length of
+ * the sockaddr instead of deriving it from sa_family. The man page says
+ * that there's an sa_len member in the sockaddr, but actually there isn't.
+ * This means that any caller to getnameinfo() and getaddrinfo() has to know
+ * the size for the protocol family used by that sockaddr.
+ *
+ * The new sockaddr_in6 is bigger than the generic sockaddr (which is what
+ * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect).
+ * Rather than increase the size of sockaddr, there's a new sockaddr_storage
+ * which is only usable for allocating space.
+ */
+
+#define SADRLEN sizeof (struct sockaddr_storage)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+#define SADR6(sadr) ((struct sockaddr_in6 *) sadr)
+#define SADR6LEN sizeof (struct sockaddr_in6)
+#define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr
+#define ADR6LEN sizeof (struct in6_addr)
+#define SADR6PORT(sadr) SADR6 (sadr)->sin6_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ }
+ return "NON-IP";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ switch (sadr->sa_family) {
+ case PF_INET:
+ return ntohs (SADR4PORT (sadr));
+ case PF_INET6:
+ return ntohs (SADR6PORT (sadr));
+ }
+ return -1;
+}
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+
+{
+ char tmp[MAILTMPLEN];
+ static struct addrinfo *hints;
+ struct addrinfo *ai;
+ void *adr = NIL;
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ hints->ai_family = AF_UNSPEC;/* allow any address family */
+ hints->ai_socktype = SOCK_STREAM;
+ /* numeric name only */
+ hints->ai_flags = AI_NUMERICHOST;
+ }
+ /* case-independent lookup */
+ if (text && (strlen (text) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) {
+ switch (*family = ai->ai_family) {
+ case AF_INET: /* IPv4 */
+ adr = fs_get (*len = ADR4LEN);
+ memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len);
+ break;
+ case AF_INET6: /* IPv6 */
+ adr = fs_get (*len = ADR6LEN);
+ memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len);
+ break;
+ }
+ freeaddrinfo (ai); /* free addrinfo */
+ }
+ return adr;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ case AF_INET6: /* IPv6 */
+ sadr->sa_family = PF_INET6;
+ /* copy host address */
+ memcpy (&SADR6ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR6PORT (sadr) = htons (port);
+ *len = SADR6LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ }
+ return NIL;
+}
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ struct addrinfo *cur = NIL;
+ static struct addrinfo *hints;
+ static struct addrinfo *ai = NIL;
+ static char lcname[MAILTMPLEN];
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ /* allow any address family */
+ hints->ai_family = AF_UNSPEC;
+ hints->ai_socktype = SOCK_STREAM;
+ /* need canonical name */
+ hints->ai_flags = AI_CANONNAME;
+ }
+ if (name) { /* name supplied? */
+ if (ai) {
+ freeaddrinfo (ai); /* free old addrinfo */
+ ai = NIL;
+ }
+ /* case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) {
+ cur = ai; /* current block */
+ if (canonical) /* set canonical name */
+ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname;
+ /* remember as next block */
+ if (next) *next = (void *) ai;
+ }
+ else { /* error */
+ cur = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) {
+ *next = cur; /* set as last address */
+ /* set canonical in case changed */
+ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname;
+ }
+
+ if (cur) { /* got data? */
+ if (family) *family = cur->ai_family;
+ switch (cur->ai_family) {
+ case AF_INET:
+ if (len) *len = ADR4LEN;
+ return (void *) &SADR4ADR (cur->ai_addr);
+ case AF_INET6:
+ if (len) *len = ADR6LEN;
+ return (void *) &SADR6ADR (cur->ai_addr);
+ }
+ }
+ if (len) *len = 0; /* error return */
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/ipo_unix.c b/imap/src/osdep/unix/ipo_unix.c
new file mode 100644
index 00000000..665fb2e8
--- /dev/null
+++ b/imap/src/osdep/unix/ipo_unix.c
@@ -0,0 +1,181 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 old routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *he;
+ void *ret = NIL;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = 0;
+ ret = he->h_addr; /* set result to this one and only block */
+ }
+ else { /* error */
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ return ret; /* return result */
+}
diff --git a/imap/src/osdep/unix/kerb_mit.c b/imap/src/osdep/unix/kerb_mit.c
new file mode 100644
index 00000000..82e6c936
--- /dev/null
+++ b/imap/src/osdep/unix/kerb_mit.c
@@ -0,0 +1,111 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIT Kerberos routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 March 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ *
+ * Note that this routine will probably return T only if the process is root.
+ * This is alright since the server is probably still root at this point.
+ */
+
+long kerberos_server_valid ()
+{
+ krb5_context ctx;
+ krb5_keytab kt;
+ krb5_kt_cursor csr;
+ long ret = NIL;
+ /* make a context */
+ if (!krb5_init_context (&ctx)) {
+ /* get default keytab */
+ if (!krb5_kt_default (ctx,&kt)) {
+ /* can do server if have good keytab */
+ if (!krb5_kt_start_seq_get (ctx,kt,&csr) &&
+ !krb5_kt_end_seq_get (ctx,kt,&csr)) ret = LONGT;
+ krb5_kt_close (ctx,kt); /* finished with keytab */
+ }
+ krb5_free_context (ctx); /* finished with context */
+ }
+ return ret;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ switch (error) {
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_FCC_NOFILE: /* MIT */
+ case KRB5_CC_NOTFOUND: /* Heimdal */
+ return LONGT;
+ }
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ krb5_context ctx;
+ krb5_principal prnc;
+ char kuser[NETMAXUSER];
+ char *ret = NIL;
+ /* make a context */
+ if (!krb5_init_context (&ctx)) {
+ /* build principal */
+ if (!krb5_parse_name (ctx,authuser,&prnc)) {
+ /* can get local name for this principal? */
+ if (!krb5_aname_to_localname (ctx,prnc,NETMAXUSER-1,kuser)) {
+ /* yes, local name permitted login as user? */
+ if (authserver_login (user,kuser,argc,argv) ||
+ authserver_login (lcase (user),kuser,argc,argv))
+ ret = myusername (); /* yes, return user name */
+ }
+ krb5_free_principal (ctx,prnc);
+ }
+ krb5_free_context (ctx); /* finished with context */
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_bsi.c b/imap/src/osdep/unix/log_bsi.c
new file mode 100644
index 00000000..5db7415d
--- /dev/null
+++ b/imap/src/osdep/unix/log_bsi.c
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSI login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ long ret = NIL;
+ uid_t euid = geteuid ();
+ login_cap_t *lc = login_getclass (pw->pw_class);
+ /* have class and can become user? */
+ if (lc && !seteuid (pw->pw_uid)) {
+ /* ask for approval */
+ if (auth_approve (lc,pw->pw_name,
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL)) <= 0)
+ seteuid (euid); /* not approved, restore root euid */
+ else { /* approved */
+ seteuid (euid); /* restore former root euid first */
+ setsid (); /* ensure we are session leader */
+ /* log the guy in */
+ ret = !setusercontext (lc,pw,pw->pw_uid,LOGIN_SETALL&~LOGIN_SETPATH);
+ }
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_cyg.c b/imap/src/osdep/unix/log_cyg.c
new file mode 100644
index 00000000..b60a4a7d
--- /dev/null
+++ b/imap/src/osdep/unix/log_cyg.c
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Cygwin login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ /* must be same user name as last checkpw() */
+ if (!(cyg_user && !strcmp (pw->pw_name,cyg_user))) return NIL;
+ /* do the ImpersonateLoggedOnUser() */
+ cygwin_set_impersonation_token (cyg_hdl);
+
+ return !(setgid (pw->pw_gid) || initgroups (cyg_user,pw->pw_gid) ||
+ setuid (uid));
+}
diff --git a/imap/src/osdep/unix/log_old.c b/imap/src/osdep/unix/log_old.c
new file mode 100644
index 00000000..2cb10529
--- /dev/null
+++ b/imap/src/osdep/unix/log_old.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login for very old UNIX systems
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ return !(setgid (pw->pw_gid) || setuid (pw->pw_uid));
+}
diff --git a/imap/src/osdep/unix/log_os4.c b/imap/src/osdep/unix/log_os4.c
new file mode 100644
index 00000000..dec704e5
--- /dev/null
+++ b/imap/src/osdep/unix/log_os4.c
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OSF/1 (Digital UNIX) 4 login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ int i;
+ char *s;
+ char *name = cpystr (pw->pw_name);
+ char *host = cpystr (tcp_clienthost ());
+ uid_t uid = pw->pw_uid;
+ long ret = NIL;
+ /* tie off address */
+ if (s = strchr (host,' ')) *s = '\0';
+ if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */
+ memmove (host,host+1,i = strlen (host + 2));
+ host[i] = '\0';
+ }
+ if (sia_become_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,NIL,
+ SIA_BEU_REALLOGIN|SIA_BEU_OKROOTDIR) != SIASUCCESS)
+ setreuid (0,0); /* make sure have root again */
+ /* probable success, complete login */
+ else ret = (!setreuid (0,0) && !setuid (uid));
+ fs_give ((void **) &name);
+ fs_give ((void **) &host);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_sec.c b/imap/src/osdep/unix/log_sec.c
new file mode 100644
index 00000000..f2641896
--- /dev/null
+++ b/imap/src/osdep/unix/log_sec.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setluid (uid) || setgid (pw->pw_gid) ||
+ initgroups (name,pw->pw_gid) || setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_std.c b/imap/src/osdep/unix/log_std.c
new file mode 100644
index 00000000..a36fa88f
--- /dev/null
+++ b/imap/src/osdep/unix/log_std.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) ||
+ setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_sv4.c b/imap/src/osdep/unix/log_sv4.c
new file mode 100644
index 00000000..4738ce12
--- /dev/null
+++ b/imap/src/osdep/unix/log_sv4.c
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name) || setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mbx.c b/imap/src/osdep/unix/mbx.c
new file mode 100644
index 00000000..1ece5d8d
--- /dev/null
+++ b/imap/src/osdep/unix/mbx.c
@@ -0,0 +1,1855 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+
+/* Kludge to make Cygwin happy */
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned long lastpid; /* PID of last writer */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mbx_file (char *dst,char *name);
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mbx_status, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * scratch buffer
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ time_t tp[2];
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
+ ((fd = open (tmp,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* bogus format */
+ /* I love cretinous C compilers -- don't you? */
+ if (read (fd,hdr,HDRSIZE) == HDRSIZE)
+ if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
+ (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') &&
+ (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]))
+ if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
+ isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
+ isxdigit (c = hdr[15]) && isxdigit (hdr[16]))
+ if (isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
+ isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
+ (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockfd (fd,lock,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr,HDRSIZE) != HDRSIZE)) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),
+ 0,sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) &&
+ (j = strtol (s+1,&s,10)) && (*s == ';') &&
+ (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) &&
+ (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (tmp,tp); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mbx_file ((char *) value,"INBOX");
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = mbx_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if made directory */
+ if ((s = strrchr (s,'/')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i) {
+ t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ ((t = default_user_flag (i)) ? t : "");
+ sprintf (s += strlen (s),"%s\015\012",t);
+ }
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mbx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (tmp,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* superior name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT)))
+ return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mbxproto);
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!mbx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ unsigned long oldpid = LOCAL->lastpid;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ /* we are the last flag updater */
+ LOCAL->lastpid = (unsigned long) getpid ();
+ /* update header if needed */
+ if (((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid))
+ mbx_update_header (stream);
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ int snarf = stream->inbox && !stream->rdonly;
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs || snarf) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ /* reparse header if not flagchecking */
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ /* always reparse header at least */
+ if (ret && snarf) { /* snarf new messages if still OK */
+ mbx_snarf (stream);
+ /* parse snarfed messages */
+ ret = mbx_parse (stream);
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail snarf messages from system inbox
+ * Accepts: MAIL stream, already locked
+ */
+
+void mbx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* build header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;00000000%04x-00000000\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) +
+ (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) +
+ (fANSWERED * elt->answered) + (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_setflag (sysibx,tmp,"\\Deleted");
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) >= 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ MM_CRITICAL (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message))
+ ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ /* set atime to now-1 if successful copy */
+ if (ret) tp[0] = time (0) - 1;
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ MM_NOCRITICAL (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mbx_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s;
+}
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ /* get current last flag updater PID */
+ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) &&
+ (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))?
+ strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0;
+ /* set flagcheck if lastpid changed */
+ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T;
+ LOCAL->lastpid = i; /* set as last PID */
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ MM_LOG (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#ifndef CYGKLUDGEOFFSET
+#define CYGKLUDGEOFFSET 0
+#endif
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid);
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET,
+ HDRSIZE - CYGKLUDGEOFFSET) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock mailbox for rewrite",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ MM_CRITICAL (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ MM_NOCRITICAL (stream); /* release critical */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ }
+
+ else { /* can't get exclusive */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ (*bn) (BLOCK_NONE,NIL);
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/unix/mh.c b/imap/src/osdep/unix/mh.c
new file mode 100644
index 00000000..0226b7af
--- /dev/null
+++ b/imap/src/osdep/unix/mh.c
@@ -0,0 +1,1283 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MH mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 February 1992
+ * Last Edited: 11 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */
+#define MHINBOXDIR "inbox"
+#define MHPROFILE ".mh_profile"
+#define MHCOMMA ','
+#define MHSEQUENCE ".mh_sequence"
+#define MHSEQUENCES ".mh_sequences"
+#define MHPATH "Mail"
+
+
+/* mh_load_message() flags */
+
+#define MLM_HEADER 0x1 /* load message text */
+#define MLM_TEXT 0x2 /* load message text */
+
+/* MH I/O stream local data */
+
+typedef struct mh_local {
+ char *dir; /* spool directory name */
+ unsigned char buf[CHUNKSIZE]; /* temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MHLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MHLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mh_valid (char *name);
+int mh_isvalid (char *name,char *tmp,long synonly);
+int mh_namevalid (char *name);
+char *mh_path (char *tmp);
+void *mh_parameters (long function,void *value);
+long mh_dirfmttest (char *name);
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mh_list (MAILSTREAM *stream,char *ref,char *pat);
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
+long mh_subscribe (MAILSTREAM *stream,char *mailbox);
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mh_create (MAILSTREAM *stream,char *mailbox);
+long mh_delete (MAILSTREAM *stream,char *mailbox);
+long mh_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mh_open (MAILSTREAM *stream);
+void mh_close (MAILSTREAM *stream,long options);
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long mh_ping (MAILSTREAM *stream);
+void mh_check (MAILSTREAM *stream);
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+int mh_select (struct direct *name);
+int mh_numsort (const void *d1,const void *d2);
+char *mh_file (char *dst,char *name);
+long mh_canonicalize (char *pattern,char *ref,char *pat);
+void mh_setdate (char *file,MESSAGECACHE *elt);
+
+/* MH mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mhdriver = {
+ "mh", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mh_valid, /* mailbox is valid for us */
+ mh_parameters, /* manipulate parameters */
+ mh_scan, /* scan mailboxes */
+ mh_list, /* find mailboxes */
+ mh_lsub, /* find subscribed mailboxes */
+ mh_subscribe, /* subscribe to mailbox */
+ mh_unsubscribe, /* unsubscribe from mailbox */
+ mh_create, /* create mailbox */
+ mh_delete, /* delete mailbox */
+ mh_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mh_open, /* open mailbox */
+ mh_close, /* close mailbox */
+ mh_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mh_header, /* fetch message header */
+ mh_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mh_ping, /* ping mailbox to see if still alive */
+ mh_check, /* check for new messages */
+ mh_expunge, /* expunge deleted messages */
+ mh_copy, /* copy messages to another mailbox */
+ mh_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mhproto = {&mhdriver};
+
+
+static char *mh_profile = NIL; /* holds MH profile */
+static char *mh_pathname = NIL; /* holds MH path name */
+static long mh_once = 0; /* already snarled once */
+static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */
+
+/* MH mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mh_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;
+}
+
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * syntax only test flag
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_isvalid (char *name,char *tmp,long synonly)
+{
+ struct stat sbuf;
+ char *s,*t,altname[MAILTMPLEN];
+ unsigned long i;
+ int ret = NIL;
+ errno = NIL; /* zap any error condition */
+ /* mh name? */
+ if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) ||
+ !compare_cstring (name,MHINBOX) ||
+ ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){
+ if (mh_path (tmp)) /* validate name if INBOX or not synonly */
+ ret = (synonly && compare_cstring (name,"INBOX")) ?
+ T : ((stat (mh_file (tmp,name),&sbuf) == 0) &&
+ (sbuf.st_mode & S_IFMT) == S_IFDIR);
+ else if (!mh_once++) { /* only report error once */
+ sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile);
+ mm_log (tmp,WARN);
+ }
+ }
+ /* see if non-NS name within mh hierarchy */
+ else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) &&
+ (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) &&
+ (tmp[i] == '/') && tmp[i+1]) {
+ sprintf (altname,"#mh%.900s",tmp+i);
+ /* can't do synonly here! */
+ ret = mh_isvalid (altname,tmp,NIL);
+ }
+ else errno = EINVAL; /* bogus name */
+ return ret;
+}
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_namevalid (char *name)
+{
+ char *s;
+ if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') &&
+ (name[2] == 'h' || name[2] == 'H') && name[3] == '/')
+ for (s = name; s && *s;) { /* make sure no all-digit nodes */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break;/* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* Return MH path
+ * Accepts: temporary buffer
+ * Returns: MH path or NIL if MH disabled
+ */
+
+char *mh_path (char *tmp)
+{
+ char *s,*t,*v,*r;
+ int fd;
+ struct stat sbuf;
+ if (!mh_profile) { /* build mh_profile and mh_pathname now */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE);
+ if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) {
+ fstat (fd,&sbuf); /* yes, get size and read file */
+ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size);
+ close (fd); /* don't need the file any more */
+ t[sbuf.st_size] = '\0'; /* tie it off */
+ /* parse profile file */
+ for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) {
+ /* found space in line? */
+ if (v = strpbrk (s," \t")) {
+ *v++ = '\0'; /* tie off, is keyword "Path:"? */
+ if (!compare_cstring (s,"Path:")) {
+ /* skip whitespace */
+ while ((*v == ' ') || (*v == '\t')) ++v;
+ /* absolute path? */
+ if (*v == '/') s = v;
+ else sprintf (s = tmp,"%s/%s",myhomedir (),v);
+ /* copy name */
+ mh_pathname = cpystr (s);
+ break; /* don't need to look at rest of file */
+ }
+ }
+ }
+ fs_give ((void **) &t); /* flush profile text */
+ if (!mh_pathname) { /* default path if not in the profile */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPATH);
+ mh_pathname = cpystr (tmp);
+ }
+ }
+ }
+ return mh_pathname;
+}
+
+/* MH manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mh_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mh_file ((char *) value,"INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mh_dirfmttest;
+ break;
+ case SET_MHPROFILE:
+ if (mh_profile) fs_give ((void **) &mh_profile);
+ mh_profile = cpystr ((char *) value);
+ case GET_MHPROFILE:
+ ret = (void *) mh_profile;
+ break;
+ case SET_MHPATH:
+ if (mh_pathname) fs_give ((void **) &mh_pathname);
+ mh_pathname = cpystr ((char *) value);
+ case GET_MHPATH:
+ ret = (void *) mh_pathname;
+ break;
+ case SET_MHALLOWINBOX:
+ mh_allow_inbox = value ? T : NIL;
+ case GET_MHALLOWINBOX:
+ ret = (void *) (mh_allow_inbox ? VOIDT : NIL);
+ }
+ return ret;
+}
+
+
+/* MH test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mh_dirfmttest (char *s)
+{
+ int c;
+ /* sequence(s) file is an internal name */
+ if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) {
+ if (*s == MHCOMMA) ++s; /* else comma + all numeric name */
+ /* success if all-numeric */
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ }
+ return LONGT;
+}
+
+/* MH scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (mh_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ mm_list (stream,'/',test,LATT_NOSELECT);
+ }
+ }
+ /* get canonical form of name */
+ else if (mh_canonicalize (test,ref,pat)) {
+ if (contents) { /* maybe I'll implement this someday */
+ mm_log ("Scan not valid for mh mailboxes",ERROR);
+ return;
+ }
+ if (test[3] == '/') { /* looking down levels? */
+ /* yes, found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test+4,i = s - (test+4));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test+4);/* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'/')) {
+ *s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* do the work */
+ mh_list_work (stream,s,test,0);
+ }
+ /* always an INBOX */
+ if (!compare_cstring (test,MHINBOX))
+ mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS);
+ }
+}
+
+/* MH list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ mh_scan (stream,ref,pat,NIL);
+}
+
+
+/* MH list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,test[MAILTMPLEN];
+ /* get canonical form of name */
+ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) {
+ do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+ }
+}
+
+/* MH list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * search level
+ */
+
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
+{
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
+ /* build MH name to search */
+ if (dir) sprintf (name,"#mh/%s/",dir);
+ else strcpy (name,"#mh/");
+ /* make directory name, punt if bogus */
+ if (!mh_file (curdir,name)) return;
+ cp = curdir + strlen (curdir);/* end of directory name */
+ np = name + strlen (name); /* end of MH name */
+ if (dp = opendir (curdir)) { /* open directory */
+ while (d = readdir (dp)) /* scan, ignore . and numeric names */
+ if ((d->d_name[0] != '.') && !mh_select (d)) {
+ strcpy (cp,d->d_name); /* make directory name */
+ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ strcpy (np,d->d_name);/* make mh name of directory name */
+ /* yes, an MH name if full match */
+ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL);
+ /* check if should recurse */
+ if (dmatch (name,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ mh_list_work (stream,name+4,pat,level+1);
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* MH mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MH mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MH mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!mh_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox);
+ /* must not already exist */
+ else if (mh_isvalid (mailbox,tmp,NIL))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ else if (!mh_path (tmp)) return NIL;
+ /* try to make it */
+ else if (!(mh_file (tmp,mailbox) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else return LONGT; /* success */
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* MH mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int i;
+ char tmp[MAILTMPLEN];
+ /* is mailbox valid? */
+ if (!mh_isvalid (mailbox,tmp,NIL)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get name of directory */
+ i = strlen (mh_file (tmp,mailbox));
+ if (dirp = opendir (tmp)) { /* open directory */
+ tmp[i++] = '/'; /* now apply trailing delimiter */
+ /* massacre all mh owned files */
+ while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) {
+ strcpy (tmp + i,d->d_name);
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ }
+ /* try to remove the directory */
+ if (rmdir (mh_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
+ mm_log (tmp,WARN);
+ }
+ return T; /* return success */
+}
+
+/* MH mail rename mailbox
+ * Accepts: MH mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ /* old mailbox name must be valid */
+ if (!mh_isvalid (old,tmp,NIL))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mh_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mh_isvalid (newname,tmp,NIL))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ /* success if can rename the directory */
+ else { /* found superior to destination name? */
+ if (s = strrchr (mh_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (mh_file (tmp,old),tmp1)) return T;
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ mm_log (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MH mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mh_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mh recycle stream");
+ stream->local = fs_get (sizeof (MHLOCAL));
+ /* INBOXness is one of the following:
+ * #mhinbox (case-independent)
+ * #mh/inbox (mh is case-independent, inbox is case-dependent)
+ * INBOX (case-independent
+ */
+ stream->inbox = /* note if an INBOX or not */
+ (!compare_cstring (stream->mailbox,MHINBOX) ||
+ ((stream->mailbox[0] == '#') &&
+ ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) &&
+ ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) &&
+ (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) ||
+ !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL;
+ mh_file (tmp,stream->mailbox);/* get directory name */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mh_ping (stream)) return NIL;
+ if (!(stream->nmsgs || stream->silent))
+ mm_log ("Mailbox is empty",(long) NIL);
+ return stream; /* return stream to caller */
+}
+
+/* MH mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mh_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL);
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MH mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL);
+}
+
+/* MH load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ --j;
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+ /* need to load cache with message data? */
+ if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+
+ if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i < elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.header.text.data) !=
+ elt->private.msg.header.text.size) fatal ("mh hdr size mismatch");
+ }
+ if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.special.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i < elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.text.text.data) !=
+ elt->private.msg.text.text.size) fatal ("mh txt size mismatch");
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* MH mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ mh_load_message (stream,msgno,MLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* MH mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mh_load_message (stream,msgno,MLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MH mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mh_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r;
+ unsigned long old = stream->uid_last;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */
+ if (stream->inbox && /* no, create if INBOX */
+ dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"),
+ get_dir_protection ("INBOX"))) return T;
+ sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ else { /* see if already read */
+ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name);
+ if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime))
+ elt->seen = T;
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+
+ /* if INBOX, snarf from system INBOX */
+ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ mm_critical (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mh_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MH mailbox failed: %.80s",
+ s,strerror (errno));
+ mm_log (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ mm_nocritical (stream); /* release critical */
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MH mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mh_check (MAILSTREAM *stream)
+{
+ /* Perhaps in the future this will preserve flags */
+ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MH mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ mm_critical (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ mm_log (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MH mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char flags[MAILTMPLEN],date[MAILTMPLEN];
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ long ret = NIL;
+ /* copy the messages */
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
+ fstat (fd,&sbuf); /* get size of message */
+ if (!elt->day) { /* set internaldate to file date if needed */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ /* kludge; mh_append would just strip CRs */
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->deleted) strcat (flags," \\Deleted");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '('; /* open list */
+ strcat (flags,")"); /* close list */
+ mail_date (date,elt); /* generate internal date */
+ if (au) mail_parameters (NIL,SET_APPENDUID,NIL);
+ if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) &&
+ (options & CP_MOVE)) elt->deleted = T;
+ if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ close (fd);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret; /* return success */
+}
+
+/* MH mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct direct **names = NIL;
+ int fd;
+ char c,*flags,*date,*s,tmp[MAILTMPLEN];
+ STRING *message;
+ MESSAGECACHE elt;
+ FILE *df;
+ long i,size,last,nfiles;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mhproto;
+ /* make sure valid mailbox */
+ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!((!compare_cstring (mailbox,MHINBOX) ||
+ !compare_cstring (mailbox,"INBOX")) &&
+ (mh_file (tmp,MHINBOX) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) {
+ /* largest number */
+ last = atoi (names[nfiles-1]->d_name);
+ for (i = 0; i < nfiles; ++i) /* free directory */
+ fs_give ((void **) &names[i]);
+ }
+ else last = 0; /* no messages here yet */
+ if (s = (void *) names) fs_give ((void **) &s);
+
+ mm_critical (stream); /* go critical */
+ do {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ if (date) { /* want to preserve date? */
+ /* yes, parse date into an elt */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ }
+ mh_file (tmp,mailbox); /* build file name we will use */
+ sprintf (tmp + strlen (tmp),"/%ld",++last);
+ if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ /* copy the data w/o CR's */
+ for (size = 0,i = SIZE (message); i && ret; --i)
+ if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL;
+ /* close the file */
+ if (!ret || fclose (df)) {
+ unlink (tmp); /* delete message */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ if (ret) { /* set the date for this message */
+ if (date) mh_setdate (tmp,&elt);
+ /* get next message */
+ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MH file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mh_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MH file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mh_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MH mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mh_file (char *dst,char *name)
+{
+ char *s;
+ char *path = mh_path (dst);
+ if (!path) fatal ("No mh path in mh_file()!");
+ /* INBOX becomes "inbox" in the MH path */
+ if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX"))
+ sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR);
+ /* #mh names skip past prefix */
+ else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4);
+ else mailboxfile (dst,name); /* all other names */
+ /* tie off unnecessary trailing / */
+ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0';
+ return dst;
+}
+
+/* MH canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long mh_canonicalize (char *pattern,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with / */
+ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if (mh_isvalid (pattern,tmp,T)) {
+ /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ mm_log ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mh_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/unix/mix.c b/imap/src/osdep/unix/mix.c
new file mode 100644
index 00000000..fbf4a023
--- /dev/null
+++ b/imap/src/osdep/unix/mix.c
@@ -0,0 +1,2834 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIX mail routines
+ *
+ * Author(s): Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 March 2006
+ * Last Edited: 7 May 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MIX definitions */
+
+#define MEGABYTE (1024*1024)
+
+#define MIXDATAROLL MEGABYTE /* size at which we roll to a new file */
+
+
+/* MIX files */
+
+#define MIXNAME ".mix" /* prefix for all MIX file names */
+#define MIXMETA "meta" /* suffix for metadata */
+#define MIXINDEX "index" /* suffix for index */
+#define MIXSTATUS "status" /* suffix for status */
+#define MIXSORTCACHE "sortcache"/* suffix for sortcache */
+#define METAMAX (MEGABYTE-1) /* maximum metadata file size (sanity check) */
+
+
+/* MIX file formats */
+
+ /* sequence format (all but msg files) */
+#define SEQFMT "S%08lx\015\012"
+ /* metadata file format */
+#define MTAFMT "V%08lx\015\012L%08lx\015\012N%08lx\015\012"
+ /* index file record format */
+#define IXRFMT ":%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:%08lx:%08lx:%08lx:%08lx:\015\012"
+ /* status file record format */
+#define STRFMT ":%08lx:%08lx:%04x:%08lx:\015\012"
+ /* message file header format */
+#define MSRFMT "%s%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:\015\012"
+#define MSGTOK ":msg:"
+#define MSGTSZ (sizeof(MSGTOK)-1)
+ /* sortcache file record format */
+#define SCRFMT ":%08lx:%08lx:%08lx:%08lx:%08lx:%c%08lx:%08lx:%08lx:\015\012"
+
+/* MIX I/O stream local data */
+
+typedef struct mix_local {
+ unsigned long curmsg; /* current message file number */
+ unsigned long newmsg; /* current new message file number */
+ time_t lastsnarf; /* last snarf time */
+ int msgfd; /* file description of current msg file */
+ int mfd; /* file descriptor of open metadata */
+ unsigned long metaseq; /* metadata sequence */
+ char *index; /* mailbox index name */
+ unsigned long indexseq; /* index sequence */
+ char *status; /* mailbox status name */
+ unsigned long statusseq; /* status sequence */
+ char *sortcache; /* mailbox sortcache name */
+ unsigned long sortcacheseq; /* sortcache sequence */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned int expok : 1; /* non-zero if expunge reports OK */
+ unsigned int internal : 1; /* internally opened, do not validate */
+} MIXLOCAL;
+
+
+#define MIXBURP struct mix_burp
+
+MIXBURP {
+ unsigned long fileno; /* message file number */
+ char *name; /* message file name */
+ SEARCHSET *tail; /* tail of ranges */
+ SEARCHSET set; /* set of retained ranges */
+ MIXBURP *next; /* next file to burp */
+};
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MIXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mix_valid (char *name);
+long mix_isvalid (char *name,char *meta);
+void *mix_parameters (long function,void *value);
+long mix_dirfmttest (char *name);
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mix_list (MAILSTREAM *stream,char *ref,char *pat);
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mix_subscribe (MAILSTREAM *stream,char *mailbox);
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mix_create (MAILSTREAM *stream,char *mailbox);
+long mix_delete (MAILSTREAM *stream,char *mailbox);
+long mix_rename (MAILSTREAM *stream,char *old,char *newname);
+int mix_rselect (struct direct *name);
+MAILSTREAM *mix_open (MAILSTREAM *stream);
+void mix_close (MAILSTREAM *stream,long options);
+void mix_abort (MAILSTREAM *stream);
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long mix_ping (MAILSTREAM *stream);
+void mix_check (MAILSTREAM *stream);
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options);
+int mix_select (struct direct *name);
+int mix_msgfsort (const void *d1,const void *d2);
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size);
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed);
+long mix_burp_check (SEARCHSET *set,size_t size,char *file);
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq);
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags);
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq);
+long mix_meta_update (MAILSTREAM *stream);
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag);
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag);
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize);
+FILE *mix_sortcache_open (MAILSTREAM *stream);
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache);
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type);
+unsigned long mix_read_sequence (FILE *f);
+char *mix_dir (char *dst,char *name);
+char *mix_file (char *dst,char *dir,char *name);
+char *mix_file_data (char *dst,char *dir,unsigned long data);
+unsigned long mix_modseq (unsigned long oldseq);
+
+/* MIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mixdriver = {
+ "mix", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT|DR_MODSEQ,
+ (DRIVER *) NIL, /* next driver */
+ mix_valid, /* mailbox is valid for us */
+ mix_parameters, /* manipulate parameters */
+ mix_scan, /* scan mailboxes */
+ mix_list, /* find mailboxes */
+ mix_lsub, /* find subscribed mailboxes */
+ mix_subscribe, /* subscribe to mailbox */
+ mix_unsubscribe, /* unsubscribe from mailbox */
+ mix_create, /* create mailbox */
+ mix_delete, /* delete mailbox */
+ mix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mix_open, /* open mailbox */
+ mix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mix_header, /* fetch message header only */
+ mix_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mix_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ mix_sort, /* sort messages */
+ mix_thread, /* thread messages */
+ mix_ping, /* ping mailbox to see if still alive */
+ mix_check, /* check for new messages */
+ mix_expunge, /* expunge deleted messages */
+ mix_copy, /* copy messages to another mailbox */
+ mix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mixproto = {&mixdriver};
+
+/* MIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mix_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mix_isvalid (name,tmp) ? &mixdriver : NIL;
+}
+
+
+/* MIX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return meta name
+ * Returns: T if valid, NIL otherwise, metadata name written in both cases
+ */
+
+long mix_isvalid (char *name,char *meta)
+{
+ char dir[MAILTMPLEN];
+ struct stat sbuf;
+ /* validate name as directory */
+ if (!(errno = ((strlen (name) > NETMAXMBX) ? ENAMETOOLONG : NIL)) &&
+ *mix_dir (dir,name) && mix_file (meta,dir,MIXMETA) &&
+ !stat (dir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mix? */
+ if (!stat (meta,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
+ return LONGT;
+ else errno = NIL; /* directory but not mix */
+ }
+ return NIL;
+}
+
+/* MIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mix_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mix_scan_contents;
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MIX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mix_dirfmttest (char *name)
+{
+ /* belongs to MIX if starts with .mix */
+ return strncmp (name,MIXNAME,sizeof (MIXNAME) - 1) ? NIL : LONGT;
+}
+
+/* MIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MIX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mix_select,mix_msgfsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MIX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MIX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MIX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MIX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MIX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ FILE *f;
+ int c,i;
+ char *t,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = strrchr (mailbox,'/');
+ unsigned long now = time (NIL);
+ long ret = NIL;
+ /* always create \NoSelect if trailing / */
+ if (s && !s[1]) return dummy_create (stream,mailbox);
+ /* validate name */
+ if (mix_dirfmttest (s ? s + 1 : mailbox))
+ sprintf(tmp,"Can't create mailbox %.80s: invalid MIX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory and metadata */
+ else if (!dummy_create_path (stream,
+ mix_file (file,mix_dir (tmp,mailbox),MIXMETA),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %.80s",mailbox,strerror (errno));
+ else if (!(f = fopen (file,"w")))
+ sprintf (tmp,"Can't re-open metadata %.80s: %.80s",mailbox,
+ strerror (errno));
+ else { /* success, write initial metadata */
+ fprintf (f,SEQFMT,now);
+ fprintf (f,MTAFMT,now,0,now);
+ for (i = 0, c = 'K'; (i < NUSERFLAGS) &&
+ (t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ default_user_flag (i)) && *t; ++i) {
+ putc (c,f); /* write another keyword */
+ fputs (t,f);
+ c = ' '; /* delimiter is now space */
+ }
+ fclose (f);
+ set_mbx_protections (mailbox,file);
+ /* point to suffix */
+ s = file + strlen (file) - (sizeof (MIXMETA) - 1);
+ strcpy (s,MIXINDEX); /* create index */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox index: %.80s",strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ strcpy (s,MIXSTATUS); /* create status */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox status: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ sprintf (s,"%08lx",now);/* message file */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox data: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ ret = LONGT; /* declare success at this point */
+ }
+ }
+ }
+ }
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MIX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int fd = -1;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for delete: %.80s",mailbox);
+ /* delete metadata */
+ else if (unlink (tmp)) sprintf (tmp,"Can't delete mailbox %.80s index: %80s",
+ mailbox,strerror (errno));
+ else {
+ close (fd); /* close descriptor on deleted metadata */
+ /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mix_dirfmttest (d->d_name)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %.80s",
+ mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MIX mail rename mailbox
+ * Accepts: MIX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ if (!mix_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for rename: %.80s",old);
+ else if (mix_dirfmttest ((s = strrchr (newname,'/')) ? s + 1 : newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MIX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mix_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mix_dir (tmp,old); /* build old directory name */
+ mix_dir (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (tmp1,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) {
+ close (fd); /* close descriptor on metadata */
+ return LONGT;
+ }
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ char *src,*dst;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mix file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mix_rselect,alphasort);
+ i < n; ++i) {
+ size_t len = strlen (names[i]->d_name);
+ sprintf (src = (char *) fs_get (srcl + len + 2),"%s/%s",
+ tmp,names[i]->d_name);
+ sprintf (dst = (char *) fs_get (dstl + len + 1),"%s%s",
+ tmp1,names[i]->d_name);
+ if (rename (src,dst)) lasterror = errno;
+ fs_give ((void **) &src);
+ fs_give ((void **) &dst);
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror) errno = lasterror;
+ else {
+ close (fd); /* close descriptor on metadata */
+ return mix_create (NIL,"INBOX");
+ }
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",
+ old,newname,strerror (errno));
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MIX test for mix name
+ * Accepts: candidate directory name
+ * Returns: T if mix file name, NIL otherwise
+ */
+
+int mix_rselect (struct direct *name)
+{
+ return mix_dirfmttest (name->d_name);
+}
+
+/* MIX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mix_open (MAILSTREAM *stream)
+{
+ short silent;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mixproto);
+ if (stream->local) fatal ("mix recycle stream");
+ stream->local = memset (fs_get (sizeof (MIXLOCAL)),0,sizeof (MIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* set stream->mailbox to be directory name */
+ mix_dir (LOCAL->buf,stream->mailbox);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (LOCAL->buf);
+ LOCAL->msgfd = -1; /* currently no file open */
+ if (!(((!stream->rdonly && /* open metadata file */
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDWR,NIL)) >= 0)) ||
+ ((stream->rdonly = T) &&
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDONLY,NIL)) >= 0))) &&
+ !flock (LOCAL->mfd,LOCK_SH))) {
+ MM_LOG ("Error opening mix metadata file",ERROR);
+ mix_abort (stream);
+ stream = NIL; /* open fails */
+ }
+ else { /* metadata open, complete open */
+ LOCAL->index = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXINDEX));
+ LOCAL->status = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXSTATUS));
+ LOCAL->sortcache = cpystr (mix_file (LOCAL->buf,stream->mailbox,
+ MIXSORTCACHE));
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (silent = stream->silent) LOCAL->internal = T;
+ stream->silent = T;
+ if (mix_ping (stream)) { /* do initial ping */
+ /* try burping in case we are exclusive */
+ if (!stream->rdonly) mix_expunge (stream,"",NIL);
+ if (!(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = /* can we create new user flags? */
+ (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T;
+ }
+ else { /* got murdelyzed in ping */
+ mix_abort (stream);
+ stream = NIL;
+ }
+ }
+ return stream; /* return stream to caller */
+}
+
+/* MIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mix_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* burp-only or expunge */
+ mix_expunge (stream,(options & CL_EXPUNGE) ? NIL : "",NIL);
+ mix_abort (stream);
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ /* close current message file if open */
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ /* close current metadata file if open */
+ if (LOCAL->mfd >= 0) close (LOCAL->mfd);
+ if (LOCAL->index) fs_give ((void **) &LOCAL->index);
+ if (LOCAL->status) fs_give ((void **) &LOCAL->status);
+ if (LOCAL->sortcache) fs_give ((void **) &LOCAL->sortcache);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i,j,k;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (length) *length = 0; /* default return */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return "";
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ lseek (LOCAL->msgfd,elt->private.special.offset,L_SET);
+ /* size of special data and header */
+ j = elt->private.msg.header.offset + elt->private.msg.header.text.size;
+ if (j > LOCAL->buflen) { /* is buffer big enough? */
+ /* no, make one that is */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
+ }
+ /* Maybe someday validate internaldate too */
+ /* slurp special data + header, validate */
+ if ((read (LOCAL->msgfd,LOCAL->buf,j) == j) &&
+ !strncmp (LOCAL->buf,MSGTOK,MSGTSZ) &&
+ (elt->private.uid == strtoul ((char *) LOCAL->buf + MSGTSZ,&s,16)) &&
+ (*s++ == ':') && (s = strchr (s,':')) &&
+ (k = strtoul (s+1,&s,16)) && (*s++ == ':') &&
+ (s < (char *) (LOCAL->buf + elt->private.msg.header.offset))) {
+ /* won, set offset and size of message */
+ i = elt->private.msg.header.offset;
+ *length = elt->private.msg.header.text.size;
+ if (k != elt->rfc822_size) {
+ sprintf (tmp,"Inconsistency in mix message size, uid=%lx (%lu != %lu)",
+ elt->private.uid,elt->rfc822_size,k);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* document the problem */
+ LOCAL->buf[100] = '\0'; /* tie off buffer at no more than 100 octets */
+ /* or at newline, whichever is first */
+ if (s = strpbrk (LOCAL->buf,"\015\012")) *s = '\0';
+ sprintf (tmp,"Error reading mix message header, uid=%lx, s=%.0lx, h=%s",
+ elt->private.uid,elt->rfc822_size,LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ *length = i = j = 0; /* default to empty */
+ }
+ LOCAL->buf[j] = '\0'; /* tie off buffer at the end */
+ return (char *) LOCAL->buf + i;
+}
+
+/* MIX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return NIL;
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ /* doing non-peek fetch? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ FILE *idxf; /* yes, process metadata/index/status */
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ elt->seen = T; /* mark as seen */
+ MM_FLAGS (stream,elt->msgno);
+ /* update status file if possible */
+ if (statf && !stream->rdonly) {
+ elt->private.mod = LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,NIL);
+ }
+ if (idxf) fclose (idxf); /* release index and status file */
+ if (statf) fclose (statf);
+ }
+ d.fd = LOCAL->msgfd; /* set up file descriptor */
+ /* offset of message text */
+ d.pos = elt->private.special.offset + elt->private.msg.header.offset +
+ elt->private.msg.header.text.size;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* chunk size */
+ INIT (bs,fd_string,&d,elt->rfc822_size - elt->private.msg.header.text.size);
+ return T;
+}
+
+/* MIX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf,ffkey;
+ long f;
+ short nf;
+ FILE *idxf;
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ unsigned long seq = mix_modseq (LOCAL->statusseq);
+ /* find first free key */
+ for (ffkey = 0; (ffkey < NUSERFLAGS) && stream->user_flags[ffkey]; ++ffkey);
+ /* parse sequence and flags */
+ if (((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf)) {
+ /* alter flags */
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) {
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ }
+ /* update status file after change */
+ if (statf && (seq == LOCAL->statusseq))
+ mix_status_update (stream,statf,NIL);
+ /* update metadata if created a keyword */
+ if ((ffkey < NUSERFLAGS) && stream->user_flags[ffkey] &&
+ !mix_meta_update (stream))
+ MM_LOG ("Error updating mix metadata after keyword creation",ERROR);
+ }
+ if (statf) fclose (statf); /* release status file if still open */
+ if (idxf) fclose (idxf); /* release index file */
+}
+
+/* MIX mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+
+/* MIX mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+/* MIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+static int snarfing = 0; /* lock against recursive snarfing */
+
+long mix_ping (MAILSTREAM *stream)
+{
+ FILE *idxf,*statf;
+ struct stat sbuf;
+ STRING msg;
+ MESSAGECACHE *elt;
+ int mfd,ifd,sfd;
+ unsigned long i,msglen;
+ char *message,date[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *sysibx = NIL;
+ long ret = NIL;
+ long snarfok = LONGT;
+ /* time to snarf? */
+ if (stream->inbox && !stream->rdonly && !snarfing &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (time_t) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ MM_CRITICAL (stream); /* go critical */
+ snarfing = T; /* don't recursively snarf */
+ /* disable APPENDUID/COPYUID callbacks */
+ mail_parameters (NIL,SET_APPENDUID,NIL);
+ mail_parameters (NIL,SET_COPYUID,NIL);
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && sysibx->nmsgs) {
+ /* for each message in sysibx mailbox */
+ for (i = 1; snarfok && (i <= sysibx->nmsgs); ++i)
+ if (!(elt = mail_elt (sysibx,i))->deleted &&
+ (message = mail_fetch_message (sysibx,i,&msglen,FT_PEEK)) &&
+ msglen) {
+ mail_date (date,elt); /* make internal date string */
+ /* make flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '(';
+ strcat (flags,")");
+ INIT (&msg,mail_string,message,msglen);
+ if (snarfok = mail_append_full (stream,"INBOX",flags,date,&msg)) {
+ char sequence[15];
+ sprintf (sequence,"%lu",i);
+ mail_flag (sysibx,sequence,"\\Deleted",ST_SET);
+ }
+ }
+
+ /* now expunge all those messages */
+ if (snarfok) mail_expunge (sysibx);
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail at message: %lu",i - 1);
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ if (sysibx) mail_close (sysibx);
+ /* reenable APPENDUID/COPYUID */
+ mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ mail_parameters (NIL,SET_COPYUID,(void *) cu);
+ snarfing = NIL; /* no longer snarfing */
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ /* expunging OK if global flag set */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* process metadata/index/status */
+ if (statf = mix_parse (stream,&idxf,LONGT,
+ (LOCAL->internal ? NIL : LONGT))) {
+ fclose (statf); /* just close the status file */
+ ret = LONGT; /* declare success */
+ }
+ if (idxf) fclose (idxf); /* release index file */
+ LOCAL->expok = NIL; /* expunge no longer OK */
+ if (!ret) mix_abort (stream); /* murdelyze stream if ping fails */
+ return ret;
+}
+
+
+/* MIX mail checkpoint mailbox (burp only)
+ * Accepts: MAIL stream
+ */
+
+void mix_check (MAILSTREAM *stream)
+{
+ if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Checkpoint ignored on readonly mailbox",NIL);
+ /* do burp-only expunge action */
+ if (mix_expunge (stream,"",NIL)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL, empty string for burp only
+ * expunge options
+ * Returns: T on success, NIL if failure
+ */
+
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ FILE *idxf = NIL;
+ FILE *statf = NIL;
+ MESSAGECACHE *elt;
+ int ifd,sfd;
+ long ret;
+ unsigned long i;
+ unsigned long nexp = 0;
+ unsigned long reclaimed = 0;
+ int burponly = (sequence && !*sequence);
+ LOCAL->expok = T; /* expunge during ping is OK */
+ if (!(ret = burponly || !sequence ||
+ ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) || stream->rdonly);
+ /* read index and open status exclusive */
+ else if (statf = mix_parse (stream,&idxf,LONGT,
+ LOCAL->internal ? NIL : LONGT)) {
+ /* expunge unless just burping */
+ if (!burponly) for (i = 1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* need to expunge this message? */
+ if (sequence ? elt->sequence : elt->deleted) {
+ ++nexp; /* yes, make it so */
+ mail_expunged (stream,i);
+ }
+ else ++i; /* otherwise advance to next message */
+ }
+
+ /* burp if can get exclusive access */
+ if (!flock (LOCAL->mfd,LOCK_EX|LOCK_NB)) {
+ void *a;
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mix_select,mix_msgfsort);
+ if (nfiles > 0) { /* if have message files */
+ MIXBURP *burp,*cur;
+ /* initialize burp list */
+ for (i = 0, burp = cur = NIL; i < nfiles; ++i) {
+ MIXBURP *nxt = (MIXBURP *) memset (fs_get (sizeof (MIXBURP)),0,
+ sizeof (MIXBURP));
+ /* another file found */
+ if (cur) cur = cur->next = nxt;
+ else cur = burp = nxt;
+ cur->name = names[i]->d_name;
+ cur->fileno = strtoul (cur->name + sizeof (MIXNAME) - 1,NIL,16);
+ cur->tail = &cur->set;
+ fs_give ((void **) &names[i]);
+ }
+ /* now load ranges */
+ for (i = 1, cur = burp; ret && (i <= stream->nmsgs); i++) {
+ /* is this message in current set? */
+ elt = mail_elt (stream,i);
+ if (cur && (elt->private.spare.data != cur->fileno)) {
+ /* restart if necessary */
+ if (elt->private.spare.data < cur->fileno) cur = burp;
+ /* hunt for appropriate mailbox */
+ while (cur && (elt->private.spare.data > cur->fileno))
+ cur = cur->next;
+ /* ought to have found it now... */
+ if (cur && (elt->private.spare.data != cur->fileno)) cur = NIL;
+ }
+ /* if found, add to set */
+ if (cur) ret = mix_addset (&cur->tail,elt->private.special.offset,
+ elt->private.msg.header.offset +
+ elt->rfc822_size);
+ else { /* uh-oh */
+ sprintf (LOCAL->buf,"Can't locate mix message file %.08lx",
+ elt->private.spare.data);
+ MM_LOG (LOCAL->buf,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret) /* if no errors, burp all files */
+ for (cur = burp; ret && cur; cur = cur->next) {
+ /* if non-empty, burp it */
+ if (cur->set.last) ret = mix_burp (stream,cur,&reclaimed);
+ /* empty, delete it unless new msg file */
+ else if (mix_file_data (LOCAL->buf,stream->mailbox,cur->fileno) &&
+ ((cur->fileno == LOCAL->newmsg) ?
+ truncate (LOCAL->buf,0) : unlink (LOCAL->buf))) {
+ sprintf (LOCAL->buf,
+ "Can't delete empty message file %.80s: %.80s",
+ cur->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ }
+ else MM_LOG ("No mix message files found during expunge",WARN);
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ }
+
+ /* either way, re-acquire shared lock */
+ if (flock (LOCAL->mfd,LOCK_SH|LOCK_NB))
+ fatal ("Unable to re-acquire metadata shared lock!");
+ /* Do this step even if ret is NIL (meaning some burp problem)! */
+ if (nexp || reclaimed) { /* rewrite index and status if changed */
+ LOCAL->indexseq = mix_modseq (LOCAL->indexseq);
+ if (mix_index_update (stream,idxf,NIL)) {
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ /* set failure if update fails */
+ ret = mix_status_update (stream,statf,NIL);
+ }
+ }
+ }
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ LOCAL->expok = NIL; /* cancel expok */
+ if (ret) { /* only if success */
+ char *s = NIL;
+ if (nexp) sprintf (s = LOCAL->buf,"Expunged %lu messages",nexp);
+ else if (reclaimed)
+ sprintf (s=LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ else if (!burponly)
+ s = stream->rdonly ? "Expunge ignored on readonly mailbox" :
+ "No messages deleted, so no update needed";
+ if (s) MM_LOG (s,(long) NIL);
+ }
+ return ret;
+}
+
+/* MIX test for message file name
+ * Accepts: candidate directory name
+ * Returns: T if message file name, NIL otherwise
+ *
+ * ".mix" with no suffix was used by experimental versions
+ */
+
+int mix_select (struct direct *name)
+{
+ char c,*s;
+ /* make sure name has prefix */
+ if (mix_dirfmttest (name->d_name)) {
+ for (c = *(s = name->d_name + sizeof (MIXNAME) - 1); c && isxdigit (c);
+ c = *s++);
+ if (!c) return T; /* all-hex or no suffix */
+ }
+ return NIL; /* not suffix or non-hex */
+}
+
+
+/* MIX msg file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: -1 if d1 < d2, 0 if d1 == d2, 1 d1 > d2
+ */
+
+int mix_msgfsort (const void *d1,const void *d2)
+{
+ char *n1 = (*(struct direct **) d1)->d_name + sizeof (MIXNAME) - 1;
+ char *n2 = (*(struct direct **) d2)->d_name + sizeof (MIXNAME) - 1;
+ return compare_ulong (*n1 ? strtoul (n1,NIL,16) : 0,
+ *n2 ? strtoul (n2,NIL,16) : 0);
+}
+
+
+/* MIX add a range to a set
+ * Accepts: pointer to set to add
+ * start of set
+ * size of set
+ * Returns: T if success, set updated, NIL otherwise
+ */
+
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size)
+{
+ SEARCHSET *s = *set;
+ if (start < s->last) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Backwards-running mix index %lu < %lu",start,s->last);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* range initially empty? */
+ if (!s->last) s->first = start;
+ else if (start > s->last) /* no, start new range if can't append */
+ (*set = s = s->next = mail_newsearchset ())->first = start;
+ s->last = start + size; /* end of current range */
+ return LONGT;
+}
+
+/* MIX burp message file
+ * Accepts: MAIL stream
+ * current burp block for this message
+ * Returns: T if successful, NIL if failed
+ */
+
+static char *staterr = "Error in stat of mix message file %.80s: %.80s";
+static char *truncerr = "Error truncating mix message file %.80s: %.80s";
+
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed)
+{
+ MESSAGECACHE *elt;
+ SEARCHSET *set;
+ struct stat sbuf;
+ off_t rpos,wpos;
+ size_t size,wsize,wpending,written;
+ int fd;
+ FILE *f;
+ void *s;
+ unsigned long i;
+ long ret = NIL;
+ /* build file name */
+ mix_file_data (LOCAL->buf,stream->mailbox,burp->fileno);
+ /* need to burp at start or multiple ranges? */
+ if (!burp->set.first && !burp->set.next) {
+ /* easy case, single range at start of file */
+ if (stat (LOCAL->buf,&sbuf)) {
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* is this range sane? */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* if matches range then no burp needed! */
+ if (burp->set.last == sbuf.st_size) ret = LONGT;
+ /* just need to remove cruft at end */
+ else if (ret = !truncate (LOCAL->buf,burp->set.last))
+ *reclaimed += sbuf.st_size - burp->set.last;
+ else {
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ }
+ }
+ /* have to do more work, get the file */
+ else if (((fd = open (LOCAL->buf,O_RDWR,NIL)) < 0) ||
+ !(f = fdopen (fd,"r+b"))) {
+ sprintf (LOCAL->buf,"Error opening mix message file %.80s: %.80s",
+ burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ if (fd >= 0) close (fd); /* in case fdopen() failure */
+ }
+ else if (fstat (fd,&sbuf)) { /* get file size */
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ }
+
+ /* only if sane */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* make sure each range starts with token */
+ for (set = &burp->set; set; set = set->next)
+ if (fseek (f,set->first,SEEK_SET) ||
+ (fread (LOCAL->buf,1,MSGTSZ,f) != MSGTSZ) ||
+ strncmp (LOCAL->buf,MSGTOK,MSGTSZ)) {
+ sprintf (LOCAL->buf,"Bad message token in mix message file at %lu",
+ set->first);
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ return NIL; /* burp fails for this file */
+ }
+ /* burp out each old message */
+ for (set = &burp->set, wpos = 0; set; set = set->next) {
+ /* move down this range */
+ for (rpos = set->first, size = set->last - set->first;
+ size; size -= wsize) {
+ if (rpos != wpos) { /* data to skip at start? */
+ /* no, slide this buffer down */
+ wsize = min (size,LOCAL->buflen);
+ /* failure is not an option here */
+ while (fseek (f,rpos,SEEK_SET) ||
+ (fread (LOCAL->buf,1,wsize,f) != wsize)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* nor here */
+ while (fseek (f,wpos,SEEK_SET)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* and especially not here */
+ for (s = LOCAL->buf, wpending = wsize; wpending; wpending -= written)
+ if (!(written = fwrite (LOCAL->buf,1,wpending,f))) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+ else wsize = size; /* nothing to skip, say we wrote it all */
+ rpos += wsize; wpos += wsize;
+ }
+ }
+
+ while (fflush (f)) { /* failure also not an option here... */
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ if (ftruncate (fd,wpos)) { /* flush cruft at end of file */
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ else *reclaimed += rpos - wpos;
+ ret = !fclose (f); /* close file */
+ /* slide down message positions in index */
+ for (i = 1,rpos = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->private.spare.data == burp->fileno) {
+ elt->private.special.offset = rpos;
+ rpos += elt->private.msg.header.offset + elt->rfc822_size;
+ }
+ /* debugging */
+ if (rpos != wpos) fatal ("burp size consistency check!");
+ }
+ return ret;
+}
+
+
+/* MIX burp sanity check to make sure not burping off end of file
+ * Accepts: burp set
+ * file size
+ * file name
+ * Returns: T if sane, NIL if insane
+ */
+
+long mix_burp_check (SEARCHSET *set,size_t size,char *file)
+{
+ do if (set->last > size) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unexpected short mix message file %.80s %lu < %lu",
+ file,size,set->last);
+ MM_LOG (tmp,ERROR);
+ return NIL; /* don't burp this file at all */
+ } while (set = set->next);
+ return LONGT;
+}
+
+/* MIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ char tmp[2*MAILTMPLEN];
+ long ret = mix_isvalid (mailbox,LOCAL->buf);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ MAILSTREAM *astream = NIL;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (!ret) switch (errno) { /* make sure valid mailbox */
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ break;
+ }
+ /* get sequence to copy */
+ else if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append */
+ else if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ unsigned long newsize,hdrsize,size;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+
+ MM_CRITICAL (stream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* calculate size that will be added */
+ for (i = 1, newsize = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence)
+ newsize += hdrsize + elt->rfc822_size;
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,newsize)) {
+ char *t;
+ unsigned long j,uid,uidv;
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); ++i)
+ if (((elt = mail_elt (stream,i))->sequence) && elt->rfc822_size) {
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) ||
+ (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,
+ stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) >= 0)
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ if (LOCAL->msgfd < 0) ret = NIL;
+ else { /* got file */
+ d.fd = LOCAL->msgfd;/* set up file descriptor */
+ /* start of message */
+ d.pos = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,elt->rfc822_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if ((t = stream->user_flags[find_rightmost_bit (&j)]) && *t)
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* wrap list */
+ strcat (tmp,")");
+ /* if append OK, add to source set */
+ if ((ret = mix_append_msg (astream,msgf,tmp,elt,&st,dest,
+ seq)) && source)
+ mail_append_set (source,mail_uid (stream,i));
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if (ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT))) {
+ /* success, delete if doing a move */
+ if (options & CP_MOVE)
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T;
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ /* done with status file now */
+ mix_status_update (astream,statf,LONGT);
+ /* return sets if doing COPYUID */
+ if (cu) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ source = dest = NIL; /* don't free these sets now */
+ }
+ }
+ else { /* error */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message copy failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert file */
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any sets remaining */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening copy message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (stream);
+ }
+ else MM_LOG ("Can't open copy mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ /* finished with append stream */
+ if (astream) mail_close (astream);
+ return ret; /* return state */
+}
+
+/* MIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ STRING *message;
+ char *flags,*date,tmp[MAILTMPLEN];
+ /* N.B.: can't use LOCAL->buf for tmp */
+ long ret = mix_isvalid (mailbox,tmp);
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mixproto);
+ if (!ret) switch (errno) { /* if not valid mailbox */
+ case ENOENT: /* no such file? */
+ if (ret = compare_cstring (mailbox,"INBOX") ?
+ NIL : mix_create (NIL,"INBOX"))
+ break;
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ break;
+ default:
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ }
+
+ /* get first message */
+ if (ret && MM_APPEND (af) (stream,data,&flags,&date,&message)) {
+ MAILSTREAM *astream;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long size,hdrsize;
+ MESSAGECACHE elt;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+ MM_CRITICAL (astream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,hdrsize + SIZE (message))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ while (ret && message) {/* while good to go and have messages */
+ errno = NIL; /* in case one of these causes failure */
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else {
+ if (!date) { /* if date not specified, use now */
+ internal_date (tmp);
+ mail_parse_date (&elt,tmp);
+ }
+ ret = mix_append_msg (astream,msgf,flags,&elt,message,dst,seq) &&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if ((ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT) &&
+ mix_status_update (astream,statf,LONGT))) && au) {
+ (*au) (mailbox,astream->uid_validity,dst);
+ dst = NIL; /* don't free this set now */
+ }
+ }
+ else { /* failure */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message append failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert all writes to file*/
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any set remaining */
+ mail_free_searchset (&dst);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening append message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ }
+ else MM_LOG ("Can't open append mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ if (astream) mail_close (astream);
+ }
+ return ret;
+}
+
+/* MIX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * modseq of message
+ * Returns: T if success, NIL if failure
+ */
+
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq)
+{
+ MESSAGECACHE *elt;
+ int c,cs;
+ unsigned long i,j,k,uf,hoff;
+ long sf;
+ stream->kwd_create = NIL; /* don't copy unknown keywords */
+ sf = mail_parse_flags (stream,flags,&uf);
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* assign new UID from metadata */
+ (elt = mail_elt (stream,stream->nmsgs))->private.uid = ++stream->uid_last;
+ elt->private.mod = seq; /* set requested modseq in status */
+ elt->rfc822_size = SIZE (msg);/* copy message size and date to index */
+ elt->year = delt->year; elt->month = delt->month; elt->day = delt->day;
+ elt->hours = delt->hours; elt->minutes = delt->minutes;
+ elt->seconds = delt->seconds; elt->zoccident = delt->zoccident;
+ elt->zhours = delt->zhours; elt->zminutes = delt->zminutes;
+ /*
+ * Do NOT set elt->valid here! mix_status_update() uses it to determine
+ * whether a message should be marked as old.
+ */
+ if (sf&fSEEN) elt->seen = T; /* copy flags to status */
+ if (sf&fDELETED) elt->deleted = T;
+ if (sf&fFLAGGED) elt->flagged = T;
+ if (sf&fANSWERED) elt->answered = T;
+ if (sf&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ /* message is in new message file */
+ elt->private.spare.data = LOCAL->newmsg;
+
+ /* offset to message internal header */
+ elt->private.special.offset = ftell (f);
+ /* build header for message */
+ fprintf (f,MSRFMT,MSGTOK,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size);
+ /* offset to header from internal header */
+ elt->private.msg.header.offset = ftell (f) - elt->private.special.offset;
+ for (cs = 0; SIZE (msg); ) { /* copy message */
+ if (elt->private.msg.header.text.size) {
+ if (msg->cursize) /* blat entire chunk if have it */
+ for (j = msg->cursize; j; j -= k)
+ if (!(k = fwrite (msg->curpos,1,j,f))) return NIL;
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ }
+ else { /* still searching for delimiter */
+ c = 0xff & SNX (msg); /* get source character */
+ if (putc (c,f) == EOF) return NIL;
+ switch (cs) { /* decide what to do based on state */
+ case 0: /* previous char ordinary */
+ if (c == '\015') cs = 1;/* advance if CR */
+ break;
+ case 1: /* previous CR, advance if LF */
+ cs = (c == '\012') ? 2 : 0;
+ break;
+ case 2: /* previous CRLF, advance if CR */
+ cs = (c == '\015') ? 3 : 0;
+ break;
+ case 3: /* previous CRLFCR, done if LF */
+ if (c == '\012') elt->private.msg.header.text.size =
+ elt->rfc822_size - SIZE (msg);
+ cs = 0; /* reset mechanism */
+ break;
+ }
+ }
+ }
+ /* if no delimiter, header is entire msg */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* add this message to set */
+ mail_append_set (set,elt->private.uid);
+ return LONGT; /* success */
+}
+
+/* MIX mail read metadata, index, and status
+ * Accepts: MAIL stream
+ * returned index file
+ * index file flags (non-NIL if want to add/remove messages)
+ * status file flags (non-NIL if want to update elt->valid and old)
+ * Returns: open status file, or NIL if failure
+ *
+ * Note that this routine can return an open index file even if it fails!
+ */
+
+static char *shortmsg =
+ "message %lu (UID=%.08lx) truncated by %lu byte(s) (%lu < %lu)";
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags)
+{
+ int fd;
+ unsigned long i;
+ char *s,*t;
+ struct stat sbuf;
+ FILE *statf = NIL;
+ short metarepairneeded = 0;
+ short indexrepairneeded = 0;
+ short silent = stream->silent;
+ *idxf = NIL; /* in case error */
+ /* readonly means no updates */
+ if (stream->rdonly) iflags = sflags = NIL;
+ /* open index file */
+ if ((fd = open (LOCAL->index,iflags ? O_RDWR : O_RDONLY,NIL)) < 0)
+ MM_LOG ("Error opening mix index file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,iflags ? LOCK_EX : LOCK_SH) &&
+ !(*idxf = fdopen (fd,iflags ? "r+b" : "rb"))) {
+ MM_LOG ("Error obtaining stream on mix index file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+
+ /* slurp metadata */
+ else if (s = mix_meta_slurp (stream,&i)) {
+ unsigned long j = 0; /* non-zero if UIDVALIDITY/UIDLAST changed */
+ if (i != LOCAL->metaseq) { /* metadata changed? */
+ char *t,*k;
+ LOCAL->metaseq = i; /* note new metadata sequence */
+ while (s && *s) { /* parse entire metadata file */
+ /* locate end of line */
+ if (s = strstr (t = s,"\015\012")) {
+ *s = '\0'; /* tie off line */
+ s += 2; /* skip past CRLF */
+ switch (*t++) { /* parse line */
+ case 'V': /* UIDVALIDITY */
+ if (!isxdigit (*t) || !(i = strtoul (t,&t,16))) {
+ MM_LOG ("Error in mix metadata file UIDVALIDITY record",ERROR);
+ return NIL; /* give up */
+ }
+ if (i != stream->uid_validity) j = stream->uid_validity = i;
+ break;
+ case 'L': /* new UIDLAST */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file UIDLAST record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ j = stream->uid_last = i;
+ break;
+ case 'N': /* new message file */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file new msg record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ LOCAL->newmsg = i;
+ break;
+ case 'K': /* new keyword list */
+ for (i = 0; t && *t && (i < NUSERFLAGS); ++i) {
+ if (t = strchr (k = t,' ')) *t++ = '\0';
+ /* make sure keyword non-empty */
+ if (*k && (strlen (k) <= MAXUSERFLAG)) {
+ /* in case value changes (shouldn't happen) */
+ if (stream->user_flags[i] && strcmp (stream->user_flags[i],k)){
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"flag rename old=%.80s new=%.80s",
+ stream->user_flags[i],k);
+ MM_LOG (tmp,WARN);
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (k);
+ }
+ else break; /* empty keyword */
+ }
+ if ((i < NUSERFLAGS) && stream->user_flags[i]) {
+ MM_LOG ("Error in mix metadata file keyword record",ERROR);
+ return NIL; /* give up */
+ }
+ else if (i == NUSERFLAGS) stream->kwd_create = NIL;
+ break;
+ }
+ }
+ if (t && *t) { /* junk in line */
+ MM_LOG ("Error in mix metadata record",ERROR);
+ return NIL; /* give up */
+ }
+ }
+ }
+
+ /* get sequence */
+ if (!(i = mix_read_sequence (*idxf)) || (i < LOCAL->indexseq)) {
+ MM_LOG ("Error in mix index file sequence record",ERROR);
+ return NIL; /* give up */
+ }
+ /* sequence changed from last time? */
+ else if (j || (i > LOCAL->indexseq)) {
+ unsigned long uid,nmsgs,curfile,curfilesize,curpos;
+ char *t,*msg,tmp[MAILTMPLEN];
+ /* start with no messages */
+ curfile = curfilesize = curpos = nmsgs = 0;
+ /* update sequence iff expunging OK */
+ if (LOCAL->expok) LOCAL->indexseq = i;
+ /* get first elt */
+ while ((s = mix_read_record (*idxf,LOCAL->buf,LOCAL->buflen,"index")) &&
+ *s)
+ switch (*s) {
+ case ':': /* message record */
+ if (!(isxdigit (*++s) && (uid = strtoul (s,&t,16)))) msg = "UID";
+ else if (!((*t++ == ':') && isdigit (*t) && isdigit (t[1]) &&
+ isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) &&
+ isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) &&
+ isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) &&
+ isdigit (t[11]) && isdigit (t[12]) && isdigit (t[13]) &&
+ ((t[14] == '+') || (t[14] == '-')) &&
+ isdigit (t[15]) && isdigit (t[16]) && isdigit (t[17]) &&
+ isdigit (t[18]))) msg = "internaldate";
+ else if ((*(s = t+19) != ':') || !isxdigit (*++s)) msg = "size";
+ else {
+ unsigned int y = (((*t - '0') * 1000) + ((t[1] - '0') * 100) +
+ ((t[2] - '0') * 10) + t[3] - '0') - BASEYEAR;
+ unsigned int m = ((t[4] - '0') * 10) + t[5] - '0';
+ unsigned int d = ((t[6] - '0') * 10) + t[7] - '0';
+ unsigned int hh = ((t[8] - '0') * 10) + t[9] - '0';
+ unsigned int mm = ((t[10] - '0') * 10) + t[11] - '0';
+ unsigned int ss = ((t[12] - '0') * 10) + t[13] - '0';
+ unsigned int z = (t[14] == '-') ? 1 : 0;
+ unsigned int zh = ((t[15] - '0') * 10) + t[16] - '0';
+ unsigned int zm = ((t[17] - '0') * 10) + t[18] - '0';
+ unsigned long size = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long file = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long pos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hpos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hsiz = strtoul (s,&s,16);
+ if (uid > stream->uid_last) {
+ sprintf (tmp,"mix index invalid UID (%08lx < %08lx)",
+ uid,stream->uid_last);
+ if (stream->rdonly) {
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ strcat (tmp,", repaired");
+ MM_LOG (tmp,WARN);
+ stream->uid_last = uid;
+ metarepairneeded = T;
+ }
+
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ MESSAGECACHE *elt;
+ ++nmsgs; /* this is another mesage */
+ /* within current known range of messages? */
+ while (nmsgs <= stream->nmsgs) {
+ /* yes, get corresponding elt */
+ elt = mail_elt (stream,nmsgs);
+ /* existing message with matching data? */
+ if (uid == elt->private.uid) {
+ /* beware of Dracula's resurrection */
+ if (elt->private.ghost) {
+ sprintf (tmp,"mix index data unexpunged UID: %lx",
+ uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* also of static data changing */
+ if ((size != elt->rfc822_size) ||
+ (file != elt->private.spare.data) ||
+ (pos != elt->private.special.offset) ||
+ (hpos != elt->private.msg.header.offset) ||
+ (hsiz != elt->private.msg.header.text.size) ||
+ (y != elt->year) || (m != elt->month) ||
+ (d != elt->day) || (hh != elt->hours) ||
+ (mm != elt->minutes) || (ss != elt->seconds) ||
+ (z != elt->zoccident) || (zh != elt->zhours) ||
+ (zm != elt->zminutes)) {
+ sprintf (tmp,"mix index data mismatch: %lx",uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ break;
+ }
+ /* existing msg with lower UID is expunged */
+ else if (uid > elt->private.uid) {
+ if (LOCAL->expok) mail_expunged (stream,nmsgs);
+ else {/* message expunged, but not yet for us */
+ ++nmsgs;
+ elt->private.ghost = T;
+ }
+ }
+ else { /* unexpected message record */
+ sprintf (tmp,"mix index UID mismatch (%lx < %lx)",
+ uid,elt->private.uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* time to create a new message? */
+ if (nmsgs > stream->nmsgs) {
+ /* defer announcing until later */
+ stream->silent = T;
+ mail_exists (stream,nmsgs);
+ stream->silent = silent;
+ (elt = mail_elt (stream,nmsgs))->recent = T;
+ elt->private.uid = uid; elt->rfc822_size = size;
+ elt->private.spare.data = file;
+ elt->private.special.offset = pos;
+ elt->private.msg.header.offset = hpos;
+ elt->private.msg.header.text.size = hsiz;
+ elt->year = y; elt->month = m; elt->day = d;
+ elt->hours = hh; elt->minutes = mm;
+ elt->seconds = ss; elt->zoccident = z;
+ elt->zhours = zh; elt->zminutes = zm;
+ /* message in same file? */
+ if (curfile == file) {
+ if (pos < curpos) {
+ MESSAGECACHE *plt = mail_elt (stream,elt->msgno-1);
+ /* uh-oh, calculate delta? */
+ i = curpos - pos;
+ sprintf (tmp,shortmsg,plt->msgno,plt->private.uid,
+ i,pos,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < plt->rfc822_size)) {
+ plt->rfc822_size -= i;
+ if (plt->rfc822_size <
+ plt->private.msg.header.text.size)
+ plt->private.msg.header.text.size =
+ plt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* new file, restart */
+ if (stat (mix_file_data (LOCAL->buf,stream->mailbox,
+ curfile = file),&sbuf)) {
+ sprintf (tmp,"Missing mix data file: %.500s",
+ LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ curfile = file;
+ curfilesize = sbuf.st_size;
+ }
+
+ /* position of message in file */
+ curpos = pos + elt->private.msg.header.offset +
+ elt->rfc822_size;
+ /* short file? */
+ if (curfilesize < curpos) {
+ /* uh-oh, calculate delta? */
+ i = curpos - curfilesize;
+ sprintf (tmp,shortmsg,elt->msgno,elt->private.uid,
+ i,curfilesize,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < elt->rfc822_size)) {
+ elt->rfc822_size -= i;
+ if (elt->rfc822_size <
+ elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size =
+ elt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ break;
+ }
+ else msg = "expansion";
+ }
+ else msg = "header size";
+ }
+ else msg = "header position";
+ }
+ else msg = "message position";
+ }
+ else msg = "file#";
+ }
+ sprintf (tmp,"Error in %s in mix index file: %.500s",msg,s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Unknown record in mix index file: %.500s",s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if (!s) return NIL; /* barfage from mix_read_record() */
+ /* expunge trailing messages not in index */
+ if (LOCAL->expok) while (nmsgs < stream->nmsgs)
+ mail_expunged (stream,stream->nmsgs);
+ }
+
+ /* repair metadata and index if needed */
+ if ((metarepairneeded ? mix_meta_update (stream) : T) &&
+ (indexrepairneeded ? mix_index_update (stream,*idxf,NIL) : T)) {
+ MESSAGECACHE *elt;
+ int fd;
+ unsigned long uid,uf,sf,mod;
+ char *s;
+ int updatep = NIL;
+ /* open status file */
+ if ((fd = open (LOCAL->status,
+ stream->rdonly ? O_RDONLY : O_RDWR,NIL)) < 0)
+ MM_LOG ("Error opening mix status file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,stream->rdonly ? LOCK_SH : LOCK_EX) &&
+ !(statf = fdopen (fd,stream->rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix status file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ /* get sequence */
+ else if (!(i = mix_read_sequence (statf)) ||
+ ((i < LOCAL->statusseq) && stream->nmsgs && (i != 1))) {
+ sprintf (LOCAL->buf,
+ "Error in mix status sequence record, i=%lx, seq=%lx",
+ i,LOCAL->statusseq);
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sequence changed from last time? */
+ else if (i != LOCAL->statusseq) {
+ /* update sequence, get first elt */
+ if (i > LOCAL->statusseq) LOCAL->statusseq = i;
+ if (stream->nmsgs) {
+ elt = mail_elt (stream,i = 1);
+
+ /* read message records */
+ while ((t = s = mix_read_record (statf,LOCAL->buf,LOCAL->buflen,
+ "status")) && *s && (*s++ == ':') &&
+ isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ uf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ mod = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ /* need to move ahead to next elt? */
+ while ((uid > elt->private.uid) && (i < stream->nmsgs))
+ elt = mail_elt (stream,++i);
+ /* update elt if altered */
+ if ((uid == elt->private.uid) &&
+ (!elt->valid || (mod != elt->private.mod))) {
+ elt->user_flags = uf;
+ elt->private.mod = mod;
+ elt->seen = (sf & fSEEN) ? T : NIL;
+ elt->deleted = (sf & fDELETED) ? T : NIL;
+ elt->flagged = (sf & fFLAGGED) ? T : NIL;
+ elt->answered = (sf & fANSWERED) ? T : NIL;
+ elt->draft = (sf & fDRAFT) ? T : NIL;
+ /* announce if altered existing message */
+ if (elt->valid) MM_FLAGS (stream,elt->msgno);
+ /* first time, is old message? */
+ else if (sf & fOLD) {
+ /* yes, clear recent and set valid */
+ elt->recent = NIL;
+ elt->valid = T;
+ }
+ /* recent, allowed to update its status? */
+ else if (sflags) {
+ /* yes, set valid and check in status */
+ elt->valid = T;
+ elt->private.mod = mix_modseq (elt->private.mod);
+ updatep = T;
+ }
+ /* leave valid unset and recent if sflags not set */
+ }
+ continue; /* everything looks good */
+ }
+ }
+ }
+ }
+ break; /* error somewhere */
+ }
+
+ if (t && *t) { /* non-null means bogus record */
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Error in mix status file message record%s: %.80s",
+ stream->rdonly ? "" : ", fixing",t);
+ MM_LOG (msg,WARN);
+ /* update it if not readonly */
+ if (!stream->rdonly) updatep = T;
+ }
+ if (updatep) { /* need to update? */
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,LONGT);
+ }
+ }
+ }
+ }
+ }
+ if (statf) { /* still happy? */
+ unsigned long j;
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ for (i = 1, j = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->recent) ++j;
+ mail_recent (stream,j);
+ }
+ return statf;
+}
+
+/* MIX metadata file routines */
+
+/* MIX read metadata
+ * Accepts: MAIL stream
+ * return pointer for modseq
+ * Returns: pointer to metadata after modseq or NIL if failure
+ */
+
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq)
+{
+ struct stat sbuf;
+ char *s;
+ char *ret = NIL;
+ if (fstat (LOCAL->mfd,&sbuf))
+ MM_LOG ("Error obtaining size of mix metatdata file",ERROR);
+ if (sbuf.st_size > LOCAL->buflen) {
+ /* should be just a few dozen bytes */
+ if (sbuf.st_size > METAMAX) fatal ("absurd mix metadata file size");
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
+ }
+ /* read current metadata file */
+ LOCAL->buf[sbuf.st_size] = '\0';
+ if (lseek (LOCAL->mfd,0,L_SET) ||
+ (read (LOCAL->mfd,s = LOCAL->buf,sbuf.st_size) != sbuf.st_size))
+ MM_LOG ("Error reading mix metadata file",ERROR);
+ else if ((*s != 'S') || !isxdigit (s[1]) ||
+ ((*seq = strtoul (s+1,&s,16)) < LOCAL->metaseq) ||
+ (*s++ != '\015') || (*s++ != '\012'))
+ MM_LOG ("Error in mix metadata file sequence record",ERROR);
+ else ret = s;
+ return ret;
+}
+
+/* MIX update metadata
+ * Accepts: MAIL stream
+ * Returns: T on success, NIL if error
+ *
+ * Index MUST be locked!!
+ */
+
+long mix_meta_update (MAILSTREAM *stream)
+{
+ long ret;
+ /* do nothing if stream readonly */
+ if (stream->rdonly) ret = LONGT;
+ else {
+ unsigned char c,*s,*ss,*t;
+ unsigned long i;
+ /* The worst-case metadata is limited to:
+ * 4 * (1 + 8 + 2) + (NUSERFLAGS * (MAXUSERFLAG + 1))
+ * which comes out to 1994 octets. This is much smaller than the normal
+ * CHUNKSIZE definition of 64K, and CHUNKSIZE is the smallest size of
+ * LOCAL->buf.
+ *
+ * If more stuff gets added to the metadata, or if you change the value
+ * of NUSERFLAGS, MAXUSERFLAG or CHUNKSIZE, be sure to recalculate the
+ * above assertation.
+ */
+ sprintf (LOCAL->buf,SEQFMT,LOCAL->metaseq = mix_modseq (LOCAL->metaseq));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),MTAFMT,
+ stream->uid_validity,stream->uid_last,LOCAL->newmsg);
+ for (i = 0, c = 'K', s = ss = LOCAL->buf + strlen (LOCAL->buf);
+ (i < NUSERFLAGS) && (t = stream->user_flags[i]); ++i) {
+ if (!*t) fatal ("impossible empty keyword");
+ *s++ = c; /* write delimiter */
+ while (*t) *s++ = *t++; /* write keyword */
+ c = ' '; /* delimiter is now space */
+ }
+ if (s != ss) { /* tie off keywords line */
+ *s++ = '\015'; *s++ = '\012';
+ }
+ /* calculate length of metadata */
+ if ((i = s - LOCAL->buf) > LOCAL->buflen)
+ fatal ("impossible buffer overflow");
+ lseek (LOCAL->mfd,0,L_SET); /* rewind file */
+ /* write new metadata */
+ ret = (write (LOCAL->mfd,LOCAL->buf,i) == i) ? LONGT : NIL;
+ ftruncate (LOCAL->mfd,i); /* and tie off at that point */
+ }
+ return ret;
+}
+
+/* MIX index file routines */
+
+
+/* MIX update index
+ * Accepts: MAIL stream
+ * open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag)
+{
+ unsigned long i;
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* Winston Smith's first dairy entry */
+ sprintf (tmp,IXRFMT,0,14,4,4,13,0,0,'+',0,0,0,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ /* calculate file size we need */
+ sprintf (tmp,SEQFMT,LOCAL->indexseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (idxf),&sbuf)) {
+ MM_LOG ("Error getting size of mix index file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (idxf,0,SEEK_END) || (fwrite (buf,1,size,idxf) != size) ||
+ fflush (idxf)) {
+ fseek (idxf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (idxf),sbuf.st_size);
+ MM_LOG ("Error extending mix index file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (idxf); /* let's start at the very beginning */
+ /* write modseq first */
+ fprintf (idxf,SEQFMT,LOCAL->indexseq);
+ /* then write all messages */
+ for (i = 1; ret && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (idxf,IXRFMT,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size,elt->private.spare.data,
+ elt->private.special.offset,
+ elt->private.msg.header.offset,
+ elt->private.msg.header.text.size);
+ if (ferror (idxf)) {
+ MM_LOG ("Error updating mix index file",ERROR);
+ ret = NIL;
+ }
+ }
+ if (fflush (idxf)) {
+ MM_LOG ("Error flushing mix index file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (idxf),ftell (idxf));
+ }
+ }
+ return ret;
+}
+
+/* MIX status file routines */
+
+
+/* MIX update status
+ * Accepts: MAIL stream
+ * pointer to open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* number of living messages */
+ sprintf (tmp,STRFMT,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ sprintf (tmp,SEQFMT,LOCAL->statusseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (statf),&sbuf)) {
+ MM_LOG ("Error getting size of mix status file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (statf,0,SEEK_END) || (fwrite (buf,1,size,statf) != size) ||
+ fflush (statf)) {
+ fseek (statf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (statf),sbuf.st_size);
+ MM_LOG ("Error extending mix status file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (statf); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (statf,SEQFMT,LOCAL->statusseq);
+ /* write message status records */
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ /* make sure all messages have a modseq */
+ if (!elt->private.mod) elt->private.mod = LOCAL->statusseq;
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (statf,STRFMT,elt->private.uid,elt->user_flags,
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + (elt->valid ? fOLD : NIL),
+ elt->private.mod);
+ if (ferror (statf)) {
+ sprintf (tmp,"Error updating mix status file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (statf)) {
+ MM_LOG ("Error flushing mix status file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (statf),ftell (statf));
+ }
+ }
+ return ret;
+}
+
+/* MIX data file routines */
+
+
+/* MIX open data file
+ * Accepts: MAIL stream
+ * pointer to returned fd if success
+ * pointer to returned size if success
+ * size of new data to be added
+ * Returns: open FILE, or NIL if failure
+ *
+ * The curend test assumes that the last message of the mailbox is the furthest
+ * point that the current data file extends, and thus that is all that needs to
+ * be tested for short file prevention.
+ */
+
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize)
+{
+ FILE *msgf = NIL;
+ struct stat sbuf;
+ MESSAGECACHE *elt = stream->nmsgs ? mail_elt (stream,stream->nmsgs) : NIL;
+ unsigned long curend = (elt && (elt->private.spare.data == LOCAL->newmsg)) ?
+ elt->private.special.offset + elt->private.msg.header.offset +
+ elt->rfc822_size : 0;
+ /* allow create if curend 0 */
+ if ((*fd = open (mix_file_data (LOCAL->buf,stream->mailbox,LOCAL->newmsg),
+ O_RDWR | (curend ? NIL : O_CREAT),NIL)) >= 0) {
+ fstat (*fd,&sbuf); /* get current file size */
+ /* can we use this file? */
+ if ((curend <= sbuf.st_size) &&
+ (!sbuf.st_size || ((sbuf.st_size + newsize) <= MIXDATAROLL)))
+ *size = sbuf.st_size; /* yes, return current size */
+ else { /* short file or becoming too long */
+ if (curend > sbuf.st_size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"short mix message file %.08lx (%ld > %ld), rolling",
+ LOCAL->newmsg,curend,sbuf.st_size);
+ MM_LOG (tmp,WARN); /* shouldn't happen */
+ }
+ close (*fd); /* roll to a new file */
+ while ((*fd = open (mix_file_data
+ (LOCAL->buf,stream->mailbox,
+ LOCAL->newmsg = mix_modseq (LOCAL->newmsg)),
+ O_RDWR | O_CREAT | O_EXCL,sbuf.st_mode)) < 0);
+ *size = 0; /* brand new file */
+ fchmod (*fd,sbuf.st_mode);/* with same mode as previous file */
+ }
+ }
+ if (*fd >= 0) { /* have a data file? */
+ /* yes, get stdio and set position */
+ if (msgf = fdopen (*fd,"r+b")) fseek (msgf,*size,SEEK_SET);
+ else close (*fd); /* fdopen() failed? */
+ }
+ return msgf; /* return results */
+}
+
+/* MIX open sortcache
+ * Accepts: MAIL stream
+ * Returns: open FILE, or NIL if failure or could only get readonly sortcache
+ */
+
+FILE *mix_sortcache_open (MAILSTREAM *stream)
+{
+ int fd,refwd;
+ unsigned long i,uid,sentdate,fromlen,tolen,cclen,subjlen,msgidlen,reflen;
+ char *s,*t,*msg,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ SORTCACHE *sc;
+ STRINGLIST *sl;
+ struct stat sbuf;
+ int rdonly = NIL;
+ FILE *srtcf = NIL;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ fstat (LOCAL->mfd,&sbuf);
+ if (!stream->nmsgs); /* do nothing if mailbox empty */
+ /* open sortcache file */
+ else if (((fd = open (LOCAL->sortcache,O_RDWR|O_CREAT,sbuf.st_mode)) < 0) &&
+ !(rdonly = ((fd = open (LOCAL->sortcache,O_RDONLY,NIL)) >= 0)))
+ MM_LOG ("Error opening mix sortcache file",WARN);
+ /* acquire lock and FILE */
+ else if (!flock (fd,rdonly ? LOCK_SH : LOCK_EX) &&
+ !(srtcf = fdopen (fd,rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix sortcache file",WARN);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ else if (!(i = mix_read_sequence (srtcf)) || (i < LOCAL->sortcacheseq))
+ MM_LOG ("Error in mix sortcache file sequence record",WARN);
+ /* sequence changed from last time? */
+ else if (i > LOCAL->sortcacheseq) {
+ LOCAL->sortcacheseq = i; /* update sequence */
+ while ((s = t = mix_read_record (srtcf,LOCAL->buf,LOCAL->buflen,
+ "sortcache")) && *s &&
+ (msg = "uid") && (*s++ == ':') && isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sentdate = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ fromlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ tolen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ cclen = strtoul (s,&s,16);
+ if ((*s++ == ':') && ((*s == 'R') || (*s == ' ')) &&
+ isxdigit (s[1])) {
+ refwd = (*s++ == 'R') ? T : NIL;
+ subjlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ msgidlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ reflen = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+
+ if (i = mail_msgno (stream,uid)) {
+ sc = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ sc->size = (elt = mail_elt (stream,i))->rfc822_size;
+ sc->date = sentdate;
+ sc->arrival = elt->day ? mail_longdate (elt) : 1;
+ if (refwd) sc->refwd = T;
+ if (fromlen) {
+ if (sc->from) fseek (srtcf,fromlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'F') ||
+ (fread (sc->from = (char *) fs_get(fromlen),
+ 1,fromlen-1,srtcf) != (fromlen-1))||
+ (sc->from[fromlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "from data";
+ break;
+ }
+ }
+ if (tolen) {
+ if (sc->to) fseek (srtcf,tolen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'T') ||
+ (fread (sc->to = (char *) fs_get (tolen),
+ 1,tolen-1,srtcf) != (tolen - 1)) ||
+ (sc->to[tolen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "to data";
+ break;
+ }
+ }
+ if (cclen) {
+ if (sc->cc) fseek (srtcf,cclen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'C') ||
+ (fread (sc->cc = (char *) fs_get (cclen),
+ 1,cclen-1,srtcf) != (cclen - 1)) ||
+ (sc->cc[cclen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "cc data";
+ break;
+ }
+ }
+ if (subjlen) {
+ if (sc->subject) fseek (srtcf,subjlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'S') ||
+ (fread (sc->subject =
+ (char *) fs_get (subjlen),1,
+ subjlen-1,srtcf) != (subjlen-1))||
+ (sc->subject[subjlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "subject data";
+ break;
+ }
+ }
+
+ if (msgidlen) {
+ if (sc->message_id)
+ fseek (srtcf,msgidlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'M') ||
+ (fread (sc->message_id =
+ (char *) fs_get (msgidlen),1,
+ msgidlen-1,srtcf) != (msgidlen-1))||
+ (sc->message_id[msgidlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "message-id data";
+ break;
+ }
+ }
+ if (reflen) {
+ if (sc->references) fseek(srtcf,reflen + 2,SEEK_CUR);
+ /* make sure it fits */
+ else {
+ if (reflen >= LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *)
+ fs_get ((LOCAL->buflen = reflen) + 1);
+ }
+ if ((getc (srtcf) != 'R') ||
+ (fread (LOCAL->buf,1,reflen-1,srtcf) !=
+ (reflen - 1)) ||
+ (LOCAL->buf[reflen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "references data";
+ break;
+ }
+ for (s = LOCAL->buf,sl = NIL,
+ sc->references = mail_newstringlist ();
+ s && *s; s += i + 1) {
+ if ((i = strtoul (s,&s,16)) && (*s++ == ':') &&
+ (s[i] == ':')) {
+ if (sl) sl = sl->next = mail_newstringlist();
+ else sl = sc->references;
+ s[i] = '\0';
+ sl->text.data = cpystr (s);
+ sl->text.size = i;
+ }
+ else s = NIL;
+ }
+ if (!s || *s ||
+ (s != ((char *) LOCAL->buf + reflen - 1))) {
+ msg = "references length consistency check";
+ break;
+ }
+ }
+ }
+ }
+
+ /* UID not found, ignore this message */
+ else fseek (srtcf,((fromlen ? fromlen + 2 : 0) +
+ (tolen ? tolen + 2 : 0) +
+ (cclen ? cclen + 2 : 0) +
+ (subjlen ? subjlen + 2 : 0) +
+ (msgidlen ? msgidlen + 2 : 0) +
+ (reflen ? reflen + 2 : 0)),
+ SEEK_CUR);
+ continue;
+ }
+ else msg = "expansion";
+ }
+ else msg = "references";
+ }
+ else msg = "message-id";
+ }
+ else msg = "subject";
+ }
+ else msg = "cc";
+ }
+ else msg = "to";
+ }
+ else msg = "from";
+ }
+ else msg = "sentdate";
+ break; /* error somewhere */
+ }
+ if (!t || *t) { /* error detected? */
+ if (t) { /* non-null means bogus record */
+ sprintf (tmp,"Error in %s in mix sortcache record: %.500s",msg,t);
+ MM_LOG (tmp,WARN);
+ }
+ fclose (srtcf); /* either way, must punt */
+ srtcf = NIL;
+ }
+ }
+ if (rdonly && srtcf) { /* can't update if readonly */
+ unlink (LOCAL->sortcache); /* try deleting it */
+ fclose (srtcf); /* so close it and return as if error */
+ srtcf = NIL;
+ }
+ else fchmod (fd,sbuf.st_mode);
+ return srtcf;
+}
+
+/* MIX update and close sortcache
+ * Accepts: MAIL stream
+ * pointer to open FILE (if FILE is NIL, do nothing)
+ * Returns: T on success, NIL on error
+ */
+
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache)
+{
+ FILE *f = *sortcache;
+ long ret = LONGT;
+ if (f) { /* ignore if no file */
+ unsigned long i,j;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ for (i = 1; (i <= stream->nmsgs) &&
+ !((SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE))->dirty; ++i);
+ if (i <= stream->nmsgs) { /* only update if some entry is dirty */
+ rewind (f); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (f,SEQFMT,LOCAL->sortcacheseq = mix_modseq(LOCAL->sortcacheseq));
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ SORTCACHE *s = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ STRINGLIST *sl;
+ s->dirty = NIL; /* no longer dirty */
+ if (sl = s->references) /* count length of references */
+ for (j = 1; sl && sl->text.data; sl = sl->next)
+ j += 10 + sl->text.size;
+ else j = 0; /* no references yet */
+ fprintf (f,SCRFMT,elt->private.uid,s->date,
+ s->from ? strlen (s->from) + 1 : 0,
+ s->to ? strlen (s->to) + 1 : 0,s->cc ? strlen (s->cc) + 1 : 0,
+ s->refwd ? 'R' : ' ',s->subject ? strlen (s->subject) + 1: 0,
+ s->message_id ? strlen (s->message_id) + 1 : 0,j);
+ if (s->from) fprintf (f,"F%s\015\012",s->from);
+ if (s->to) fprintf (f,"T%s\015\012",s->to);
+ if (s->cc) fprintf (f,"C%s\015\012",s->cc);
+ if (s->subject) fprintf (f,"S%s\015\012",s->subject);
+ if (s->message_id) fprintf (f,"M%s\015\012",s->message_id);
+ if (j) { /* any references to write? */
+ fputc ('R',f); /* yes, do so */
+ for (sl = s->references; sl && sl->text.data; sl = sl->next)
+ fprintf (f,"%08lx:%s:",sl->text.size,sl->text.data);
+ fputs ("\015\012",f);
+ }
+ if (ferror (f)) {
+ MM_LOG ("Error updating mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (f)) {
+ MM_LOG ("Error flushing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (f),ftell (f));
+ }
+ if (fclose (f)) {
+ MM_LOG ("Error closing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ return ret;
+}
+
+/* MIX generic file routines */
+
+/* MIX read record
+ * Accepts: open FILE
+ * buffer
+ * buffer length
+ * record type
+ * Returns: buffer if success, else NIL (zero-length buffer means EOF)
+ */
+
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type)
+{
+ char *s,tmp[MAILTMPLEN];
+ /* ensure string tied off */
+ buf[buflen-2] = buf[buflen-1] = '\0';
+ while (fgets (buf,buflen-1,f)) {
+ if (s = strchr (buf,'\012')) {
+ if ((s != buf) && (s[-1] == '\015')) --s;
+ *s = '\0'; /* tie off buffer */
+ if (s != buf) return buf; /* return if non-empty buffer */
+ sprintf (tmp,"Empty mix %s record",type);
+ MM_LOG (tmp,WARN);
+ }
+ else if (buf[buflen-2]) { /* overlong record is bad news */
+ sprintf (tmp,"Oversize mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sprintf (tmp,"Truncated mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,WARN);
+ return buf; /* pass to caller anyway */
+ }
+ }
+ buf[0] = '\0'; /* return empty buffer on EOF */
+ return buf;
+}
+
+/* MIX read sequence record
+ * Accepts: open FILE
+ * Returns: sequence value, or NIL if failure
+ */
+
+unsigned long mix_read_sequence (FILE *f)
+{
+ unsigned long ret;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_read_record (f,tmp,MAILTMPLEN-1,"sequence")) return NIL;
+ switch (tmp[0]) { /* examine record */
+ case '\0': /* end of file */
+ ret = 1; /* start a new sequence regime */
+ break;
+ case 'S': /* sequence record */
+ if (isxdigit (tmp[1])) { /* must be followed by hex value */
+ ret = strtoul (tmp+1,&s,16);
+ if (!*s) break; /* and nothing more */
+ }
+ /* drop into default case */
+ default: /* anything else is an error */
+ return NIL; /* return error */
+ }
+ return ret;
+}
+
+/* MIX internal routines */
+
+
+/* MIX mail build directory name
+ * Accepts: destination string
+ * source
+ * Returns: destination or empty string if error
+ */
+
+char *mix_dir (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+
+/* MIX mail build file name
+ * Accepts: destination string
+ * directory name
+ * file name
+ * Returns: destination
+ */
+
+char *mix_file (char *dst,char *dir,char *name)
+{
+ sprintf (dst,"%.500s/%.80s%.80s",dir,MIXNAME,name);
+ return dst;
+}
+
+
+/* MIX mail build file name from data file number
+ * Accepts: destination string
+ * directory name
+ * data file number
+ * Returns: destination
+ */
+
+char *mix_file_data (char *dst,char *dir,unsigned long data)
+{
+ char tmp[MAILTMPLEN];
+ if (data) sprintf (tmp,"%08lx",data);
+ else tmp[0] = '\0'; /* compatibility with experimental version */
+ return mix_file (dst,dir,tmp);
+}
+
+/* MIX mail get new modseq
+ * Accepts: old modseq
+ * Returns: new modseq value
+ */
+
+unsigned long mix_modseq (unsigned long oldseq)
+{
+ /* normally time now */
+ unsigned long ret = (unsigned long) time (NIL);
+ /* ensure that modseq doesn't go backwards */
+ if (ret <= oldseq) ret = oldseq + 1;
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mkauths b/imap/src/osdep/unix/mkauths
new file mode 100755
index 00000000..8e9cc0cf
--- /dev/null
+++ b/imap/src/osdep/unix/mkauths
@@ -0,0 +1,40 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Authenticator Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 December 1995
+# Last Edited: 30 August 2006
+
+# Erase old authenticators list
+rm -f auths.c
+touch auths.c
+
+# Now define the new list
+for authenticator
+ do
+ if [ -f Makefile."$authenticator" ]; then
+ make -f Makefile."$authenticator" `cat SPECIALS`
+ fi
+ echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h
+ echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c
+ echo "#include \"auth_"$authenticator".c\"" >> auths.c
+done
diff --git a/imap/src/osdep/unix/mmdf.c b/imap/src/osdep/unix/mmdf.c
new file mode 100644
index 00000000..e962434e
--- /dev/null
+++ b/imap/src/osdep/unix/mmdf.c
@@ -0,0 +1,2549 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MMDF mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Supposedly, this page has everything the MMDF driver needs to know about
+ * the MMDF delimiter. By changing these macros, the MMDF driver should
+ * change with it. Note that if you change the length of MMDFHDRTXT you
+ * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new
+ * size.
+ */
+
+
+/* Useful MMDF constants */
+
+#define MMDFCHR '\01' /* MMDF character */
+#define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */
+ /* MMDF header text */
+#define MMDFHDRTXT "\01\01\01\01\n"
+ /* length of MMDF header text */
+#define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1)
+
+
+/* Validate MMDF header
+ * Accepts: pointer to candidate string to validate as an MMDF header
+ * Returns: T if valid; else NIL
+ */
+
+#define ISMMDF(s) \
+ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \
+ ((s)[3] == MMDFCHR) && ((s)[4] == '\n'))
+
+
+/* Return if a 32-bit word has the start of an MMDF header
+ * Accepts: pointer to word of four bytes to validate as an MMDF header
+ * Returns: pointer to MMDF header, else proceeds
+ */
+
+#define RETIFMMDFWRD(s) { \
+ if (s[3] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \
+ (s[7] == '\n')) return s + 3; \
+ else if (s[2] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \
+ return s + 2; \
+ else if (s[1] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \
+ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \
+ } \
+ } \
+ } \
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\n'; x++); \
+ if (*x) { \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-5 Validates that there is an end of line and points x at it.
+ * Lines 6-13 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 14 Makes sure that there are at least 27 characters in the line.
+ * Lines 15-20 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 21-23 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 24-27 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 28-31 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+
+
+/* MMDF I/O stream local data */
+
+typedef struct mmdf_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} MMDFLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MMDFLOCAL *) stream->local)
+
+
+/* MMDF protected file structure */
+
+typedef struct mmdf_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} MMDFFILE;
+
+/* Function prototypes */
+
+DRIVER *mmdf_valid (char *name);
+long mmdf_isvalid (char *name,char *tmp);
+long mmdf_isvalid_fd (int fd,char *tmp);
+void *mmdf_parameters (long function,void *value);
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat);
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mmdf_create (MAILSTREAM *stream,char *mailbox);
+long mmdf_delete (MAILSTREAM *stream,char *mailbox);
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mmdf_open (MAILSTREAM *stream);
+void mmdf_close (MAILSTREAM *stream,long options);
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mmdf_ping (MAILSTREAM *stream);
+void mmdf_check (MAILSTREAM *stream);
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void mmdf_abort (MAILSTREAM *stream);
+char *mmdf_file (char *dst,char *name);
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long mmdf_extend (MAILSTREAM *stream,unsigned long size);
+void mmdf_write (MMDFFILE *f,char *s,unsigned long i);
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size);
+
+/* MMDF mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mmdfdriver = {
+ "mmdf", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ mmdf_valid, /* mailbox is valid for us */
+ mmdf_parameters, /* manipulate parameters */
+ mmdf_scan, /* scan mailboxes */
+ mmdf_list, /* list mailboxes */
+ mmdf_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mmdf_create, /* create mailbox */
+ mmdf_delete, /* delete mailbox */
+ mmdf_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mmdf_open, /* open mailbox */
+ mmdf_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mmdf_header, /* fetch message header */
+ mmdf_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mmdf_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mmdf_ping, /* ping mailbox to see if still alive */
+ mmdf_check, /* check for new messages */
+ mmdf_expunge, /* expunge deleted messages */
+ mmdf_copy, /* copy messages to another mailbox */
+ mmdf_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mmdfproto = {&mmdfdriver};
+
+char *mmdfhdr = MMDFHDRTXT; /* MMDF header */
+
+/* MMDF mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mmdf_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL;
+}
+
+
+/* MMDF mail test for valid mailbox name
+ * Accepts: mailbox name
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* error -1 for invalid format */
+ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1;
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* MMDF mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid_fd (int fd,char *tmp)
+{
+ int ret = NIL;
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL;
+ return ret; /* return what we should */
+}
+
+
+/* MMDF manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mmdf_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* MMDF mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MMDF mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MMDF mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MMDF mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti));
+ rfc822_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* MMDF mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mmdf_rename (stream,mailbox,NIL);
+}
+
+
+/* MMDF mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = mmdf_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ mmdf_unlock (fd,NIL,&lockx);
+ mmdf_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ mmdf_unlock (fd,NIL,&lockx);
+ }
+ mmdf_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* MMDF mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *mmdf_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mmdfproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("mmdf recycle stream");
+ stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ mmdf_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (mmdf_parse (stream,&lock,LOCK_SH)) {
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* MMDF mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mmdf_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) mmdf_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) mmdf_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ mmdf_abort (stream); /* now punt the file and local data */
+}
+
+/* MMDF mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *mmdf_hlines = NIL;
+
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!mmdf_hlines) { /* once only code */
+ STRINGLIST *lines = mmdf_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* MMDF mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = mmdf_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* MMDF mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* MMDF per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* MMDF mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mmdf_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) mmdf_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) mmdf_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* MMDF mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mmdf_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* MMDF mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (mmdf_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent)
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* MMDF mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(mmdf_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mmdf_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = mmdf_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ mmdf_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* MMDF mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ /* default stream to prototype */
+ if (!stream) { /* stream specified? */
+ stream = &mmdfproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!mmdf_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ mmdf_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!mmdf_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!mmdf_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df);
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"%sFrom %s@%s %sStatus: ",mmdfhdr,myusername(),
+ mylocalhost(),x) < 0) ||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("mmdf_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out ^A and CRs (note copies NUL) */
+ for (x = tmp; x = strpbrk (x,"\01\r"); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ /* make sure read entire msg & wrote trailer */
+ if (i || (fputs (mmdfhdr,df) == EOF)) return NIL;
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* MMDF mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mmdf_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MMDF open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* MMDF unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* MMDF mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int ti,zn,m;
+ unsigned long i,j,k;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ stream->silent = T; /* quell main program new message events */
+ do { /* read MMDF header */
+ if (!(i && ISMMDF (s))){/* see if valid MMDF header */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ /* see if we can back up to a line */
+ if (i && (j > MMDFHDRLEN)) {
+ SETPOS (&bs,j -= MMDFHDRLEN);
+ /* read previous line */
+ s = mmdf_mbxline (stream,&bs,&i);
+ /* kill the error if it looks good */
+ if (i && ISMMDF (s)) tmp[0] = '\0';
+ }
+ if (tmp[0]) {
+ MM_LOG (tmp,ERROR);
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ }
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.special.text.size = i;
+
+ s = mmdf_mbxline (stream,&bs,&i);
+ ti = 0; /* assume not a valid date */
+ zn = 0,t = NIL;
+ if (i) VALID (s,t,ti,zn);
+ if (ti) { /* generate plausible IMAPish date string */
+ /* this is also part of header */
+ elt->private.special.text.size += i;
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6]==':'){ /* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* make date from file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ t = NIL; /* suppress line read */
+ }
+ /* header starts here */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+
+ do { /* look for message body */
+ j = GETPOS (&bs); /* note position before line */
+ if (t) s = t = mmdf_mbxline (stream,&bs,&i);
+ else t = s; /* this line read was suppressed */
+ if (ISMMDF (s)) { /* found terminator in header? */
+ SETPOS (&bs,j); /* oops, back up before line */
+ /* must insert a newline */
+ elt->private.spare.data++;
+ break; /* punt */
+ }
+ /* this line is part of header */
+ elt->private.msg.header.text.size += i;
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = mmdf_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ if (ISMMDF (s)) break;
+ else { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (i) { /* get next header line */
+ /* remember first internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* MMDF read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ /* embedded MMDF header at end of line? */
+ if ((*size > sizeof (MMDFHDRTXT)) &&
+ (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) {
+ SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */
+ *size -= i; /* reduce length of line */
+ ret[*size - 1] = '\n'; /* force newline at end */
+ }
+ return ret;
+}
+
+/* MMDF make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ mmdfhdr,pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ return strlen (hdr);
+}
+
+/* MMDF make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ MMDFFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = mmdf_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = mmdf_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ mmdf_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ mmdf_write (&f,LOCAL->buf,
+ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset :
+ (f.curpos + j + MMDFHDRLEN);
+ mmdf_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ mmdf_write (&f,mmdfhdr,MMDFHDRLEN);
+ }
+ else { /* tie off header and status */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ }
+ }
+
+ mmdf_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend MMDF mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long mmdf_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void mmdf_write (MMDFFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ mmdf_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/unix/mtx.c b/imap/src/osdep/unix/mtx.c
new file mode 100644
index 00000000..8e6f76e8
--- /dev/null
+++ b/imap/src/osdep/unix/mtx.c
@@ -0,0 +1,1371 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mtx_status, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mtx_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mtx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ return ret; /* return success */
+}
+
+/* Mtx Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mtxproto);
+ if (stream->local) fatal ("mtx recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!mtx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return (char *) LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ mtx_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MTX mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void mtx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (mtx_file (file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mtxproto);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mtx_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "INBOX.MTX") : s;
+}
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mx.c b/imap/src/osdep/unix/mx.c
new file mode 100644
index 00000000..45495279
--- /dev/null
+++ b/imap/src/osdep/unix/mx.c
@@ -0,0 +1,1287 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MX mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1996
+ * Last Edited: 6 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* Index file */
+
+#define MXINDEXNAME "/.mxindex"
+#define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME)
+
+
+/* MX I/O stream local data */
+
+typedef struct mx_local {
+ int fd; /* file descriptor of open index */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mx_valid (char *name);
+int mx_isvalid (char *name,char *tmp);
+int mx_namevalid (char *name);
+void *mx_parameters (long function,void *value);
+long mx_dirfmttest (char *name);
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mx_subscribe (MAILSTREAM *stream,char *mailbox);
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mx_create (MAILSTREAM *stream,char *mailbox);
+long mx_delete (MAILSTREAM *stream,char *mailbox);
+long mx_rename (MAILSTREAM *stream,char *old,char *newname);
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name);
+MAILSTREAM *mx_open (MAILSTREAM *stream);
+void mx_close (MAILSTREAM *stream,long options);
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags);
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt);
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mx_ping (MAILSTREAM *stream);
+void mx_check (MAILSTREAM *stream);
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set);
+
+int mx_select (struct direct *name);
+int mx_numsort (const void *d1,const void *d2);
+char *mx_file (char *dst,char *name);
+long mx_lockindex (MAILSTREAM *stream);
+void mx_unlockindex (MAILSTREAM *stream);
+void mx_setdate (char *file,MESSAGECACHE *elt);
+
+
+/* MX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mxdriver = {
+ "mx", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mx_valid, /* mailbox is valid for us */
+ mx_parameters, /* manipulate parameters */
+ mx_scan, /* scan mailboxes */
+ mx_list, /* find mailboxes */
+ mx_lsub, /* find subscribed mailboxes */
+ mx_subscribe, /* subscribe to mailbox */
+ mx_unsubscribe, /* unsubscribe from mailbox */
+ mx_create, /* create mailbox */
+ mx_delete, /* delete mailbox */
+ mx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mx_open, /* open mailbox */
+ mx_close, /* close mailbox */
+ mx_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mx_header, /* fetch message header only */
+ mx_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mx_flag, /* modify flags */
+ mx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mx_ping, /* ping mailbox to see if still alive */
+ mx_check, /* check for new messages */
+ mx_expunge, /* expunge deleted messages */
+ mx_copy, /* copy messages to another mailbox */
+ mx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mxproto = {&mxdriver};
+
+/* MX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mx_isvalid (name,tmp) ? &mxdriver : NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * Returns: T if valid, NIL otherwise with errno holding dir stat error
+ */
+
+int mx_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ errno = NIL; /* zap error */
+ if ((strlen (name) <= NETMAXMBX) && *mx_file (tmp,name) &&
+ !stat (tmp,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mx? */
+ if (!stat (MXINDEX (tmp,name),&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return T;
+ errno = NIL; /* directory but not mx */
+ }
+ else if (!compare_cstring (name,"INBOX")) errno = NIL;
+ return NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mx_namevalid (char *name)
+{
+ char *s = (*name == '/') ? name + 1 : name;
+ while (s && *s) { /* make sure valid name */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break; /* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* MX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mx_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mx_scan_contents;
+ break;
+ }
+ return ret;
+}
+
+
+/* MX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mx_dirfmttest (char *name)
+{
+ int c;
+ /* success if index name or all-numberic */
+ if (strcmp (name,MXINDEXNAME+1))
+ while (c = *name++) if (!isdigit (c)) return NIL;
+ return LONGT;
+}
+
+/* MX scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mx_select,mx_numsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ int mask = umask (0);
+ long ret = NIL;
+ if (!mx_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory */
+ else if (!dummy_create_path (stream,MXINDEX (tmp,mailbox),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else { /* success */
+ /* set index protection */
+ set_mbx_protections (mailbox,tmp);
+ /* tie off directory name */
+ *(s = strrchr (tmp,'/') + 1) = '\0';
+ /* set directory protection */
+ set_mbx_protections (mailbox,tmp);
+ ret = LONGT;
+ }
+ umask (mask); /* restore mask */
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ char *s;
+ char tmp[MAILTMPLEN];
+ if (!mx_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ /* delete index */
+ else if (unlink (MXINDEX (tmp,mailbox)))
+ sprintf (tmp,"Can't delete mailbox %.80s index: %s",
+ mailbox,strerror (errno));
+ else { /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mx_select (d)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MX mail rename mailbox
+ * Accepts: MX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ if (!mx_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mx_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mx_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mx_file (tmp,old); /* build old directory name */
+ mx_file (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (mx_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) return LONGT;
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mx file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mx_select,mx_numsort);
+ i < n; ++i) {
+ if (mx_rename_work (tmp,srcl,tmp1,dstl,names[i]->d_name))
+ lasterror = errno;
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror || mx_rename_work (tmp,srcl,tmp1,dstl,MXINDEXNAME+1))
+ errno = lasterror;
+ else return mx_create (NIL,"INBOX");
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MX rename worker routine
+ * Accepts: source directory name
+ * source directory name length
+ * destination directory name
+ * destination directory name length
+ * name of node to move
+ * Returns: zero if success, non-zero if error
+ */
+
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name)
+{
+ int ret;
+ size_t len = strlen (name);
+ char *s = (char *) fs_get (srcl + len + 2);
+ char *d = (char *) fs_get (dstl + len + 1);
+ sprintf (s,"%s/%s",src,name);
+ sprintf (d,"%s%s",dst,name);
+ ret = rename (s,d);
+ fs_give ((void **) &s);
+ fs_give ((void **) &d);
+ return ret;
+}
+
+/* MX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mx_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mxproto);
+ if (stream->local) fatal ("mx recycle stream");
+ stream->local = fs_get (sizeof (MXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ mx_file (tmp,stream->mailbox);/* get directory name */
+ /* canonicalize mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->fd = -1; /* no index yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mx_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mx_expunge (stream,NIL,NIL);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt);
+}
+
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * message cache element
+ * Returns: name of message file
+ */
+
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ struct tm *tm;
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ /* have size yet? */
+ if (!elt->rfc822_size && !stat (LOCAL->buf,&sbuf)) {
+ /* make plausible IMAPish date string */
+ tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
+ elt->rfc822_size = sbuf.st_size;
+ }
+ return (char *) LOCAL->buf; /* return file name */
+}
+
+/* MX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ int fd;
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ mail_gc (stream,GC_TEXTS);/* just can't keep that much */
+ LOCAL->cachedtexts = 0;
+ }
+ if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return "";
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
+ }
+ /* slurp message */
+ read (fd,LOCAL->buf,elt->rfc822_size);
+ /* tie off file */
+ LOCAL->buf[elt->rfc822_size] = '\0';
+ close (fd); /* flush message file */
+ /* find end of header */
+ if (elt->rfc822_size < 4) i = 0;
+ else for (i = 4; (i < elt->rfc822_size) &&
+ !((LOCAL->buf[i - 4] == '\015') &&
+ (LOCAL->buf[i - 3] == '\012') &&
+ (LOCAL->buf[i - 2] == '\015') &&
+ (LOCAL->buf[i - 1] == '\012')); i++);
+ /* copy header */
+ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i);
+ cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i);
+ /* add to cached size */
+ LOCAL->cachedtexts += elt->rfc822_size;
+ }
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+/* MX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mx_header (stream,msgno,&i,flags);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ /* mark as seen */
+ if (!(flags & FT_PEEK) && mx_lockindex (stream)) {
+ elt->seen = T;
+ mx_unlockindex (stream);
+ MM_FLAGS (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ mx_unlockindex (stream); /* finished with index */
+}
+
+
+/* MX per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ mx_lockindex (stream); /* lock index if not already locked */
+}
+
+/* MX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mx_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r,old;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (stream->mailbox,&sbuf)) return NIL;
+ stream->silent = T; /* don't pass up exists events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mx_select,mx_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ old = stream->uid_last;
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+ stream->nmsgs = nmsgs; /* don't upset mail_uid() */
+
+ /* if INBOX, snarf from system INBOX */
+ if (mx_lockindex (stream) && stream->inbox &&
+ !strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ MM_CRITICAL (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mx_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MX mailbox failed: %.80s",
+ s,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (stream->mailbox,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ }
+ mx_unlockindex (stream); /* done with index */
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mx_check (MAILSTREAM *stream)
+{
+ if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mx_lockindex (stream)) { /* lock the index */
+ MM_CRITICAL (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ MM_LOG (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ if(elt->recent)--recent;/* if recent, note one less recent message */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ MM_NOCRITICAL (stream); /* release critical */
+ mx_unlockindex (stream); /* finished with index */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ MAILSTREAM *astream;
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j,uid,uidv;
+ char *t,tmp[MAILTMPLEN];
+ long ret;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mx_valid (mailbox)) switch (errno) {
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ /* copy the messages */
+ if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append to */
+ else if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open copy mailbox",ERROR);
+ ret = NIL;
+ }
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message copy failed: unable to lock index",ERROR);
+ else {
+
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (ret = ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))
+ >= 0)) {
+ fstat (fd,&sbuf); /* get size of message */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if (t = stream->user_flags[find_rightmost_bit (&j)])
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* open list */
+ strcat (tmp,")"); /* close list */
+ if (ret = mx_append_msg (astream,tmp,elt,&st,dest)) {
+ /* add to source set if needed */
+ if (source) mail_append_set (source,mail_uid (stream,i));
+ /* delete if doing a move */
+ if (options & CP_MOVE) elt->deleted = T;
+ }
+ }
+ }
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (stream);
+ mail_close (astream); /* finished with append stream */
+ }
+ return ret; /* return success */
+}
+
+/* MX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MESSAGECACHE elt;
+ MAILSTREAM *astream;
+ char *flags,*date,tmp[MAILTMPLEN];
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mxproto);
+ /* N.B.: can't use LOCAL->buf for tmp */
+ /* make sure valid mailbox */
+ if (!mx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open append mailbox",ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (astream); /* go critical */
+ /* lock the index */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message append failed: unable to lock index",ERROR);
+ else {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ do {
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = mx_append_msg (astream,flags,date ? &elt : NIL,message,dst)&&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ } while (ret && message);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,astream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ mail_close (astream);
+ return ret;
+}
+
+/* MX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set)
+{
+ char tmp[MAILTMPLEN];
+ int fd;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* make message file name */
+ sprintf (tmp,"%s/%lu",stream->mailbox,++stream->uid_last);
+ if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't create append message: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ while (SIZE (st)) { /* copy the file */
+ if (st->cursize && (write (fd,st->curpos,st->cursize) < 0)) {
+ unlink (tmp); /* delete file */
+ close (fd); /* close the file */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ SETPOS (st,GETPOS (st) + st->cursize);
+ }
+ close (fd); /* close the file */
+ if (elt) mx_setdate (tmp,elt);/* set file date */
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* copy flags */
+ mail_append_set (set,(elt = mail_elt (stream,stream->nmsgs))->private.uid =
+ stream->uid_last);
+ if (f&fSEEN) elt->seen = T;
+ if (f&fDELETED) elt->deleted = T;
+ if (f&fFLAGGED) elt->flagged = T;
+ if (f&fANSWERED) elt->answered = T;
+ if (f&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ return LONGT;
+}
+
+/* Internal routines */
+
+
+/* MX file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mx_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MX file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mx_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MX mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mx_file (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+/* MX read and lock index
+ * Accepts: MAIL stream
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_lockindex (MAILSTREAM *stream)
+{
+ unsigned long uf,sf,uid;
+ int k = 0;
+ unsigned long msgno = 1;
+ struct stat sbuf;
+ char *s,*t,*idx,tmp[2*MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
+ (LOCAL->fd = open (strcat (strcpy (tmp,stream->mailbox),MXINDEXNAME),
+ O_RDWR|O_CREAT,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
+ (*bn) (BLOCK_NONE,NIL);
+ fstat (LOCAL->fd,&sbuf); /* get size of index */
+ /* slurp index */
+ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ idx[sbuf.st_size] = '\0'; /* tie off index */
+ /* parse index */
+ if (sbuf.st_size) while (s && *s) switch (*s) {
+ case 'V': /* UID validity record */
+ stream->uid_validity = strtoul (s+1,&s,16);
+ break;
+ case 'L': /* UID last record */
+ stream->uid_last = strtoul (s+1,&s,16);
+ break;
+ case 'K': /* keyword */
+ /* find end of keyword */
+ if (s = strchr (t = ++s,'\n')) {
+ *s++ = '\0'; /* tie off keyword */
+ /* copy keyword */
+ if ((k < NUSERFLAGS) && !stream->user_flags[k] &&
+ (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t);
+ k++; /* one more keyword */
+ }
+ break;
+
+ case 'M': /* message status record */
+ uid = strtoul (s+1,&s,16);/* get UID for this message */
+ if (*s == ';') { /* get user flags */
+ uf = strtoul (s+1,&s,16);
+ if (*s == '.') { /* get system flags */
+ sf = strtoul (s+1,&s,16);
+ while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid))
+ msgno++; /* find message number for this UID */
+ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) {
+ (elt = mail_elt (stream,msgno))->valid = T;
+ elt->user_flags=uf; /* set user and system flags in elt */
+ if (sf & fSEEN) elt->seen = T;
+ if (sf & fDELETED) elt->deleted = T;
+ if (sf & fFLAGGED) elt->flagged = T;
+ if (sf & fANSWERED) elt->answered = T;
+ if (sf & fDRAFT) elt->draft = T;
+ }
+ break;
+ }
+ }
+ default: /* bad news */
+ sprintf (tmp,"Error in index: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ *s = NIL; /* ignore remainder of index */
+ }
+ else { /* new index */
+ stream->uid_validity = time (0);
+ user_flags (stream); /* init stream with default user flags */
+ }
+ fs_give ((void **) &idx); /* flush index */
+ }
+ return (LOCAL->fd >= 0) ? T : NIL;
+}
+
+/* MX write and unlock index
+ * Accepts: MAIL stream
+ */
+
+#define MXIXBUFLEN 2048
+
+void mx_unlockindex (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ off_t size = 0;
+ char *s,tmp[MXIXBUFLEN + 64];
+ MESSAGECACHE *elt;
+ if (LOCAL->fd >= 0) {
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* write header */
+ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]);
+ /* write messages */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ /* filled buffer? */
+ if (((s += strlen (s)) - tmp) > MXIXBUFLEN) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ *(s = tmp) = '\0'; /* dump out and restart buffer */
+ }
+ elt = mail_elt (stream,i);
+ sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ }
+ /* write tail end of buffer */
+ if ((s += strlen (s)) != tmp) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ }
+ ftruncate (LOCAL->fd,size);
+ flock (LOCAL->fd,LOCK_UN); /* unlock the index */
+ close (LOCAL->fd); /* finished with file */
+ LOCAL->fd = -1; /* no index now */
+ }
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mx_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/unix/news.c b/imap/src/osdep/unix/news.c
new file mode 100644
index 00000000..4cf5bb70
--- /dev/null
+++ b/imap/src/osdep/unix/news.c
@@ -0,0 +1,738 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: News routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 September 1991
+ * Last Edited: 30 January 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "newsrc.h"
+#include "fdstring.h"
+
+
+/* news_load_message() flags */
+
+#define NLM_HEADER 0x1 /* load message text */
+#define NLM_TEXT 0x2 /* load message text */
+
+/* NEWS I/O stream local data */
+
+typedef struct news_local {
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ char *dir; /* spool directory name */
+ char *name; /* local mailbox name */
+ unsigned char buf[CHUNKSIZE]; /* scratch buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+} NEWSLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NEWSLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *news_valid (char *name);
+DRIVER *news_isvalid (char *name,char *mbx);
+void *news_parameters (long function,void *value);
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void news_list (MAILSTREAM *stream,char *ref,char *pat);
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long news_canonicalize (char *ref,char *pat,char *pattern);
+long news_subscribe (MAILSTREAM *stream,char *mailbox);
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long news_create (MAILSTREAM *stream,char *mailbox);
+long news_delete (MAILSTREAM *stream,char *mailbox);
+long news_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *news_open (MAILSTREAM *stream);
+int news_select (struct direct *name);
+int news_numsort (const void *d1,const void *d2);
+void news_close (MAILSTREAM *stream,long options);
+void news_fast (MAILSTREAM *stream,char *sequence,long flags);
+void news_flags (MAILSTREAM *stream,char *sequence,long flags);
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long news_ping (MAILSTREAM *stream);
+void news_check (MAILSTREAM *stream);
+long news_expunge (MAILSTREAM *stream,char *sequence,long options);
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* News routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER newsdriver = {
+ "news", /* driver name */
+ /* driver flags */
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ news_valid, /* mailbox is valid for us */
+ news_parameters, /* manipulate parameters */
+ news_scan, /* scan mailboxes */
+ news_list, /* find mailboxes */
+ news_lsub, /* find subscribed mailboxes */
+ news_subscribe, /* subscribe to mailbox */
+ news_unsubscribe, /* unsubscribe from mailbox */
+ news_create, /* create mailbox */
+ news_delete, /* delete mailbox */
+ news_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ news_open, /* open mailbox */
+ news_close, /* close mailbox */
+ news_fast, /* fetch message "fast" attributes */
+ news_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ news_header, /* fetch message header */
+ news_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ news_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ news_ping, /* ping mailbox to see if still alive */
+ news_check, /* check for new messages */
+ news_expunge, /* expunge deleted messages */
+ news_copy, /* copy messages to another mailbox */
+ news_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM newsproto = {&newsdriver};
+
+/* News validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *news_valid (char *name)
+{
+ int fd;
+ char *s,*t,*u;
+ struct stat sbuf;
+ if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
+ (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
+ !strchr (name,'/') &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
+ NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get size of active file */
+ /* slurp in active file */
+ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
+ s[sbuf.st_size] = '\0'; /* tie off file */
+ close (fd); /* flush file */
+ while (*t && (u = strchr (t,' '))) {
+ *u++ = '\0'; /* tie off at end of name */
+ if (!strcmp (name+6,t)) {
+ fs_give ((void **) &s); /* flush data */
+ return &newsdriver;
+ }
+ t = 1 + strchr (u,'\n'); /* next line */
+ }
+ fs_give ((void **) &s); /* flush data */
+ }
+ return NIL; /* return status */
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *news_parameters (long function,void *value)
+{
+ return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
+}
+
+
+/* News scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (news_canonicalize (ref,pat,tmp))
+ mm_log ("Scan not valid for news mailboxes",ERROR);
+}
+
+/* News find list of newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int fd;
+ int i;
+ char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
+ struct stat sbuf;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (news_canonicalize (ref,"*",pattern)) {
+ /* tie off name at root */
+ if (s = strchr (pattern,'.')) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,LATT_NOSELECT);
+ }
+ }
+ else if (news_canonicalize (ref,pat,pattern) &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
+ O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file size and read data */
+ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ close (fd); /* close file */
+ s[sbuf.st_size] = '\0'; /* tie off string */
+ strcpy (name,"#news."); /* write initial prefix */
+ i = strlen (pattern); /* length of pattern */
+ if (pattern[--i] != '%') i = 0;
+ if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
+ *u = '\0'; /* tie off at end of name */
+ strcpy (name + 6,t); /* make full form of name */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else if (i && (u = strchr (name + i,'.'))) {
+ *u = '\0'; /* tie off at delimiter, see if matches */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ } while (t = strtok_r (NIL,"\n",&r));
+ fs_give ((void **) &s);
+ }
+}
+
+/* News find list of subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char pattern[MAILTMPLEN];
+ /* return data from newsrc */
+ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
+}
+
+
+/* News canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long news_canonicalize (char *ref,char *pat,char *pattern)
+{
+ unsigned long i;
+ char *s;
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
+ (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
+ !strchr (pattern,'/')) { /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* News subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
+}
+
+
+/* NEWS unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
+}
+
+/* News create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long news_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long news_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long news_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for News */
+}
+
+/* News open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *news_open (MAILSTREAM *stream)
+{
+ long i,nmsgs;
+ char *s,tmp[MAILTMPLEN];
+ struct direct **names = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &newsproto;
+ if (stream->local) fatal ("news recycle stream");
+ /* build directory name */
+ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
+ stream->mailbox + 6);
+ while (s = strchr (s,'.')) *s = '/';
+ /* scan directory */
+ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ stream->local = fs_get (sizeof (NEWSLOCAL));
+ LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->name = cpystr (stream->mailbox + 6);
+ for (i = 0; i < nmsgs; ++i) {
+ stream->uid_last = mail_elt (stream,i+1)->private.uid =
+ atoi (names[i]->d_name);
+ fs_give ((void **) &names[i]);
+ }
+ s = (void *) names; /* stupid language */
+ fs_give ((void **) &s); /* free directory */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (LOCAL->name,stream));
+ /* notify if empty newsgroup */
+ if (!(stream->nmsgs || stream->silent)) {
+ sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ else mm_log ("Unable to scan newsgroup spool directory",ERROR);
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* News file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int news_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* News file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int news_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* News close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void news_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ news_check (stream); /* dump final checkpoint */
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* News fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
+}
+
+
+/* News fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* News load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+
+ /* need to load cache with message data? */
+ if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+ if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i <= elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.msg.header.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i <= elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* News fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ news_load_message (stream,msgno,NLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* News fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ news_load_message (stream,msgno,NLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* News per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+
+/* News ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long news_ping (MAILSTREAM *stream)
+{
+ return T; /* always alive */
+}
+
+
+/* News check mailbox
+ * Accepts: MAIL stream
+ */
+
+void news_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* News expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long news_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* News copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for News",ERROR);
+ return NIL;
+}
+
+
+/* News append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for news",ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/nl_unix.c b/imap/src/osdep/unix/nl_unix.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/unix/nl_unix.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/unix/opendir.c b/imap/src/osdep/unix/opendir.c
new file mode 100644
index 00000000..445fd148
--- /dev/null
+++ b/imap/src/osdep/unix/opendir.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Read directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 16 December 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for BSD opendir() call
+ * Accepts: directory name
+ * Returns: directory structure pointer
+ */
+
+DIR *opendir (char *name)
+{
+ DIR *d = NIL;
+ struct stat sbuf;
+ int fd = open (name,O_RDONLY,NIL);
+ errno = ENOTDIR; /* default error is bogus directory */
+ if ((fd >= 0) && !(fstat (fd,&sbuf)) && ((sbuf.st_mode&S_IFMT) == S_IFDIR)) {
+ d = (DIR *) fs_get (sizeof (DIR));
+ /* initialize structure */
+ d->dd_loc = 0;
+ read (fd,d->dd_buf = (char *) fs_get (sbuf.st_size),
+ d->dd_size = sbuf.st_size);
+ }
+ else if (d) fs_give ((void **) &d);
+ if (fd >= 0) close (fd);
+ return d;
+}
+
+
+/* Emulator for BSD closedir() call
+ * Accepts: directory structure pointer
+ */
+
+int closedir (DIR *d)
+{
+ /* free storage */
+ fs_give ((void **) &(d->dd_buf));
+ fs_give ((void **) &d);
+ return NIL; /* return */
+}
+
+
+/* Emulator for BSD readdir() call
+ * Accepts: directory structure pointer
+ */
+
+struct direct *readdir (DIR *d)
+{
+ /* loop through directory */
+ while (d->dd_loc < d->dd_size) {
+ struct direct *dp = (struct direct *) (d->dd_buf + d->dd_loc);
+ d->dd_loc += sizeof (struct direct);
+ if (dp->d_ino) return dp; /* if have a good entry return it */
+ }
+ return NIL; /* all done */
+}
diff --git a/imap/src/osdep/unix/os_a32.c b/imap/src/osdep/unix/os_a32.c
new file mode 100644
index 00000000..513e92c7
--- /dev/null
+++ b/imap/src/osdep/unix/os_a32.c
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 3.2 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+
+char *crypt (char *key,char *salt);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a32.h b/imap/src/osdep/unix/os_a32.h
new file mode 100644
index 00000000..40dd6fd4
--- /dev/null
+++ b/imap/src/osdep/unix/os_a32.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_a41.c b/imap/src/osdep/unix/os_a41.c
new file mode 100644
index 00000000..ecaff582
--- /dev/null
+++ b/imap/src/osdep/unix/os_a41.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 4.1 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+#include <stddef.h> /* needed for authenticate() */
+
+int authenticate (char *UserName,char *Response,int *Reenter,char **Message);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a41.h b/imap/src/osdep/unix/os_a41.h
new file mode 100644
index 00000000..40dd6fd4
--- /dev/null
+++ b/imap/src/osdep/unix/os_a41.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_a52.c b/imap/src/osdep/unix/os_a52.c
new file mode 100644
index 00000000..fed6af78
--- /dev/null
+++ b/imap/src/osdep/unix/os_a52.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 4.1 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+#include <stddef.h> /* needed for authenticate() */
+
+int authenticate (char *UserName,char *Response,int *Reenter,char **Message);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a52.h b/imap/src/osdep/unix/os_a52.h
new file mode 100644
index 00000000..a8261746
--- /dev/null
+++ b/imap/src/osdep/unix/os_a52.h
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_aix.c b/imap/src/osdep/unix/os_aix.c
new file mode 100644
index 00000000..c38e5cf9
--- /dev/null
+++ b/imap/src/osdep/unix/os_aix.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+extern long timezone;
+extern int daylight;
+extern char *tzname[2];
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_aix.h b/imap/src/osdep/unix/os_aix.h
new file mode 100644
index 00000000..6c2d4340
--- /dev/null
+++ b/imap/src/osdep/unix/os_aix.h
@@ -0,0 +1,47 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define direct dirent
+
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_aos.c b/imap/src/osdep/unix/os_aos.c
new file mode 100644
index 00000000..a916f47b
--- /dev/null
+++ b/imap/src/osdep/unix/os_aos.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_aos.h b/imap/src/osdep/unix/os_aos.h
new file mode 100644
index 00000000..b604a475
--- /dev/null
+++ b/imap/src/osdep/unix/os_aos.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_art.c b/imap/src/osdep/unix/os_art.c
new file mode 100644
index 00000000..8fb78028
--- /dev/null
+++ b/imap/src/osdep/unix/os_art.c
@@ -0,0 +1,86 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX/RT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno;
+#include <pwd.h>
+#include <sys/socket.h>
+#include <time.h>
+#define KERNEL
+#include <sys/time.h>
+#undef KERNEL
+#include "misc.h"
+
+#define DIR_SIZE(d) sizeof (DIR)
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define NBBY 8 /* number of bits in a byte */
+#define FD_SETSIZE 256
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+ /* bits per mask */
+#define howmany(x, y) (((x)+((y)-1))/(y))
+
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "memmove2.c"
+#include "strerror.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_art.h b/imap/src/osdep/unix/os_art.h
new file mode 100644
index 00000000..9bfd6f86
--- /dev/null
+++ b/imap/src/osdep/unix/os_art.h
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX/RT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#define direct dirent
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names between BSD and SVR4 */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define random lrand48
+
+#define SIGSTOP SIGQUIT
+
+
+/* For setitimer() emulation */
+
+#define ITIMER_REAL 0
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+int openlog (ident,logopt,facility);
+int syslog (priority,message,parameters ...);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_asv.c b/imap/src/osdep/unix/os_asv.c
new file mode 100644
index 00000000..bf943b3e
--- /dev/null
+++ b/imap/src/osdep/unix/os_asv.c
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Altos System V version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <sys/socket.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define pid_t short /* may not be known on all ASV systems */
+
+#include "strstr.c"
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "strerror.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "strtoul.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "memmove.c"
+#include "fsync.c"
diff --git a/imap/src/osdep/unix/os_asv.h b/imap/src/osdep/unix/os_asv.h
new file mode 100644
index 00000000..f92f210b
--- /dev/null
+++ b/imap/src/osdep/unix/os_asv.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Altos System V version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define ftruncate chsize
+#define random lrand48
+
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_aux.c b/imap/src/osdep/unix/os_aux.c
new file mode 100644
index 00000000..ec5f666b
--- /dev/null
+++ b/imap/src/osdep/unix/os_aux.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- A/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "strerror.c"
+#include "strtoul.c"
+#include "strpbrk.c" /* the A/UX version is bogus! */
+#include "memmove.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_aux.h b/imap/src/osdep/unix/os_aux.h
new file mode 100644
index 00000000..53ec6cc5
--- /dev/null
+++ b/imap/src/osdep/unix/os_aux.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- A/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+
+extern int errno;
+
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *memmove (void *s,void *ct,size_t n);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_bsd.c b/imap/src/osdep/unix/os_bsd.c
new file mode 100644
index 00000000..a916f47b
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsd.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsd.h b/imap/src/osdep/unix/os_bsd.h
new file mode 100644
index 00000000..851434af
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsd.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <machine/endian.h> /* needed for htons() prototypes */
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_bsf.c b/imap/src/osdep/unix/os_bsf.c
new file mode 100644
index 00000000..afe26e23
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsf.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsf.h b/imap/src/osdep/unix/os_bsf.h
new file mode 100644
index 00000000..af18d567
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsf.h
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- FreeBSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define direct dirent
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_bsi.c b/imap/src/osdep/unix/os_bsi.c
new file mode 100644
index 00000000..afe26e23
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsi.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsi.h b/imap/src/osdep/unix/os_bsi.h
new file mode 100644
index 00000000..15f371a3
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsi.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_cvx.c b/imap/src/osdep/unix/os_cvx.c
new file mode 100644
index 00000000..10f01904
--- /dev/null
+++ b/imap/src/osdep/unix/os_cvx.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Convex version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_cvx.h b/imap/src/osdep/unix/os_cvx.h
new file mode 100644
index 00000000..cdb0e92f
--- /dev/null
+++ b/imap/src/osdep/unix/os_cvx.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Convex version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <sys/timeb.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+void *malloc (size_t byteSize);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_cyg.c b/imap/src/osdep/unix/os_cyg.c
new file mode 100644
index 00000000..c9cd2f70
--- /dev/null
+++ b/imap/src/osdep/unix/os_cyg.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Cygwin version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <crypt.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_nul.c"
+#include "flockcyg.c"
+#include "gethstid.c"
+
+
+/* Emulator for geteuid() call
+ * Returns: effective UID
+ */
+
+#undef geteuid
+
+uid_t Geteuid (void)
+{
+ uid_t ret = geteuid ();
+ return (ret == SYSTEMUID) ? 0 : ret;
+}
diff --git a/imap/src/osdep/unix/os_cyg.h b/imap/src/osdep/unix/os_cyg.h
new file mode 100644
index 00000000..061db332
--- /dev/null
+++ b/imap/src/osdep/unix/os_cyg.h
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Cygwin version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <time.h>
+#include <sys/time.h>
+
+#define direct dirent
+
+
+#define CYGKLUDGEOFFSET 1 /* don't write 1st byte of shared-lock files */
+
+/* Cygwin gets this wrong */
+
+#define setpgrp setpgid
+
+#define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */
+#define geteuid Geteuid
+uid_t Geteuid (void);
+
+/* Now Cygwin has reportedly joined this madness. Use ifndef in case it shares
+ the SVR4 <sys/file.h> silliness too */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flockcyg.h"
diff --git a/imap/src/osdep/unix/os_d-g.c b/imap/src/osdep/unix/os_d-g.c
new file mode 100644
index 00000000..b34662f7
--- /dev/null
+++ b/imap/src/osdep/unix/os_d-g.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- D-G version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_d-g.h b/imap/src/osdep/unix/os_d-g.h
new file mode 100644
index 00000000..3d308038
--- /dev/null
+++ b/imap/src/osdep/unix/os_d-g.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- D-G version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/dir.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#define _USEC_UTIME_FLAVOR /* break it for compatibility with */
+#include <utime.h> /* the incompatible past */
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* D-G gets this wrong */
+
+#define setpgrp setpgrp2
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_do4.c b/imap/src/osdep/unix/os_do4.c
new file mode 100644
index 00000000..186fd94e
--- /dev/null
+++ b/imap/src/osdep/unix/os_do4.c
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_do4.h b/imap/src/osdep/unix/os_do4.h
new file mode 100644
index 00000000..196061c9
--- /dev/null
+++ b/imap/src/osdep/unix/os_do4.h
@@ -0,0 +1,47 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#ifndef htons
+#include <netinet/in.h> /* needed for htons() prototypes */
+#endif
+
+extern int daylight; /* local timzone uses daylight savings time */
+extern long altzone; /* seconds west of UTC during daylight time */
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_drs.c b/imap/src/osdep/unix/os_drs.c
new file mode 100644
index 00000000..74386af9
--- /dev/null
+++ b/imap/src/osdep/unix/os_drs.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ICL DRS/NX
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "scandir.c"
diff --git a/imap/src/osdep/unix/os_drs.h b/imap/src/osdep/unix/os_drs.h
new file mode 100644
index 00000000..d90755ee
--- /dev/null
+++ b/imap/src/osdep/unix/os_drs.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ICL DRS/NX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define random rand
+#define direct dirent
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_dyn.c b/imap/src/osdep/unix/os_dyn.c
new file mode 100644
index 00000000..98a1a548
--- /dev/null
+++ b/imap/src/osdep/unix/os_dyn.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Dynix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strpbrk.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "strtok.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_dyn.h b/imap/src/osdep/unix/os_dyn.h
new file mode 100644
index 00000000..e5426498
--- /dev/null
+++ b/imap/src/osdep/unix/os_dyn.h
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Dynix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+typedef unsigned long size_t;
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+char *strstr (char *cs,char *ct);
+char *strpbrk (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+int errno;
+
+#define memcpy memmove
+#define strchr index
+#define strrchr rindex
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_hpp.c b/imap/src/osdep/unix/os_hpp.c
new file mode 100644
index 00000000..39794973
--- /dev/null
+++ b/imap/src/osdep/unix/os_hpp.c
@@ -0,0 +1,77 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+extern char *sys_errlist[];
+extern int sys_nerr;
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "tz_sv4.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
+
+/* Emulator for BSD gethostid() call
+ * Returns: a unique identifier for the system.
+ * Even though HP/UX has an undocumented gethostid() system call,
+ * it does not work (at least for non-privileged users).
+ */
+
+long gethostid (void)
+{
+ struct utsname udata;
+ return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber);
+}
diff --git a/imap/src/osdep/unix/os_hpp.h b/imap/src/osdep/unix/os_hpp.h
new file mode 100644
index 00000000..d51dba53
--- /dev/null
+++ b/imap/src/osdep/unix/os_hpp.h
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define direct dirent
+#define random lrand48
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_isc.c b/imap/src/osdep/unix/os_isc.c
new file mode 100644
index 00000000..1269e69b
--- /dev/null
+++ b/imap/src/osdep/unix/os_isc.c
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ISC version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <net/errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/socket.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define pid_t short /* may not be known on all ISC systems */
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "strerror.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "fsync.c"
diff --git a/imap/src/osdep/unix/os_isc.h b/imap/src/osdep/unix/os_isc.h
new file mode 100644
index 00000000..1e7c22eb
--- /dev/null
+++ b/imap/src/osdep/unix/os_isc.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ISC version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/bsdtypes.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define ftruncate chsize
+#define random lrand48
+
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_lnx.c b/imap/src/osdep/unix/os_lnx.c
new file mode 100644
index 00000000..03fd17d9
--- /dev/null
+++ b/imap/src/osdep/unix/os_lnx.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- old Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocklnx.c"
diff --git a/imap/src/osdep/unix/os_lnx.h b/imap/src/osdep/unix/os_lnx.h
new file mode 100644
index 00000000..b5f39ffa
--- /dev/null
+++ b/imap/src/osdep/unix/os_lnx.h
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ *** These lines are claimed to be necessary to build on Debian Linux on an
+ *** Alpha.
+ */
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 1
+#endif /* _XOPEN_SOURCE */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE 1
+#endif /* _BSD_SOURCE */
+
+/* end Debian Linux on Alpha strangeness */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Linux gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#define flock safe_flock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_lyn.c b/imap/src/osdep/unix/os_lyn.c
new file mode 100644
index 00000000..49871251
--- /dev/null
+++ b/imap/src/osdep/unix/os_lyn.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- LynxOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_lyn.h b/imap/src/osdep/unix/os_lyn.h
new file mode 100644
index 00000000..bba4a3d6
--- /dev/null
+++ b/imap/src/osdep/unix/os_lyn.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- LynxOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+#define gethostid clock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_mct.c b/imap/src/osdep/unix/os_mct.c
new file mode 100644
index 00000000..cb62de50
--- /dev/null
+++ b/imap/src/osdep/unix/os_mct.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MachTen version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 7 December 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_mct.h b/imap/src/osdep/unix/os_mct.h
new file mode 100644
index 00000000..f4b98d5e
--- /dev/null
+++ b/imap/src/osdep/unix/os_mct.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MachTen version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 7 December 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+#define unix 1
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_mnt.c b/imap/src/osdep/unix/os_mnt.c
new file mode 100644
index 00000000..f19a487b
--- /dev/null
+++ b/imap/src/osdep/unix/os_mnt.c
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mint version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_mnt.h b/imap/src/osdep/unix/os_mnt.h
new file mode 100644
index 00000000..4519f8b7
--- /dev/null
+++ b/imap/src/osdep/unix/os_mnt.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mint version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <time.h>
+#include <portlib.h>
+
+#define EAGAIN EWOULDBLOCK
+#define FNDELAY O_NDELAY
+
+/* MiNT gets this wrong */
+
+#define setpgrp setpgid
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_nto.c b/imap/src/osdep/unix/os_nto.c
new file mode 100644
index 00000000..36c45b42
--- /dev/null
+++ b/imap/src/osdep/unix/os_nto.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX Neutrino RTP version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "flocksim.c"
+#include "utime.c"
+
+/* QNX local readdir()
+ * Accepts: directory structure
+ * Returns: direct struct or NIL if failed
+ */
+
+#undef readdir
+
+struct direct *Readdir (DIR *dirp)
+{
+ static struct direct dc;
+ struct dirent *de = readdir (dirp);
+ if (!de) return NIL; /* end of data */
+ dc.d_fileno = 0; /* could get from de->stat.st_ino */
+ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name));
+ dc.d_reclen = sizeof (dc);
+ return &dc;
+}
diff --git a/imap/src/osdep/unix/os_nto.h b/imap/src/osdep/unix/os_nto.h
new file mode 100644
index 00000000..12ea809a
--- /dev/null
+++ b/imap/src/osdep/unix/os_nto.h
@@ -0,0 +1,75 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX Neutrino RTP version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include <time.h>
+#include <utime.h>
+
+
+/* QNX gets these wrong */
+
+#define setpgrp setpgid
+#define readdir Readdir
+#define FNDELAY O_NONBLOCK
+#define d_ino d_fileno
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+struct direct *Readdir (DIR *dirp);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_nxt.c b/imap/src/osdep/unix/os_nxt.c
new file mode 100644
index 00000000..a3b749f5
--- /dev/null
+++ b/imap/src/osdep/unix/os_nxt.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NeXT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
+#include "strtok.c"
diff --git a/imap/src/osdep/unix/os_nxt.h b/imap/src/osdep/unix/os_nxt.h
new file mode 100644
index 00000000..2af89168
--- /dev/null
+++ b/imap/src/osdep/unix/os_nxt.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NeXT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 January 2007
+ */
+
+#include <libc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+/* Use ours instead of theirs */
+
+#define strtok STRTOK
+#define strtok_r STRTOK_R
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_os4.c b/imap/src/osdep/unix/os_os4.c
new file mode 100644
index 00000000..42d90404
--- /dev/null
+++ b/imap/src/osdep/unix/os_os4.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sia.h>
+#include <siad.h>
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "gr_waitp.c"
+#include "tcp_unix.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_os4.h b/imap/src/osdep/unix/os_os4.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_os4.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_osf.c b/imap/src/osdep/unix/os_osf.c
new file mode 100644
index 00000000..560a3b59
--- /dev/null
+++ b/imap/src/osdep/unix/os_osf.c
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "gr_waitp.c"
+#include "tcp_unix.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_osf.h b/imap/src/osdep/unix/os_osf.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_osf.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_osx.c b/imap/src/osdep/unix/os_osx.c
new file mode 100644
index 00000000..25eb1a7d
--- /dev/null
+++ b/imap/src/osdep/unix/os_osx.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mac OS X version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "getspnam.c"
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_osx.h b/imap/src/osdep/unix/os_osx.h
new file mode 100644
index 00000000..3480e14a
--- /dev/null
+++ b/imap/src/osdep/unix/os_osx.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mac OS X version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 26 October 2007
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Mac OS X gets this wrong as of Leopard */
+
+#define setpgrp setpgid
+
+
+#define unix 1
+
+/* Mac OS X security framework also has checkpw, and this causes
+ * multiple-definition problems when building Alpine.
+ */
+
+#define checkpw Checkpw
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_ptx.c b/imap/src/osdep/unix/os_ptx.c
new file mode 100644
index 00000000..d7f84d7d
--- /dev/null
+++ b/imap/src/osdep/unix/os_ptx.c
@@ -0,0 +1,115 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- PTX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#define env_init ENV_INIT
+#include "env_unix.c"
+#undef env_init
+#define getpeername Getpeername
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "utime.c"
+
+/* Jacket around env_init() to work around PTX inetd braindamage */
+
+static char may_need_server_init = T;
+
+long env_init (char *user,char *home)
+{
+ if (may_need_server_init) { /* maybe need to do server init cruft? */
+ may_need_server_init = NIL; /* not any more we don't */
+ if (!getuid ()) { /* if root, we're most likely a server */
+ t_sync (0); /* PTX inetd is stupid, stupid, stupid */
+ ioctl (0,I_PUSH,"tirdwr");/* it needs this cruft, else servers won't */
+ dup2 (0,1); /* work. How obnoxious!!! */
+ }
+ }
+ ENV_INIT (user,home); /* call the real routine */
+}
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ struct sockaddr_in sin;
+ int inet = t_open (TLI_TCP, O_RDWR, 0);
+ if (inet < 0) return 0;
+ getmyinaddr (inet,&sin,sizeof (sin));
+ close (inet);
+ return sin.sin_addr.s_addr;
+}
+
+
+/* Replaced version of getpeername() that jackets into getpeerinaddr()
+ * Accepts: file descriptor
+ * pointer to Internet socket addr
+ * length
+ * Returns: zero if success, data in socket addr
+ */
+
+int Getpeername (int s,struct sockaddr *name,int *namelen)
+{
+ return getpeerinaddr (s,(struct sockaddr_in *) name,*namelen);
+}
diff --git a/imap/src/osdep/unix/os_ptx.h b/imap/src/osdep/unix/os_ptx.h
new file mode 100644
index 00000000..db772597
--- /dev/null
+++ b/imap/src/osdep/unix/os_ptx.h
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- PTX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <stropts.h> /* needed in daemons */
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+#define random lrand48
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+long ENV_INIT (char *user,char *home);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_pyr.c b/imap/src/osdep/unix/os_pyr.c
new file mode 100644
index 00000000..69c8e7e4
--- /dev/null
+++ b/imap/src/osdep/unix/os_pyr.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "memset.c"
+#include "strerror.c"
+#include "strpbrk.c"
+#include "strstr.c"
+#include "strtok.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_pyr.h b/imap/src/osdep/unix/os_pyr.h
new file mode 100644
index 00000000..c7c7e57a
--- /dev/null
+++ b/imap/src/osdep/unix/os_pyr.h
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+char *strstr (char *cs,char *ct);
+char *strpbrk (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+int errno;
+
+#define memcpy memmove
+#define strchr index
+#define strrchr rindex
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_qnx.c b/imap/src/osdep/unix/os_qnx.c
new file mode 100644
index 00000000..b1382519
--- /dev/null
+++ b/imap/src/osdep/unix/os_qnx.c
@@ -0,0 +1,77 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 20 December 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+#define DIR_SIZE(d) d->d_reclen
+
+extern char *crypt (const char *pw, const char *salt);
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "scandir.c"
+
+/* QNX local readdir()
+ * Accepts: directory structure
+ * Returns: direct struct or NIL if failed
+ */
+
+#undef readdir
+
+struct direct *Readdir (DIR *dirp)
+{
+ static struct direct dc;
+ struct dirent *de = readdir (dirp);
+ if (!de) return NIL; /* end of data */
+ dc.d_fileno = 0; /* could get from de->stat.st_ino */
+ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name));
+ dc.d_reclen = sizeof (dc);
+ return &dc;
+}
diff --git a/imap/src/osdep/unix/os_qnx.h b/imap/src/osdep/unix/os_qnx.h
new file mode 100644
index 00000000..66ea5e62
--- /dev/null
+++ b/imap/src/osdep/unix/os_qnx.h
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include </usr/include/unix.h>
+#include <time.h>
+#include <utime.h>
+
+
+/* QNX gets these wrong */
+
+#define setpgrp setpgid
+#define readdir Readdir
+#define FNDELAY O_NONBLOCK
+#define d_ino d_fileno
+
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid(void);
+struct direct *Readdir (DIR *dirp);
+
+extern long random (void);
diff --git a/imap/src/osdep/unix/os_s40.c b/imap/src/osdep/unix/os_s40.c
new file mode 100644
index 00000000..3dc2fe63
--- /dev/null
+++ b/imap/src/osdep/unix/os_s40.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS 4.0 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "memmove.c"
+#include "strerror.c"
+#define strstr Strstr /* override SUN's broken version */
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_s40.h b/imap/src/osdep/unix/os_s40.h
new file mode 100644
index 00000000..a67d923f
--- /dev/null
+++ b/imap/src/osdep/unix/os_s40.h
@@ -0,0 +1,33 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS 4.0 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "os_sun.h" /* now use regular SUN-OS file */
diff --git a/imap/src/osdep/unix/os_sc5.c b/imap/src/osdep/unix/os_sc5.c
new file mode 100644
index 00000000..11ac69de
--- /dev/null
+++ b/imap/src/osdep/unix/os_sc5.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include <sys/time.h> /* must be before osdep.h */
+#include "mail.h"
+#include <stdio.h>
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include "misc.h"
+#define SecureWare /* protected subsystem */
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#include <pwd.h>
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "rename.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sc5.h b/imap/src/osdep/unix/os_sc5.h
new file mode 100644
index 00000000..f81e4961
--- /dev/null
+++ b/imap/src/osdep/unix/os_sc5.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* SCO gets this wrong */
+
+#define setpgrp Setpgrp
+int Setpgrp (int pid,int gid);
+
+#define rename Rename
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sco.c b/imap/src/osdep/unix/os_sco.c
new file mode 100644
index 00000000..fb082420
--- /dev/null
+++ b/imap/src/osdep/unix/os_sco.c
@@ -0,0 +1,66 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include <sys/time.h> /* must be before osdep.h */
+#include "mail.h"
+#include <stdio.h>
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include "misc.h"
+#define SecureWare /* protected subsystem */
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#include <pwd.h>
+
+char *bigcrypt (char *key,char *salt);
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "fsync.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "rename.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sco.h b/imap/src/osdep/unix/os_sco.h
new file mode 100644
index 00000000..6bcd5376
--- /dev/null
+++ b/imap/src/osdep/unix/os_sco.h
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* SCO gets this wrong */
+
+#define setpgrp Setpgrp
+int Setpgrp (int pid,int gid);
+
+#define rename Rename
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#define ftruncate chsize
+#define random rand
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sgi.c b/imap/src/osdep/unix/os_sgi.c
new file mode 100644
index 00000000..3e5eb550
--- /dev/null
+++ b/imap/src/osdep/unix/os_sgi.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SGI version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <bstring.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
+#include "flocksim.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sgi.h b/imap/src/osdep/unix/os_sgi.h
new file mode 100644
index 00000000..22664858
--- /dev/null
+++ b/imap/src/osdep/unix/os_sgi.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SGI version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <utime.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define direct dirent
+
+#define fatal cclient_fatal
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_shp.c b/imap/src/osdep/unix/os_shp.c
new file mode 100644
index 00000000..eb7e4e80
--- /dev/null
+++ b/imap/src/osdep/unix/os_shp.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+extern char *sys_errlist[];
+extern int sys_nerr;
+#include <pwd.h>
+#include <hpsecurity.h>
+#include <prot.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "tz_sv4.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
+
+/* Emulator for BSD gethostid() call
+ * Returns: a unique identifier for the system.
+ * Even though HP/UX has an undocumented gethostid() system call,
+ * it does not work (at least for non-privileged users).
+ */
+
+long gethostid (void)
+{
+ struct utsname udata;
+ return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber);
+}
diff --git a/imap/src/osdep/unix/os_shp.h b/imap/src/osdep/unix/os_shp.h
new file mode 100644
index 00000000..d51dba53
--- /dev/null
+++ b/imap/src/osdep/unix/os_shp.h
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define direct dirent
+#define random lrand48
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_slx.c b/imap/src/osdep/unix/os_slx.c
new file mode 100644
index 00000000..c94d6322
--- /dev/null
+++ b/imap/src/osdep/unix/os_slx.c
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- modern Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c" /* has socklen_t in spite of man page?? */
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocklnx.c"
diff --git a/imap/src/osdep/unix/os_slx.h b/imap/src/osdep/unix/os_slx.h
new file mode 100644
index 00000000..b5f39ffa
--- /dev/null
+++ b/imap/src/osdep/unix/os_slx.h
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ *** These lines are claimed to be necessary to build on Debian Linux on an
+ *** Alpha.
+ */
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 1
+#endif /* _XOPEN_SOURCE */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE 1
+#endif /* _BSD_SOURCE */
+
+/* end Debian Linux on Alpha strangeness */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Linux gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#define flock safe_flock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_sol.c b/imap/src/osdep/unix/os_sol.c
new file mode 100644
index 00000000..7a78ef80
--- /dev/null
+++ b/imap/src/osdep/unix/os_sol.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <crypt.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_soln.h b/imap/src/osdep/unix/os_soln.h
new file mode 100644
index 00000000..969dcbbb
--- /dev/null
+++ b/imap/src/osdep/unix/os_soln.h
@@ -0,0 +1,87 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+#define direct dirent
+#define random lrand48
+
+#define scandir Scandir
+#define alphasort Alphasort
+
+#define getpass getpassphrase
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (const void *d1,const void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_solo.h b/imap/src/osdep/unix/os_solo.h
new file mode 100644
index 00000000..c3fe4a84
--- /dev/null
+++ b/imap/src/osdep/unix/os_solo.h
@@ -0,0 +1,84 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+#define direct dirent
+#define random lrand48
+
+#define scandir Scandir
+#define alphasort Alphasort
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (const void *d1,const void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sos.c b/imap/src/osdep/unix/os_sos.c
new file mode 100644
index 00000000..8256aafa
--- /dev/null
+++ b/imap/src/osdep/unix/os_sos.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- S OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/security.h>
+#include <prot.h>
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_sos.h b/imap/src/osdep/unix/os_sos.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_sos.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sua.c b/imap/src/osdep/unix/os_sua.c
new file mode 100644
index 00000000..dcf9311b
--- /dev/null
+++ b/imap/src/osdep/unix/os_sua.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows Vista SUA
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
diff --git a/imap/src/osdep/unix/os_sua.h b/imap/src/osdep/unix/os_sua.h
new file mode 100644
index 00000000..f4150d4f
--- /dev/null
+++ b/imap/src/osdep/unix/os_sua.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows Vista SUA
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 4 May 2007
+ */
+
+#define _REENTRANT /* for strtok_r() */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_sun.c b/imap/src/osdep/unix/os_sun.c
new file mode 100644
index 00000000..5348ef58
--- /dev/null
+++ b/imap/src/osdep/unix/os_sun.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "memmove.c"
+#include "strerror.c"
+#define strstr Strstr /* override SUN's broken version */
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_sun.h b/imap/src/osdep/unix/os_sun.h
new file mode 100644
index 00000000..c7d72ace
--- /dev/null
+++ b/imap/src/osdep/unix/os_sun.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define strstr Strstr /* override system definition */
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+char *Strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+#define memcpy memmove
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
diff --git a/imap/src/osdep/unix/os_sv2.c b/imap/src/osdep/unix/os_sv2.c
new file mode 100644
index 00000000..14e3951b
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv2.c
@@ -0,0 +1,128 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR2 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno;
+#include <pwd.h>
+#include <sys/socket.h>
+#include <time.h>
+#define KERNEL
+#include <sys/time.h>
+#undef KERNEL
+#include "misc.h"
+
+#define DIR_SIZE(d) sizeof (DIR)
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define NBBY 8 /* number of bits in a byte */
+#define FD_SETSIZE 256
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+ /* bits per mask */
+#define howmany(x, y) (((x)+((y)-1))/(y))
+
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "opendir.c"
+#include "scandir.c"
+#include "memmove2.c"
+#include "strstr.c"
+#include "strerror.c"
+#include "strtoul.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "fsync.c"
+#undef setpgrp
+#include "setpgrp.c"
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+int syslog (int priority,char *message,char *parameters)
+{
+ /* nothing here for now */
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+int openlog (char *ident,int logopt,int facility)
+{
+ /* nothing here for now */
+}
+
+
+/* Emulator for BSD ftruncate() routine
+ * Accepts: file descriptor
+ * length
+ */
+
+int ftruncate (int fd,unsigned long length)
+{
+ return -1; /* gotta figure out how to do this */
+}
diff --git a/imap/src/osdep/unix/os_sv2.h b/imap/src/osdep/unix/os_sv2.h
new file mode 100644
index 00000000..c39904a4
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv2.h
@@ -0,0 +1,120 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR2 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 20 December 2006
+ */
+
+#include <unistd.h>
+#include <string.h>
+#define char void
+#include <memory.h>
+#undef char
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names between BSD and SVR4 */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define lstat stat
+#define random lrand48
+
+#define SIGSTOP SIGQUIT
+
+#define S_IFLNK 0120000
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+
+/* For setitimer() emulation */
+
+#define ITIMER_REAL 0
+
+
+/* For opendir() emulation */
+
+typedef struct _dirdesc {
+ int dd_fd;
+ long dd_loc;
+ long dd_size;
+ char *dd_buf;
+} DIR;
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+struct group *getgrnam (char *name);
+
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+DIR *opendir (char * name);
+int closedir (DIR *d);
+struct direct *readdir (DIR *d);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+int openlog (ident,logopt,facility);
+int syslog (priority,message,parameters ...);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sv4.c b/imap/src/osdep/unix/os_sv4.c
new file mode 100644
index 00000000..4b8dd12f
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv4.c
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR4 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sv4.h b/imap/src/osdep/unix/os_sv4.h
new file mode 100644
index 00000000..a11ad15d
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv4.h
@@ -0,0 +1,78 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR4 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+#define random lrand48
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_ult.c b/imap/src/osdep/unix/os_ult.c
new file mode 100644
index 00000000..28b72ecc
--- /dev/null
+++ b/imap/src/osdep/unix/os_ult.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Ultrix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_ult.h b/imap/src/osdep/unix/os_ult.h
new file mode 100644
index 00000000..3b93f6db
--- /dev/null
+++ b/imap/src/osdep/unix/os_ult.h
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Ultrix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_vu2.c b/imap/src/osdep/unix/os_vu2.c
new file mode 100644
index 00000000..be574e07
--- /dev/null
+++ b/imap/src/osdep/unix/os_vu2.c
@@ -0,0 +1,82 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+#define NFDBITS (sizeof(long) * 8)
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+/* Old Ultrix has its own wierd inet_addr() that returns a in_addr struct. */
+
+/* Portable inet_addr () that returns a u_long
+ * Accepts: dotted host string
+ * Returns: u_long
+ */
+
+u_long portable_inet_addr (char *hostname)
+{
+ struct in_addr *in = &inet_addr (hostname);
+ return in->s_addr;
+}
+
+
+#define inet_addr portable_inet_addr
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_vu2.h b/imap/src/osdep/unix/os_vu2.h
new file mode 100644
index 00000000..7721e092
--- /dev/null
+++ b/imap/src/osdep/unix/os_vu2.h
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <memory.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+u_long portable_inet_addr (char *hostname);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/phile.c b/imap/src/osdep/unix/phile.c
new file mode 100644
index 00000000..ce72d0a9
--- /dev/null
+++ b/imap/src/osdep/unix/phile.c
@@ -0,0 +1,553 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1993
+ * Last Edited: 9 May 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "rfc822.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Types returned from phile_type() */
+
+#define PTYPEBINARY 0 /* binary data */
+#define PTYPETEXT 1 /* textual data */
+#define PTYPECRTEXT 2 /* textual data with CR */
+#define PTYPE8 4 /* textual 8bit data */
+#define PTYPEISO2022JP 8 /* textual Japanese */
+#define PTYPEISO2022KR 16 /* textual Korean */
+#define PTYPEISO2022CN 32 /* textual Chinese */
+
+
+/* PHILE I/O stream local data */
+
+typedef struct phile_local {
+ ENVELOPE *env; /* file envelope */
+ BODY *body; /* file body */
+ char tmp[MAILTMPLEN]; /* temporary buffer */
+} PHILELOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((PHILELOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *phile_valid (char *name);
+int phile_isvalid (char *name,char *tmp);
+void *phile_parameters (long function,void *value);
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void phile_list (MAILSTREAM *stream,char *ref,char *pat);
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long phile_create (MAILSTREAM *stream,char *mailbox);
+long phile_delete (MAILSTREAM *stream,char *mailbox);
+long phile_rename (MAILSTREAM *stream,char *old,char *newname);
+long phile_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *phile_open (MAILSTREAM *stream);
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j);
+void phile_close (MAILSTREAM *stream,long options);
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long phile_ping (MAILSTREAM *stream);
+void phile_check (MAILSTREAM *stream);
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options);
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* File routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER philedriver = {
+ "phile", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_READONLY|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ phile_valid, /* mailbox is valid for us */
+ phile_parameters, /* manipulate parameters */
+ phile_scan, /* scan mailboxes */
+ phile_list, /* list mailboxes */
+ phile_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ phile_status, /* status of mailbox */
+ phile_open, /* open mailbox */
+ phile_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ phile_structure, /* fetch message envelopes */
+ phile_header, /* fetch message header only */
+ phile_text, /* fetch message body only */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ phile_ping, /* ping mailbox to see if still alive */
+ phile_check, /* check for new messages */
+ phile_expunge, /* expunge deleted messages */
+ phile_copy, /* copy messages to another mailbox */
+ phile_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM phileproto = {&philedriver};
+
+/* File validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *phile_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return phile_isvalid (name,tmp) ? &philedriver : NIL;
+}
+
+
+/* File test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int phile_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ char *s;
+ /* INBOX never accepted, any other name is */
+ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
+ !(sbuf.st_mode & S_IFDIR) &&
+ /* only allow empty files if no empty proto
+ or if #ftp */
+ (sbuf.st_size || !default_proto (T) ||
+ ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/'))));
+}
+
+/* File manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *phile_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* File mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* File list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* File list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+
+/* File status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long phile_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ char *s,tmp[MAILTMPLEN];
+ MAILSTATUS status;
+ struct stat sbuf;
+ long ret = NIL;
+ if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) {
+ status.flags = flags; /* return status values */
+ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1;
+ status.messages = status.recent = status.uidnext = 1;
+ status.uidvalidity = sbuf.st_mtime;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = LONGT; /* success */
+ }
+ return ret;
+}
+
+/* File open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *phile_open (MAILSTREAM *stream)
+{
+ int i,k,fd;
+ unsigned long j,m;
+ char *s,tmp[MAILTMPLEN];
+ struct passwd *pw;
+ struct stat sbuf;
+ struct tm *t;
+ MESSAGECACHE *elt;
+ SIZEDTEXT *buf;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &phileproto;
+ if (stream->local) fatal ("phile recycle stream");
+ /* open associated file */
+ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) ||
+ (fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Unable to open file %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ stream->local = fs_get (sizeof (PHILELOCAL));
+ mail_exists (stream,1); /* make sure upper level knows */
+ mail_recent (stream,1);
+ elt = mail_elt (stream,1); /* instantiate cache element */
+ elt->valid = elt->recent = T; /* mark valid flags */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = T; /* make sure upper level knows readonly */
+ /* instantiate a new envelope and body */
+ LOCAL->env = mail_newenvelope ();
+ LOCAL->body = mail_newbody ();
+
+ t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */
+ i = t->tm_hour * 60 + t->tm_min;
+ k = t->tm_yday;
+ t = localtime(&sbuf.st_mtime);/* get local time */
+ /* calculate time delta */
+ i = t->tm_hour * 60 + t->tm_min - i;
+ if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
+ k = abs (i); /* time from UTC either way */
+ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
+ elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
+ elt->year = t->tm_year - (BASEYEAR - 1900);
+ elt->zoccident = (k == i) ? 0 : 1;
+ elt->zhours = k/60;
+ elt->zminutes = k % 60;
+ sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
+ days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
+ elt->zhours,elt->zminutes);
+ /* set up Date field */
+ LOCAL->env->date = cpystr (tmp);
+
+ /* fill in From field from file owner */
+ LOCAL->env->from = mail_newaddr ();
+ if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
+ else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
+ LOCAL->env->from->mailbox = cpystr (tmp);
+ LOCAL->env->from->host = cpystr (mylocalhost ());
+ /* set subject to be mailbox name */
+ LOCAL->env->subject = cpystr (stream->mailbox);
+ /* slurp the data */
+ (buf = &elt->private.special.text)->size = sbuf.st_size;
+ read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size);
+ buf->data[buf->size] = '\0';
+ close (fd); /* close the file */
+ /* analyze data type */
+ if (i = phile_type (buf->data,buf->size,&j)) {
+ LOCAL->body->type = TYPETEXT;
+ LOCAL->body->subtype = cpystr ("PLAIN");
+ if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */
+ s = (char *) buf->data; /* make copy of UNIX-format string */
+ buf->data = NIL; /* zap the buffer */
+ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size);
+ fs_give ((void **) &s); /* flush original UNIX-format string */
+ }
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("charset");
+ LOCAL->body->parameter->value =
+ cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
+ (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
+ (i & PTYPEISO2022CN) ? "ISO-2022-CN" :
+ (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII");
+ LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
+ LOCAL->body->size.lines = j;
+ }
+ else { /* binary data */
+ LOCAL->body->type = TYPEAPPLICATION;
+ LOCAL->body->subtype = cpystr ("OCTET-STREAM");
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("name");
+ LOCAL->body->parameter->value =
+ cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
+ LOCAL->body->encoding = ENCBASE64;
+ buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size);
+ fs_give ((void **) &s); /* flush originary binary contents */
+ }
+ phile_header (stream,1,&j,NIL);
+ LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size;
+ elt->rfc822_size = j + buf->size;
+ /* only one message ever... */
+ stream->uid_validity = sbuf.st_mtime;
+ stream->uid_last = elt->private.uid = 1;
+ return stream; /* return stream alive to caller */
+}
+
+/* File determine data type
+ * Accepts: data to examine
+ * size of data
+ * pointer to line count return
+ * Returns: PTYPE mask of data type
+ */
+
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j)
+{
+ int ret = PTYPETEXT;
+ char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ *j = 0; /* no lines */
+ /* check type of every character */
+ while (i--) switch (charvec[*s++]) {
+ case 'A':
+ ret |= PTYPE8; /* 8bit character */
+ break;
+ case 'a':
+ break; /* ASCII character */
+ case 'b':
+ return PTYPEBINARY; /* binary byte seen, stop immediately */
+ case 'c':
+ ret |= PTYPECRTEXT; /* CR indicates Internet text */
+ break;
+ case 'e': /* ESC */
+ if (*s == '$') { /* ISO-2022 sequence? */
+ switch (s[1]) {
+ case 'B': case '@': ret |= PTYPEISO2022JP; break;
+ case ')':
+ switch (s[2]) {
+ case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break;
+ case 'C': ret |= PTYPEISO2022KR; break;
+ }
+ case '*':
+ switch (s[2]) {
+ case 'H': ret |= PTYPEISO2022CN; break;
+ }
+ case '+':
+ switch (s[2]) {
+ case 'I': case 'J': case 'K': case 'L': case 'M':
+ ret |= PTYPEISO2022CN; break;
+ }
+ }
+ }
+ break;
+ case 'l': /* newline */
+ (*j)++;
+ break;
+ }
+ return ret; /* return type of data */
+}
+
+/* File close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void phile_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* File fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ if (body) *body = LOCAL->body;
+ return LOCAL->env; /* return the envelope */
+}
+
+
+/* File fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body);
+ *length = strlen (LOCAL->tmp);
+ return LOCAL->tmp;
+}
+
+
+/* File fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text;
+ if (!(flags &FT_PEEK)) { /* mark message as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,buf->data,buf->size);
+ return T;
+}
+
+/* File ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long phile_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+/* File check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void phile_check (MAILSTREAM *stream)
+{
+ mm_log ("Check completed",NIL);
+}
+
+/* File expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* File copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format",
+ stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* File append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = mailboxfile (file,mailbox);
+ if (s && *s)
+ sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s);
+ else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/pmatch.c b/imap/src/osdep/unix/pmatch.c
new file mode 100644
index 00000000..a7539e50
--- /dev/null
+++ b/imap/src/osdep/unix/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-dependent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL;
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL;
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/pseudo.c b/imap/src/osdep/unix/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/unix/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/unix/pseudo.h b/imap/src/osdep/unix/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/unix/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/unix/rename.c b/imap/src/osdep/unix/rename.c
new file mode 100644
index 00000000..6a4c1bda
--- /dev/null
+++ b/imap/src/osdep/unix/rename.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Rename file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 May 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for working Unix rename() call
+ * Accepts: old file name
+ * new file name
+ * Returns: 0 if success, -1 if error with error in errno
+ */
+
+int Rename (char *oldname,char *newname)
+{
+ int ret;
+ unlink (newname); /* make sure the old name doesn't exist */
+ /* link to new name, unlink old name */
+ if (!(ret = link (oldname,newname))) unlink (oldname);
+ return ret;
+}
+
+
diff --git a/imap/src/osdep/unix/scandir.c b/imap/src/osdep/unix/scandir.c
new file mode 100644
index 00000000..878343e8
--- /dev/null
+++ b/imap/src/osdep/unix/scandir.c
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Scan directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+/* Emulator for BSD scandir() call
+ * Accepts: directory name
+ * destination pointer of names array
+ * selection function
+ * comparison function
+ * Returns: number of elements in the array or -1 if error
+ */
+
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar)
+{
+ struct direct *p,*d,**names;
+ int nitems;
+ struct stat stb;
+ long nlmax;
+ DIR *dirp = opendir (dirname);/* open directory and get status poop */
+ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1;
+ nlmax = stb.st_size / 24; /* guesstimate at number of files */
+ names = (struct direct **) fs_get (nlmax * sizeof (struct direct *));
+ nitems = 0; /* initially none found */
+ while (d = readdir (dirp)) { /* read directory item */
+ /* matches select criterion? */
+ if (select && !(*select) (d)) continue;
+ /* get size of direct record for this file */
+ p = (struct direct *) fs_get (DIR_SIZE (d));
+ p->d_ino = d->d_ino; /* copy the poop */
+ strcpy (p->d_name,d->d_name);
+ if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */
+ void *s = (void *) names; /* stupid language */
+ nlmax *= 2; /* double it */
+ fs_resize ((void **) &s,nlmax * sizeof (struct direct *));
+ names = (struct direct **) s;
+ }
+ names[nitems - 1] = p; /* store this file there */
+ }
+ closedir (dirp); /* done with directory */
+ /* sort if necessary */
+ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar);
+ *namelist = names; /* return directory */
+ return nitems; /* and size */
+}
+
+/* Alphabetic file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int alphasort (void *d1,void *d2)
+{
+ return strcmp ((*(struct direct **) d1)->d_name,
+ (*(struct direct **) d2)->d_name);
+}
diff --git a/imap/src/osdep/unix/setpgrp.c b/imap/src/osdep/unix/setpgrp.c
new file mode 100644
index 00000000..ca957bb2
--- /dev/null
+++ b/imap/src/osdep/unix/setpgrp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Set process group emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD setpgrp() call
+ * Accepts: process ID
+ * group ID
+ * Returns: 0 if successful, -1 if failure
+ */
+
+int Setpgrp (int pid,int gid)
+{
+ return setpgrp ();
+}
diff --git a/imap/src/osdep/unix/sig_bsd.c b/imap/src/osdep/unix/sig_bsd.c
new file mode 100644
index 00000000..c615e1b0
--- /dev/null
+++ b/imap/src/osdep/unix/sig_bsd.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ return (void *) signal (sig,action);
+}
diff --git a/imap/src/osdep/unix/sig_psx.c b/imap/src/osdep/unix/sig_psx.c
new file mode 100644
index 00000000..0d592c31
--- /dev/null
+++ b/imap/src/osdep/unix/sig_psx.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: POSIX Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+#include <string.h>
+
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ struct sigaction nact,oact;
+ memset (&nact,0,sizeof (struct sigaction));
+ sigemptyset (&nact.sa_mask); /* no signals blocked */
+ nact.sa_handler = action; /* set signal handler */
+ nact.sa_flags = SA_RESTART; /* needed on Linux, nice on SVR4 */
+ sigaction (sig,&nact,&oact); /* do the signal action */
+ return (void *) oact.sa_handler;
+}
diff --git a/imap/src/osdep/unix/sig_sv4.c b/imap/src/osdep/unix/sig_sv4.c
new file mode 100644
index 00000000..599d0b38
--- /dev/null
+++ b/imap/src/osdep/unix/sig_sv4.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ return (void *) sigset (sig,action);
+}
diff --git a/imap/src/osdep/unix/ssl_none.c b/imap/src/osdep/unix/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/unix/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/unix/ssl_unix.c b/imap/src/osdep/unix/ssl_unix.c
new file mode 100644
index 00000000..5a7d0bc0
--- /dev/null
+++ b/imap/src/osdep/unix/ssl_unix.c
@@ -0,0 +1,821 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2007
+ */
+
+#define crypt ssl_private_crypt
+#include <x509v3.h>
+#include <ssl.h>
+#include <err.h>
+#include <pem.h>
+#include <buffer.h>
+#include <bio.h>
+#include <crypto.h>
+#include <rand.h>
+#undef crypt
+
+#define SSLBUFLEN 8192
+#define SSLCIPHERLIST "ALL:!LOW"
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ SSL_CTX *context; /* SSL context */
+ SSL *con; /* SSL connection */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[SSLBUFLEN]; /* input buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
+static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
+static char *ssl_validate_cert (X509 *cert,char *host);
+static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+static RSA *ssl_genkey (SSL *con,int export,int keylength);
+
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+ /* non-NIL if doing SSL primary I/O */
+static SSLSTDIOSTREAM *sslstdio = NIL;
+static char *start_tls = NIL; /* non-NIL if start TLS requested */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ int fd;
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* if system doesn't have /dev/urandom */
+ if (stat ("/dev/urandom",&sbuf)) {
+ while ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT|O_EXCL,0600)) < 0)
+ sleep (1);
+ unlink (tmp); /* don't need the file */
+ fstat (fd,&sbuf); /* get information about the file */
+ close (fd); /* flush descriptor */
+ /* not great but it'll have to do */
+ sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
+ tcp_serveraddr (),(unsigned long) tcp_serverport (),
+ tcp_clientaddr (),(unsigned long) tcp_clientport (),
+ (unsigned long) sbuf.st_ino,(unsigned long) time (0),
+ (unsigned long) gethostid (),(unsigned long) getpid ());
+ RAND_seed (tmp,strlen (tmp));
+ }
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ SSL_library_init (); /* add all algorithms */
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ char *reason,tmp[MAILTMPLEN];
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* do the work */
+ reason = ssl_start_work (stream,host,flags);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (reason) { /* failed? */
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ }
+ return stream;
+}
+
+/* Start SSL/TLS negotiations worker routine
+ * Accepts: SSL stream
+ * user's host name
+ * flags
+ * Returns: NIL if success, else error reason
+ */
+
+ /* evil but I had no choice */
+static char *ssl_last_error = NIL;
+static char *ssl_last_host = NIL;
+
+static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
+{
+ BIO *bio;
+ X509 *cert;
+ unsigned long sl,tl;
+ char *s,*t,*err,tmp[MAILTMPLEN];
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslclientcert_t scc =
+ (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
+ sslclientkey_t sck =
+ (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
+ if (ssl_last_error) fs_give ((void **) &ssl_last_error);
+ ssl_last_host = host;
+ if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
+ TLSv1_client_method () :
+ SSLv23_client_method ())))
+ return "SSL context failed";
+ SSL_CTX_set_options (stream->context,0);
+ /* disable certificate validation? */
+ if (flags & NET_NOVALIDATECERT)
+ SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
+ else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
+ /* set default paths to CAs... */
+ SSL_CTX_set_default_verify_paths (stream->context);
+ /* ...unless a non-standard path desired */
+ if (s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL))
+ SSL_CTX_load_verify_locations (stream->context,NIL,s);
+ /* want to send client certificate? */
+ if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
+ if (cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) {
+ SSL_CTX_use_certificate (stream->context,cert);
+ X509_free (cert);
+ }
+ BIO_free (bio);
+ if (!cert) return "SSL client certificate failed";
+ /* want to supply private key? */
+ if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
+ EVP_PKEY *key;
+ if (key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
+ NIL,NIL,"")) {
+ SSL_CTX_use_PrivateKey (stream->context,key);
+ EVP_PKEY_free (key);
+ }
+ BIO_free (bio);
+ memset (t,0,tl); /* erase key */
+ }
+ if (s != t) memset (s,0,sl);/* erase certificate if different from key */
+ }
+
+ /* create connection */
+ if (!(stream->con = (SSL *) SSL_new (stream->context)))
+ return "SSL connection failed";
+ bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
+ SSL_set_bio (stream->con,bio,bio);
+ SSL_set_connect_state (stream->con);
+ if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
+ /* now negotiate SSL */
+ if (SSL_write (stream->con,"",0) < 0)
+ return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
+ /* need to validate host names? */
+ if (!(flags & NET_NOVALIDATECERT) &&
+ (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
+ host))) {
+ /* application callback */
+ if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
+ /* error message to return via mm_log() */
+ sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
+ return ssl_last_error = cpystr (tmp);
+ }
+ return NIL;
+}
+
+/* SSL certificate verification callback
+ * Accepts: error flag
+ * X509 context
+ * Returns: error flag
+ */
+
+static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
+{
+ char *err,cert[256],tmp[MAILTMPLEN];
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ if (!ok) { /* in case failure */
+ err = (char *) X509_verify_cert_error_string
+ (X509_STORE_CTX_get_error (ctx));
+ X509_NAME_oneline (X509_get_subject_name
+ (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
+ if (!scq) { /* mm_log() error message if no callback */
+ sprintf (tmp,"*%.128s: %.255s",err,cert);
+ ssl_last_error = cpystr (tmp);
+ }
+ /* ignore error if application says to */
+ else if ((*scq) (err,ssl_last_host,cert)) ok = T;
+ /* application wants punt */
+ else ssl_last_error = cpystr ("");
+ }
+ return ok;
+}
+
+
+/* SSL validate certificate
+ * Accepts: certificate
+ * host to validate against
+ * Returns: NIL if validated, else string of error message
+ */
+
+static char *ssl_validate_cert (X509 *cert,char *host)
+{
+ int i,n;
+ char *s,*t,*ret;
+ void *ext;
+ GENERAL_NAME *name;
+ /* make sure have a certificate */
+ if (!cert) ret = "No certificate from server";
+ /* and that it has a name */
+ else if (!cert->name) ret = "No name in certificate";
+ /* locate CN */
+ else if (s = strstr (cert->name,"/CN=")) {
+ if (t = strchr (s += 4,'/')) *t = '\0';
+ /* host name matches pattern? */
+ ret = ssl_compare_hostnames (host,s) ? NIL :
+ "Server name does not match certificate";
+ if (t) *t = '/'; /* restore smashed delimiter */
+ /* if mismatch, see if in extensions */
+ if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
+ (n = sk_GENERAL_NAME_num (ext)))
+ /* older versions of OpenSSL use "ia5" instead of dNSName */
+ for (i = 0; ret && (i < n); i++)
+ if ((name = sk_GENERAL_NAME_value (ext,i)) &&
+ (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
+ ssl_compare_hostnames (host,s)) ret = NIL;
+ }
+ else ret = "Unable to locate common name in certificate";
+ return ret;
+}
+
+/* Case-independent wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * Returns: T if pattern matches base, else NIL
+ */
+
+static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
+{
+ long ret = NIL;
+ switch (*pat) {
+ case '*': /* wildcard */
+ if (pat[1]) { /* there must be a pattern suffix */
+ /* there is, scan base against it */
+ do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
+ while (!ret && (*s != '.') && *s++);
+ }
+ break;
+ case '\0': /* end of pattern */
+ if (!*s) ret = LONGT; /* success if base is also at end */
+ break;
+ default: /* non-wildcard, recurse if match */
+ if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
+ break;
+ }
+ return ret;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ int i,sock;
+ fd_set fds,efds;
+ struct timeval tmo;
+ tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
+ long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
+ /* tcp_unix should have prevented this */
+ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ int ti = ttmo_read ? now + ttmo_read : 0;
+ if (SSL_pending (stream->con)) i = 1;
+ else {
+ if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* set bit in selection vector */
+ FD_SET (sock,&efds); /* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ }
+ if (i) { /* non-timeout result from select? */
+ errno = 0; /* just in case */
+ if (i > 0) /* read what we can */
+ while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
+ ((errno == EINTR) ||
+ (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
+ errno,SSL_get_error (stream->con,i));
+ else s = "SSL data read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return ssl_abort (stream);
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
+ if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
+ return ssl_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ long i;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!stream->con) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
+ /* until request satisfied */
+ for (i = 0; size > 0; string += i,size -= i)
+ /* write as much as we can */
+ if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"SSL data write I/O error %d SSL error %d",
+ errno,SSL_get_error (stream->con,i));
+ mm_log (tmp,TCPDEBUG);
+ }
+ return ssl_abort (stream);/* write failed */
+ }
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ (*bn) (BLOCK_NONE,NIL);
+ return LONGT; /* all done */
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->con) { /* close SSL connection */
+ SSL_shutdown (stream->con);
+ SSL_free (stream->con);
+ stream->con = NIL;
+ }
+ if (stream->context) { /* clean up context */
+ SSL_CTX_free (stream->context);
+ stream->context = NIL;
+ }
+ if (stream->tcpstream) { /* close TCP stream */
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ if (sslstdio) return cpystr ("Already in an SSL session");
+ if (start_tls) return cpystr ("TLS already started");
+ if (server) { /* build specific certificate/key file name */
+ sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
+ if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
+ sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
+ if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
+ }
+ start_tls = server; /* switch to STARTTLS mode */
+ }
+ return NIL;
+}
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ char cert[MAILTMPLEN],key[MAILTMPLEN];
+ unsigned long i;
+ struct stat sbuf;
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ ssl_onceonlyinit (); /* make sure algorithms added */
+ ERR_load_crypto_strings ();
+ SSL_load_error_strings ();
+ /* build specific certificate/key file names */
+ sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
+ sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
+ /* use non-specific name if no specific cert */
+ if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
+ if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
+ sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
+ /* use cert file as fallback for key */
+ if (stat (key,&sbuf)) strcpy (key,cert);
+ }
+ /* create context */
+ if (!(stream->context = SSL_CTX_new (start_tls ?
+ TLSv1_server_method () :
+ SSLv23_server_method ())))
+ syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
+ tcp_clienthost ());
+ else { /* set context options */
+ SSL_CTX_set_options (stream->context,SSL_OP_ALL);
+ /* set cipher list */
+ if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
+ syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
+ SSLCIPHERLIST,tcp_clienthost ());
+ /* load certificate */
+ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
+ syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
+ cert,tcp_clienthost ());
+ /* load key */
+ else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
+ SSL_FILETYPE_PEM)))
+ syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
+ key,tcp_clienthost ());
+
+ else { /* generate key if needed */
+ if (SSL_CTX_need_tmp_RSA (stream->context))
+ SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
+ /* create new SSL connection */
+ if (!(stream->con = SSL_new (stream->context)))
+ syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
+ tcp_clienthost ());
+ else { /* set file descriptor */
+ SSL_set_fd (stream->con,0);
+ /* all OK if accepted */
+ if (SSL_accept (stream->con) < 0)
+ syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
+ tcp_clienthost ());
+ else { /* server set up */
+ sslstdio = (SSLSTDIOSTREAM *)
+ memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
+ sslstdio->sslstream = stream;
+ /* available space in output buffer */
+ sslstdio->octr = SSLBUFLEN;
+ /* current output buffer pointer */
+ sslstdio->optr = sslstdio->obuf;
+ /* allow plaintext if disable value was 2 */
+ if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
+ mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
+ /* unhide PLAIN SASL authenticator */
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
+ return;
+ }
+ }
+ }
+ }
+ while (i = ERR_get_error ()) /* SSL failure */
+ syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
+ ssl_close (stream); /* punt stream */
+ exit (1); /* punt this program too */
+}
+
+/* Generate one-time key for server
+ * Accepts: SSL connection
+ * export flag
+ * keylength
+ * Returns: generated key, always
+ */
+
+static RSA *ssl_genkey (SSL *con,int export,int keylength)
+{
+ unsigned long i;
+ static RSA *key = NIL;
+ if (!key) { /* if don't have a key already */
+ /* generate key */
+ if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
+ syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
+ tcp_clienthost ());
+ while (i = ERR_get_error ())
+ syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
+ exit (1);
+ }
+ }
+ return key;
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long ssl_server_input_wait (long seconds)
+{
+ int i,sock;
+ fd_set fds,efd;
+ struct timeval tmo;
+ SSLSTREAM *stream;
+ if (!sslstdio) return server_input_wait (seconds);
+ /* input available in buffer */
+ if (((stream = sslstdio->sslstream)->ictr > 0) ||
+ !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
+ /* sock ought to be 0 always */
+ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
+ /* input available from SSL */
+ if (SSL_pending (stream->con) &&
+ ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ return LONGT;
+ }
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efd); /* initialize selection vector */
+ FD_SET (sock,&fds); /* set bit in selection vector */
+ FD_SET (sock,&efd); /* set bit in selection vector */
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ /* see if input available from the socket */
+ return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+#include "sslstdio.c"
diff --git a/imap/src/osdep/unix/sslstdio.c b/imap/src/osdep/unix/sslstdio.c
new file mode 100644
index 00000000..4059a859
--- /dev/null
+++ b/imap/src/osdep/unix/sslstdio.c
@@ -0,0 +1,169 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL standard I/O routines for server use
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ if (!sslstdio) return getchar ();
+ if (!ssl_getdata (sslstdio->sslstream)) return EOF;
+ /* one last byte available */
+ sslstdio->sslstream->ictr--;
+ return (int) *(sslstdio->sslstream->iptr)++;
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ int i,c;
+ if (start_tls) { /* doing a start TLS? */
+ ssl_server_init (start_tls);/* enter the mode */
+ start_tls = NIL; /* don't do this again */
+ }
+ if (!sslstdio) return fgets (s,n,stdin);
+ for (i = c = 0, n-- ; (c != '\n') && (i < n); sslstdio->sslstream->ictr--) {
+ if ((sslstdio->sslstream->ictr <= 0) && !ssl_getdata (sslstdio->sslstream))
+ return NIL; /* read error */
+ c = s[i++] = *(sslstdio->sslstream->iptr)++;
+ }
+ s[i] = '\0'; /* tie off string */
+ return s;
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ if (start_tls) { /* doing a start TLS? */
+ ssl_server_init (start_tls);/* enter the mode */
+ start_tls = NIL; /* don't do this again */
+ }
+ if (sslstdio) return ssl_getbuffer (sslstdio->sslstream,n,s);
+ /* non-SSL case */
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return (sslstdio ? ssl_server_input_wait : server_input_wait) (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ if (!sslstdio) return putchar (c);
+ /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) return EOF;
+ sslstdio->octr--; /* count down one character */
+ *sslstdio->optr++ = c; /* write character */
+ return c; /* return that character */
+}
+
+
+/* Put string
+ * Accepts: destination string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ if (!sslstdio) return fputs (s,stdout);
+ while (*s) { /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) return EOF;
+ *sslstdio->optr++ = *s++; /* write one more character */
+ sslstdio->octr--; /* count down one character */
+ }
+ return 0; /* success */
+}
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t = s->data;
+ unsigned long i = s->size;
+ unsigned long j;
+ if (sslstdio) while (i) { /* until request satisfied */
+ /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) break;
+ /* blat as big a chucnk as we can */
+ memcpy (sslstdio->optr,t,j = min (i,sslstdio->octr));
+ sslstdio->optr += j; /* account for chunk */
+ sslstdio->octr -= j;
+ t += j;
+ i -= j;
+ }
+ else while (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)))
+ t += j,i -= j;
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ if (!sslstdio) return fflush (stdout);
+ /* force out buffer */
+ if (!ssl_sout (sslstdio->sslstream,sslstdio->obuf,
+ SSLBUFLEN - sslstdio->octr)) return EOF;
+ /* renew output buffer */
+ sslstdio->optr = sslstdio->obuf;
+ sslstdio->octr = SSLBUFLEN;
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/strerror.c b/imap/src/osdep/unix/strerror.c
new file mode 100644
index 00000000..3c2e6a14
--- /dev/null
+++ b/imap/src/osdep/unix/strerror.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Error number to string
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Return implementation-defined string corresponding to error
+ * Accepts: error number
+ * Returns: string for that error
+ */
+
+char *strerror (int n)
+{
+ return (n >= 0 && n < sys_nerr) ? sys_errlist[n] : NIL;
+}
diff --git a/imap/src/osdep/unix/tcp_unix.c b/imap/src/osdep/unix/tcp_unix.c
new file mode 100644
index 00000000..127cf2ae
--- /dev/null
+++ b/imap/src/osdep/unix/tcp_unix.c
@@ -0,0 +1,1043 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+#include "ip_unix.c"
+
+#undef write /* don't use redefined write() */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long rshtimeout = 15; /* rsh timeout */
+static char *rshcommand = NIL; /* rsh command */
+static char *rshpath = NIL; /* rsh path */
+static long sshtimeout = 15; /* ssh timeout */
+static char *sshcommand = NIL; /* ssh command */
+static char *sshpath = NIL; /* ssh path */
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+static char *myClientAddr = NIL;/* client IP address */
+static char *myClientHost = NIL;/* client DNS name */
+static long myClientPort = -1; /* client port number */
+static char *myServerAddr = NIL;/* server IP address */
+static char *myServerHost = NIL;/* server DNS name */
+static long myServerPort = -1; /* server port number */
+
+extern long maxposint; /* get this from write.c */
+
+/* Local function prototypes */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,int *ctr,char *hst);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+char *tcp_name (struct sockaddr *sadr,long flag);
+char *tcp_name_valid (char *s);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+
+ case SET_RSHTIMEOUT:
+ rshtimeout = (long) value;
+ case GET_RSHTIMEOUT:
+ ret = (void *) rshtimeout;
+ break;
+ case SET_RSHCOMMAND:
+ if (rshcommand) fs_give ((void **) &rshcommand);
+ rshcommand = cpystr ((char *) value);
+ case GET_RSHCOMMAND:
+ ret = (void *) rshcommand;
+ break;
+ case SET_RSHPATH:
+ if (rshpath) fs_give ((void **) &rshpath);
+ rshpath = cpystr ((char *) value);
+ case GET_RSHPATH:
+ ret = (void *) rshpath;
+ break;
+ case SET_SSHTIMEOUT:
+ sshtimeout = (long) value;
+ case GET_SSHTIMEOUT:
+ ret = (void *) sshtimeout;
+ break;
+ case SET_SSHCOMMAND:
+ if (sshcommand) fs_give ((void **) &sshcommand);
+ sshcommand = cpystr ((char *) value);
+ case GET_SSHCOMMAND:
+ ret = (void *) sshcommand;
+ break;
+ case SET_SSHPATH:
+ if (sshpath) fs_give ((void **) &sshpath);
+ sshpath = cpystr ((char *) value);
+ case GET_SSHPATH:
+ ret = (void *) sshpath;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int family;
+ int sock = -1;
+ int ctr = 0;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
+ char *s,*hostname,tmp[MAILTMPLEN];
+ void *adr;
+ size_t adrlen;
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data,*next;
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sv->s_port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[(strlen (tmp))-1] = '\0';
+ if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ /* get an open socket for this system */
+ sock = tcp_socket_open (family,adr,adrlen,port,tmp,ctrp,hostname = host);
+ (*bn) (BLOCK_NONE,NIL);
+ fs_give ((void **) &adr);
+ }
+ else sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
+ sprintf (tmp,"No such host as %.80s",host);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL);
+ if (s) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ do {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ if (((sock = tcp_socket_open (family,s,adrlen,port,tmp,ctrp,
+ hostname)) < 0) &&
+ (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
+ !silent) mm_log (tmp,WARN);
+ (*bn) (BLOCK_NONE,NIL);
+ } while ((sock < 0) && s);/* repeat until success or no more addreses */
+ }
+ }
+ if (sock >= 0) { /* won */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ /* stash in the snuck-in byte */
+ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ else if (!silent) mm_log (tmp,ERROR);
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: protocol family
+ * address to connect to
+ * address length
+ * port
+ * scratch buffer
+ * pointer to "first byte read in" storage or NIL
+ * host name for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,int *ctr,char *hst)
+{
+ int i,ti,sock,flgs;
+ size_t len;
+ time_t now;
+ struct protoent *pt = getprotobyname ("tcp");
+ fd_set fds,efds;
+ struct timeval tmo;
+ struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* fetid Solaris */
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
+ mm_log (tmp,NIL);
+ /* make a socket */
+ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ }
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ close (sock);
+ sock = -1;
+ errno = EMFILE;
+ }
+
+ else { /* get current socket flags */
+ flgs = fcntl (sock,F_GETFL,0);
+ /* set non-blocking if want open timeout */
+ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
+ /* open connection */
+ while ((i = connect (sock,sadr,len)) < 0 && (errno == EINTR));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (i < 0) switch (errno) { /* failed? */
+ case EAGAIN: /* DG brain damage */
+ case EINPROGRESS: /* what we expect to happen */
+ case EALREADY: /* or another form of it */
+ case EISCONN: /* restart after interrupt? */
+ case EADDRINUSE: /* restart after interrupt? */
+ break; /* well, not really, it was interrupted */
+ default:
+ sprintf (tmp,"Can't connect to %.80s,%u: %s",hst,(unsigned int) port,
+ strerror (errno));
+ close (sock); /* flush socket */
+ sock = -1;
+ }
+ if ((sock >= 0) && ctr) { /* want open timeout? */
+ now = time (0); /* open timeout */
+ ti = ttmo_open ? now + ttmo_open : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* block for error or readable */
+ FD_SET (sock,&efds);
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* success, make sure really connected */
+ /* restore blocking status */
+ fcntl (sock,F_SETFL,flgs);
+ /* This used to be a zero-byte read(), but that crashes Solaris */
+ /* get socket status */
+ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
+ }
+ if (i <= 0) { /* timeout or error? */
+ i = i ? errno : ETIMEDOUT;/* determine error code */
+ close (sock); /* flush socket */
+ sock = -1;
+ errno = i; /* return error code */
+ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,
+ (unsigned long) port,strerror (errno));
+ }
+ }
+ }
+ fs_give ((void **) &sadr);
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+#define MAXARGV 20
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ TCPSTREAM *stream = NIL;
+ void *adr;
+ char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r;
+ int i,ti,pipei[2],pipeo[2];
+ size_t len;
+ time_t now;
+ struct timeval tmo;
+ fd_set fds,efds;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+#ifdef SSHPATH /* ssh path defined yet? */
+ if (!sshpath) sshpath = cpystr (SSHPATH);
+#endif
+#ifdef RSHPATH /* rsh path defined yet? */
+ if (!rshpath) rshpath = cpystr (RSHPATH);
+#endif
+ if (*service == '*') { /* want ssh? */
+ /* return immediately if ssh disabled */
+ if (!(sshpath && (ti = sshtimeout))) return NIL;
+ /* ssh command prototype defined yet? */
+ if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
+ }
+ /* want rsh? */
+ else if (rshpath && (ti = rshtimeout)) {
+ /* rsh command prototype defined yet? */
+ if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
+ }
+ else return NIL; /* rsh disabled */
+ /* look like domain literal? */
+ if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') {
+ strcpy (host,mb->host+1); /* yes, copy without brackets */
+ host[i-1] = '\0';
+ /* validate domain literal */
+ if (adr = ip_stringtoaddr (host,&len,&i)) fs_give ((void **) &adr);
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else strcpy (host,tcp_canonical (mb->host));
+
+ if (*service == '*') /* build ssh command */
+ sprintf (tmp,sshcommand,sshpath,host,
+ mb->user[0] ? mb->user : myusername (),service + 1);
+ else sprintf (tmp,rshcommand,rshpath,host,
+ mb->user[0] ? mb->user : myusername (),service);
+ if (tcpdebug) {
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Trying %.100s",tmp);
+ mm_log (msg,TCPDEBUG);
+ }
+ /* parse command into argv */
+ for (i = 1,path = argv[0] = strtok_r (tmp," ",&r);
+ (i < MAXARGV) && (argv[i] = strtok_r (NIL," ",&r)); i++);
+ argv[i] = NIL; /* make sure argv tied off */
+ /* make command pipes */
+ if (pipe (pipei) < 0) return NIL;
+ if ((pipei[0] >= FD_SETSIZE) || (pipei[1] >= FD_SETSIZE) ||
+ (pipe (pipeo) < 0)) {
+ close (pipei[0]); close (pipei[1]);
+ return NIL;
+ }
+ (*bn) (BLOCK_TCPOPEN,NIL); /* quell alarm up here for NeXT */
+ if ((pipeo[0] >= FD_SETSIZE) || (pipeo[1] >= FD_SETSIZE) ||
+ ((i = fork ()) < 0)) { /* make inferior process */
+ close (pipei[0]); close (pipei[1]);
+ close (pipeo[0]); close (pipeo[1]);
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+ }
+ if (!i) { /* if child */
+ alarm (0); /* never have alarms in children */
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ int cf; /* don't alter parent vars in case vfork() */
+ int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
+ dup2 (pipei[1],1); /* parent's input is my output */
+ dup2 (pipei[1],2); /* parent's input is my error output too */
+ dup2 (pipeo[0],0); /* parent's output is my input */
+ /* close all unnecessary descriptors */
+ for (cf = 3; cf <= maxfd; cf++) close (cf);
+ setpgrp (0,getpid ()); /* be our own process group */
+ _exit (execv (path,argv));/* now run it */
+ }
+ _exit (1); /* child is done */
+ }
+ grim_pid_reap (i,NIL); /* reap child; grandchild now owned by init */
+ close (pipei[1]); /* close child's side of the pipes */
+ close (pipeo[0]);
+
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ /* copy remote host name from argument */
+ stream->remotehost = cpystr (stream->host = cpystr (host));
+ stream->tcpsi = pipei[0]; /* init sockets */
+ stream->tcpso = pipeo[1];
+ stream->ictr = 0; /* init input counter */
+ stream->port = 0xffffffff; /* no port number */
+ ti += now = time (0); /* open timeout */
+ tmo.tv_usec = 0; /* initialize usec timeout */
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds); /* set bit in selection vector */
+ FD_SET (stream->tcpsi,&efds); /* set bit in error selection vector */
+ FD_SET (stream->tcpso,&efds); /* set bit in error selection vector */
+ do { /* block under timeout */
+ tmo.tv_sec = ti - now;
+ i = select (max (stream->tcpsi,stream->tcpso)+1,&fds,NIL,&efds,&tmo);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i <= 0) { /* timeout or error? */
+ sprintf (tmp,i ? "error in %s to IMAP server" :
+ "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh");
+ mm_log (tmp,WARN);
+ tcp_close (stream); /* punt stream */
+ stream = NIL;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ /* return user name */
+ strcpy (usrbuf,mb->user[0] ? mb->user : myusername ());
+ return stream; /* return success */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi < 0) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ if (i > 0) /* read what we can */
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0)
+ && (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
+ else s = "TCP buffer read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ s += i; /* success, point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return LONGT;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi < 0) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ /* read what we can */
+ if (i > 0) while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
+ else s = "TCP data read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ stream->ictr = i; /* success, set new count and pointer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpso < 0) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ /* write what we can */
+ if (i > 0) while (((i = write (stream->tcpso,string,size)) < 0) &&
+ (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"TCP write I/O error %d",errno);
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ string += i; /* how much we sent */
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ close (stream->tcpsi); /* nuke the socket */
+ if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->remotehost = /* get socket's peer name */
+ getpeername (stream->tcpsi,sadr,(void *) &sadrlen) ?
+ cpystr (stream->host) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ getsockname (stream->tcpsi,sadr,(void *) &sadrlen)) ?
+ cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getpeername (0,sadr,(void *) &sadrlen))
+ myClientAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getpeername (0,sadr,(void *) &sadrlen)) {
+ char *s,*t,*v,tmp[MAILTMPLEN];
+ if ((s = getenv (t = "SSH_CLIENT")) ||
+ (s = getenv (t = "KRB5REMOTEADDR")) ||
+ (s = getenv (t = "SSH2_CLIENT"))) {
+ if (v = strchr (s,' ')) *v = '\0';
+ sprintf (v = tmp,"%.80s=%.80s",t,s);
+ }
+ else v = "UNKNOWN";
+ myClientHost = cpystr (v);
+ }
+ else { /* get stdin's peer name */
+ myClientHost = tcp_name (sadr,T);
+ if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientHost;
+}
+
+
+/* TCP/IP get client port number (server calls only)
+ * Returns: client port number
+ */
+
+long tcp_clientport ()
+{
+ if (!myClientHost && !myClientAddr) tcp_clientaddr ();
+ return myClientPort;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getsockname (0,sadr,(void *) &sadrlen))
+ myServerAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's name */
+ myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) { /* once-only */
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ /* get stdin's name */
+ if (getsockname (0,sadr,(void *) &sadrlen))
+ myServerHost = cpystr (mylocalhost ());
+ else { /* get stdin's name */
+ myServerHost = tcp_name (sadr,NIL);
+ if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost && !myServerAddr) tcp_serveraddr ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* get canonical name */
+ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr *sadr,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
+ if (allowreversedns) {
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+
+/* TCP/IP validate name
+ * Accepts: domain name
+ * Returns: name if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
+
+/* TCP/IP check if client is given host name
+ * Accepts: candidate host name
+ * Returns: T if match, NIL otherwise
+ */
+
+long tcp_isclienthost (char *host)
+{
+ int family;
+ size_t adrlen,sadrlen,len;
+ void *adr,*next;
+ struct sockaddr *sadr;
+ long ret = NIL;
+ /* make sure that myClientAddr is set */
+ if (tcp_clienthost () && myClientAddr)
+ /* get sockaddr of client */
+ for (adr = ip_nametoaddr (host,&adrlen,&family,NIL,&next); adr && !ret;
+ adr = ip_nametoaddr (NIL,&adrlen,&family,NIL,&next)) {
+ /* build sockaddr of given address */
+ sadr = ip_sockaddr (family,adr,adrlen,1,&len);
+ if (!strcmp (myClientAddr,ip_sockaddrtostring (sadr))) ret = LONGT;
+ fs_give ((void **) &sadr); /* done with client sockaddr */
+ }
+ return ret;
+}
+
+/* Following statement must be at end of this module */
+
+#undef fork /* undo any use of vfork() */
diff --git a/imap/src/osdep/unix/tcp_unix.h b/imap/src/osdep/unix/tcp_unix.h
new file mode 100644
index 00000000..d4b29797
--- /dev/null
+++ b/imap/src/osdep/unix/tcp_unix.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ char *remotehost; /* remote host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/unix/tenex.c b/imap/src/osdep/unix/tenex.c
new file mode 100644
index 00000000..eee61fba
--- /dev/null
+++ b/imap/src/osdep/unix/tenex.c
@@ -0,0 +1,1470 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *tmp);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+char *tenex_file (char *dst,char *name);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ tenex_status, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_fast, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = tenex_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!tenex_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt");
+ return ret; /* return success */
+}
+
+/* Tenex Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&tenexproto);
+ if (stream->local) fatal ("tenex recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!tenex_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ LOCAL->fd = fd; /* bind the file */
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch fast data
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = strcrlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return (char *) LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ MM_FLAGS (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,i);
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ tenex_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* Tenex mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void tenex_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\n",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* Tenex mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return LONGT;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (tenex_file(file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&tenexproto);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message))
+ ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+
+/* Tenex mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *tenex_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "mail.txt") : s;
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/truncate.c b/imap/src/osdep/unix/truncate.c
new file mode 100644
index 00000000..3dde1072
--- /dev/null
+++ b/imap/src/osdep/unix/truncate.c
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Truncate a file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 June 1994
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for ftruncate() call
+ * Accepts: file descriptor
+ * length
+ * Returns: 0 if success, -1 if failure
+ */
+
+int ftruncate (int fd,off_t length)
+{
+ struct flock fb;
+ fb.l_whence = 0;
+ fb.l_len = 0;
+ fb.l_start = length;
+ fb.l_type = F_WRLCK; /* write lock on file space */
+ return fcntl (fd,F_FREESP,&fb);
+}
diff --git a/imap/src/osdep/unix/tz_bsd.c b/imap/src/osdep/unix/tz_bsd.c
new file mode 100644
index 00000000..75ee624c
--- /dev/null
+++ b/imap/src/osdep/unix/tz_bsd.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ /* append timezone from tm struct */
+ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone);
+}
diff --git a/imap/src/osdep/unix/tz_nul.c b/imap/src/osdep/unix/tz_nul.c
new file mode 100644
index 00000000..d2dd2895
--- /dev/null
+++ b/imap/src/osdep/unix/tz_nul.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Null Time Zone String (unknown)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+}
diff --git a/imap/src/osdep/unix/tz_sv4.c b/imap/src/osdep/unix/tz_sv4.c
new file mode 100644
index 00000000..409adf14
--- /dev/null
+++ b/imap/src/osdep/unix/tz_sv4.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ tzset (); /* get timezone from TZ environment stuff */
+ sprintf (s + strlen (s)," (%.50s)",
+ tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]);
+}
diff --git a/imap/src/osdep/unix/unix.c b/imap/src/osdep/unix/unix.c
new file mode 100644
index 00000000..be3c437b
--- /dev/null
+++ b/imap/src/osdep/unix/unix.c
@@ -0,0 +1,2708 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "unix.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time (for mbox driver) */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+long unix_isvalid_fd (int fd);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* mbox mail routines */
+
+/* Function prototypes */
+
+DRIVER *mbox_valid (char *name);
+long mbox_create (MAILSTREAM *stream,char *mailbox);
+long mbox_delete (MAILSTREAM *stream,char *mailbox);
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbox_open (MAILSTREAM *stream);
+long mbox_ping (MAILSTREAM *stream);
+void mbox_check (MAILSTREAM *stream);
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* OK if mailbox format good */
+ if (unix_isvalid_fd (fd)) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* UNIX mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long unix_isvalid_fd (int fd)
+{
+ int zn;
+ int ret = NIL;
+ char tmp[MAILTMPLEN],*s,*t,c = '\n';
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
+ for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');)
+ c = *s++;
+ if (c == '\n') VALID (s,t,ret,zn);
+ }
+ return ret; /* return what we should */
+}
+
+
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ /* write the pseudo-header */
+ sprintf (s += strlen (s),
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = unix_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ unix_unlock (fd,NIL,&lockx);
+ unix_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ unix_unlock (fd,NIL,&lockx);
+ }
+ unix_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&unixproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,&lock,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && unix_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ DRIVER *d;
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ (d && strcmp (d->name,"mbox") && !(d->flags & DR_DISABLE));
+ d = d->next); /* see if mbox driver active */
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!((d && mbox_valid (mailbox) && (mailbox = "mbox")) ||
+ unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);/* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df); /* note that unix_unlock() released the fd */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out CRs (note also copies NUL) */
+ for (x = tmp; x = strchr (x,'\r'); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ MM_LOG (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ if ((i == 1) && (*s == '\n')) {
+ k = 2;
+ m = 1;
+ }
+ else if ((i == 2) && (*s == '\r') && (s[1] == '\n'))
+ k = m = 2;
+ else k = m = 0; /* file does not end with newline! */
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ /* flush ending blank line */
+ elt->private.msg.text.text.size -= m;
+ elt->rfc822_size -= k;
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ return strlen (hdr); /* return header length */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 1;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\n",1);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
+
+/* MBOX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mboxdriver = {
+ "mbox", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
+ (DRIVER *) NIL, /* next driver */
+ mbox_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* find mailboxes */
+ unix_lsub, /* find subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbox_create, /* create mailbox */
+ mbox_delete, /* delete mailbox */
+ mbox_rename, /* rename mailbox */
+ mbox_status, /* status of mailbox */
+ mbox_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbox_ping, /* ping mailbox to see if still alive */
+ mbox_check, /* check for new messages */
+ mbox_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ mbox_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mboxproto = {&mboxdriver};
+
+/* MBOX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbox_valid (char *name)
+{
+ /* only INBOX, mbox must exist */
+ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) &&
+ (unix_valid (sysinbox()) || !errno || (errno == ENOENT)))
+ return &mboxdriver;
+ return NIL; /* can't win (yet, anyway) */
+}
+
+/* MBOX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!compare_cstring (mailbox,"INBOX")) return unix_create (NIL,"mbox");
+ sprintf (tmp,"Can't create non-INBOX name as mbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+
+/* MBOX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbox_rename (stream,mailbox,NIL);
+}
+
+
+/* MBOX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char tmp[MAILTMPLEN];
+ long ret = unix_rename (stream,"~/mbox",newname);
+ /* recreate file if renamed INBOX */
+ if (ret) unix_create (NIL,"mbox");
+ else MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success */
+}
+
+/* MBOX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ if (!status.recent && /* calculate post-snarf results */
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBOX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbox_open (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ unsigned long recent = 0;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mboxproto;
+ /* change mailbox file name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr ("mbox");
+ /* open mailbox, snarf new mail */
+ if (!(unix_open (stream) && mbox_ping (stream))) return NIL;
+ stream->inbox = T; /* mark that this is an INBOX */
+ /* notify upper level of mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
+ mail_recent (stream,recent); /* including recent messages */
+ return stream;
+}
+
+/* MBOX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+static int snarfed = 0; /* number of snarfs */
+
+long mbox_ping (MAILSTREAM *stream)
+{
+ int sfd;
+ unsigned long size;
+ struct stat sbuf;
+ char *s;
+ DOTLOCK lock,lockx;
+ /* time to try snarf and sysinbox non-empty? */
+ if (LOCAL && !stream->rdonly && !stream->lock &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ !stat (sysinbox (),&sbuf) && sbuf.st_size) {
+ MM_CRITICAL (stream); /* yes, go critical */
+ /* open and lock sysinbox */
+ if ((sfd = unix_lock (sysinbox (),O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) >= 0) {
+ /* locked sysinbox in good format? */
+ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) ||
+ !unix_isvalid_fd (sfd)) {
+ sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format",
+ sysinbox ());
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sysinbox good, parse and excl-lock mbox */
+ else if (unix_parse (stream,&lock,LOCK_EX)) {
+ lseek (sfd,0,L_SET); /* read entire sysinbox into memory */
+ read (sfd,s = (char *) fs_get (size + 1),size);
+ s[size] = '\0'; /* tie it off */
+ /* append to end of mbox */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+
+ /* copy to mbox */
+ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) {
+ sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ }
+ /* sysinbox better not have changed */
+ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) {
+ sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu",
+ sysinbox (),size,(unsigned long) sbuf.st_size);
+ MM_LOG (LOCAL->buf,ERROR);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ /* Believe it or not, a Singaporean government system actually had
+ * symlinks from /var/mail/user to ~user/mbox. To compound this
+ * error, they used an SVR4 system; BSD and OSF locks would have
+ * prevented it but not SVR4 locks.
+ */
+ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size))
+ syslog (LOG_ALERT,"File %s and %s are the same file!",
+ sysinbox (),stream->mailbox);
+ }
+ else { /* data copied OK */
+ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */
+ if (!snarfed++) { /* have we snarfed before? */
+ /* syslog if server, else user log */
+ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s",
+ size,stream->mailbox,sysinbox ());
+ if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ "unknown"))
+ syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ());
+ else MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ /* done with sysinbox text */
+ fs_give ((void **) &s);
+ /* all done with mbox */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ /* all done with sysinbox */
+ unix_unlock (sfd,NIL,&lockx);
+ }
+ MM_NOCRITICAL (stream); /* done with critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ return unix_ping (stream); /* do the unix routine now */
+}
+
+/* MBOX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mbox_check (MAILSTREAM *stream)
+{
+ /* do local ping, then do unix routine */
+ if (mbox_ping (stream)) unix_check (stream);
+}
+
+
+/* MBOX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = unix_expunge (stream,sequence,options);
+ mbox_ping (stream); /* do local ping */
+ return ret;
+}
+
+
+/* MBOX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ if (mbox_valid (mailbox)) return unix_append (stream,"mbox",af,data);
+ sprintf (tmp,"Can't append to that name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/unix.h b/imap/src/osdep/unix/unix.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/unix/unix.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/unix/utime.c b/imap/src/osdep/unix/utime.c
new file mode 100644
index 00000000..6fef2d36
--- /dev/null
+++ b/imap/src/osdep/unix/utime.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD utime() emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 October 1996
+ * Last Edited: 30 August 2006
+ */
+
+#undef utime
+
+/* Portable utime() that takes it args like real Unix systems
+ * Accepts: file path
+ * traditional utime() argument
+ * Returns: utime() results
+ */
+
+int portable_utime (char *file,time_t timep[2])
+{
+ struct utimbuf times;
+ /* in case there's other cruft there */
+ memset (&times,0,sizeof (struct utimbuf));
+ times.actime = timep[0]; /* copy the portable values */
+ times.modtime = timep[1];
+ return utime (file,&times); /* now call the SVR4 routine */
+}
diff --git a/imap/src/osdep/unix/write.c b/imap/src/osdep/unix/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/unix/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/vms/build.com b/imap/src/osdep/vms/build.com
new file mode 100644
index 00000000..aedbcc82
--- /dev/null
+++ b/imap/src/osdep/vms/build.com
@@ -0,0 +1,99 @@
+$! ========================================================================
+$! Copyright 1988-2006 University of Washington
+$!
+$! Licensed under the Apache License, Version 2.0 (the "License");
+$! you may not use this file except in compliance with the License.
+$! You may obtain a copy of the License at
+$!
+$! http://www.apache.org/licenses/LICENSE-2.0
+$!
+$!
+$! ========================================================================
+$!
+$! Program: Portable c-client build for VMS
+$!
+$! Author: Mark Crispin
+$! Networks and Distributed Computing
+$! Computing & Communications
+$! University of Washington
+$! Administration Building, AG-44
+$! Seattle, WA 98195
+$! Internet: MRC@CAC.Washington.EDU
+$!
+$! Date: 2 August 1994
+$! Last Edited: 30 August 2006
+$!
+$! Change this to your local timezone. This value is the number of minutes
+$! east of UTC (formerly known as GMT). Sample values: -300 (US east coast),
+$! -480 (US west coast), 540 (Japan), 60 (western Europe).
+$! VAX C's HELP information says that you should be able to use gmtime(), but
+$! it returns 0 for the struct. ftime(), you ask? It, too, returns 0 for a
+$! timezone. Nothing sucks like a VAX!
+$!
+$ CC_TIMEZONE=-480
+$!
+$! CC options
+$!
+$ CC_PREF = "/OPTIMIZE/INCLUDE=[]"
+$ CC_PREF = CC_PREF + "/DEFINE=net_getbuffer=NET_GETBUF"
+$ CC_PREF = CC_PREF + "/DEFINE=LOCALTIMEZONE='CC_TIMEZONE'"
+$!
+$! Determine TCP type
+$!
+$ TCP_TYPE = "VMSN" ! default to none
+$ IF F$LOCATE("MULTINET", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ DEFINE SYS MULTINET_ROOT:[MULTINET.INCLUDE.SYS],sys$library
+$ DEFINE NETINET MULTINET_ROOT:[MULTINET.INCLUDE.NETINET]
+$ DEFINE ARPA MULTINET_ROOT:[MULTINET.INCLUDE.ARPA]
+$ TCP_TYPE = "VMSM" ! Multinet
+$ LINK_OPT = ",LINK_MNT/OPTION"
+$ ENDIF
+$ IF F$LOCATE("NETLIB", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ DEFINE SYS SYS$LIBRARY: ! normal .H location
+$ DEFINE NETINET SYS$LIBRARY:
+$ DEFINE ARPA SYS$LIBRARY:
+$ LINK_OPT = ",LINK_NLB/OPTION"
+$ TCP_TYPE = "VMSL" ! NETLIB
+$ ENDIF
+$ IF TCP_TYPE .EQS. "VMSN"
+$ THEN
+$ DEFINE SYS SYS$LIBRARY: ! normal .H location
+$ DEFINE NETINET SYS$LIBRARY:
+$ DEFINE ARPA SYS$LIBRARY:
+$ LINK_OPT = ""
+$ ENDIF
+$!
+$ COPY TCP_'TCP_TYPE'.C TCP_VMS.C;
+$!
+$ COPY OS_VMS.H OSDEP.H;
+$ SET VERIFY
+$ CC'CC_PREF' MAIL
+$ CC'CC_PREF' IMAP4R1
+$ CC'CC_PREF' SMTP
+$ CC'CC_PREF' NNTP
+$ CC'CC_PREF' POP3
+$ CC'CC_PREF' DUMMYVMS
+$ CC'CC_PREF' RFC822
+$ CC'CC_PREF' MISC
+$ CC'CC_PREF' OS_VMS
+$ CC'CC_PREF' SMANAGER
+$ CC'CC_PREF' FLSTRING
+$ CC'CC_PREF' NEWSRC
+$ CC'CC_PREF' NETMSG
+$ CC'CC_PREF' UTF8
+$ CC'CC_PREF' UTF8AUX
+$ CC'CC_PREF' MTEST
+$ CC'CC_PREF' MAILUTIL
+$!
+$ LINK MTEST,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,-
+ UTF8AUX,SMANAGER,FLSTRING,NEWSRC,NETMSG,-
+ SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION
+PSECT=_CTYPE_,NOWRT
+$ LINK MAILUTIL,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,-
+ UTF8AUX,SMANAGER,FLSTRING,NEWSRC,NETMSG,-
+ SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION
+PSECT=_CTYPE_,NOWRT
+$ SET NOVERIFY
+$ EXIT
diff --git a/imap/src/osdep/vms/clean.com b/imap/src/osdep/vms/clean.com
new file mode 100644
index 00000000..98df0db5
--- /dev/null
+++ b/imap/src/osdep/vms/clean.com
@@ -0,0 +1,26 @@
+$! ========================================================================
+$! Copyright 1988-2006 University of Washington
+$!
+$! Licensed under the Apache License, Version 2.0 (the "License");
+$! you may not use this file except in compliance with the License.
+$! You may obtain a copy of the License at
+$!
+$! http://www.apache.org/licenses/LICENSE-2.0
+$!
+$!
+$! ========================================================================
+$!
+$! Program: Portable c-client cleanup for VMS
+$!
+$! Author: Mark Crispin
+$! Networks and Distributed Computing
+$! Computing & Communications
+$! University of Washington
+$! Administration Building, AG-44
+$! Seattle, WA 98195
+$! Internet: MRC@CAC.Washington.EDU
+$!
+$! Date: 14 June 1995
+$! Last Edited: 30 August 2006
+$!
+$ DELETE *.OBJ;*,OSDEP.*;*,TCP_VMS.C;*,MTEST.EXE;*,MAILUTIL.EXE;*
diff --git a/imap/src/osdep/vms/dummy.h b/imap/src/osdep/vms/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/vms/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/vms/dummyvms.c b/imap/src/osdep/vms/dummyvms.c
new file mode 100644
index 00000000..cfcb45d0
--- /dev/null
+++ b/imap/src/osdep/vms/dummyvms.c
@@ -0,0 +1,295 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for VMS
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/vms/env_vms.c b/imap/src/osdep/vms/env_vms.c
new file mode 100644
index 00000000..56b7a223
--- /dev/null
+++ b/imap/src/osdep/vms/env_vms.c
@@ -0,0 +1,174 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_USERNAME:
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s:.newsrc",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ */
+
+static void do_date (char *date,char *prefix,char *fmt)
+{
+ time_t tn = time (0);
+ struct tm *t = localtime (&tn);
+ int zone = LOCALTIMEZONE + (t->tm_isdst ? 60 : 0);
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d");
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d");
+}
+
+/* Return my user name
+ * Returns: my user name
+ */
+
+char *myusername ()
+{
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+
+ if (!myUserName) { /* get user name if don't have it yet */
+ myUserName = cpystr (cuserid (NIL));
+ myHomeDir = cpystr ("SYS$LOGIN");
+ }
+ return myUserName;
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir;
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ return NIL; /* no default prototype */
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/vms/env_vms.h b/imap/src/osdep/vms/env_vms.h
new file mode 100644
index 00000000..59160a06
--- /dev/null
+++ b/imap/src/osdep/vms/env_vms.h
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *myusername ();
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/vms/fs_vms.c b/imap/src/osdep/vms/fs_vms.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/vms/fs_vms.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/vms/ftl_vms.c b/imap/src/osdep/vms/ftl_vms.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/vms/ftl_vms.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/vms/link.opt b/imap/src/osdep/vms/link.opt
new file mode 100644
index 00000000..0f454490
--- /dev/null
+++ b/imap/src/osdep/vms/link.opt
@@ -0,0 +1 @@
+SYS$SHARE:VAXCRTL/SHARE
diff --git a/imap/src/osdep/vms/link_mnt.opt b/imap/src/osdep/vms/link_mnt.opt
new file mode 100644
index 00000000..5eb99104
--- /dev/null
+++ b/imap/src/osdep/vms/link_mnt.opt
@@ -0,0 +1 @@
+MULTINET:MULTINET_SOCKET_LIBRARY/SHARE
diff --git a/imap/src/osdep/vms/link_nlb.opt b/imap/src/osdep/vms/link_nlb.opt
new file mode 100644
index 00000000..883d2859
--- /dev/null
+++ b/imap/src/osdep/vms/link_nlb.opt
@@ -0,0 +1 @@
+NETLIB_SHR/SHARE
diff --git a/imap/src/osdep/vms/linkage.c b/imap/src/osdep/vms/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/vms/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/vms/linkage.h b/imap/src/osdep/vms/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/vms/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/vms/nl_vms.c b/imap/src/osdep/vms/nl_vms.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/vms/nl_vms.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/vms/os_vms.c b/imap/src/osdep/vms/os_vms.c
new file mode 100644
index 00000000..cd333ba9
--- /dev/null
+++ b/imap/src/osdep/vms/os_vms.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VMS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_vms.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "misc.h"
+
+
+#include "fs_vms.c"
+#include "ftl_vms.c"
+#include "nl_vms.c"
+#include "env_vms.c"
+#include "tcp_vms.c"
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() "" /* dummy definition to prevent build errors */
+#define MD5ENABLE ""
+
+#include "auth_md5.c"
+#include "auth_pla.c"
+#include "auth_log.c"
+
+
+/* Emulator for UNIX getpass() call
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ char *s;
+ static char pwd[PWDLEN];
+ fputs (prompt,stdout);
+ fgets (pwd,PWDLEN-1,stdin);
+ if (s = strchr (pwd,'\n')) *s = '\0';
+ return pwd;
+}
diff --git a/imap/src/osdep/vms/os_vms.h b/imap/src/osdep/vms/os_vms.h
new file mode 100644
index 00000000..1da72f13
--- /dev/null
+++ b/imap/src/osdep/vms/os_vms.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VMS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 January 2007
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unixio.h>
+#include <file.h>
+#include <stat.h>
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#include "env_vms.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#define gethostid clock
+#define random rand
+#define unlink delete
+
+char *getpass (const char *prompt);
+
+#define strtok_r(a,b,c) strtok(a,b)
diff --git a/imap/src/osdep/vms/pmatch.c b/imap/src/osdep/vms/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/vms/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/vms/tcp_vms.h b/imap/src/osdep/vms/tcp_vms.h
new file mode 100644
index 00000000..ff747016
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vms.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/vms/tcp_vmsl.c b/imap/src/osdep/vms/tcp_vmsl.c
new file mode 100644
index 00000000..692c9212
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsl.c
@@ -0,0 +1,378 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines for Netlib.
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 13 January 2008
+ */
+
+/* Thanks to Yehavi Bourvine at The Hebrew University of Jerusalem who
+ contributed the original VMS code */
+
+#include <descrip.h>
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ unsigned long sock;
+ int status;
+ char tmp[MAILTMPLEN];
+ /* hostname to connect to */
+ struct dsc$descriptor HostDesc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
+ port &= 0xffff; /* erase flags */
+ /* assign a local socket */
+ if (!((status = net_assign (&sock)) & 0x1)) {
+ sprintf (tmp,"Unable to assign to net, status=%d",status);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if (!((status = net_bind (&sock,1)) & 0x1)) {
+ sprintf (tmp,"Unable to create local socket, status=%d",status);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* open connection */
+ HostDesc.dsc$w_length = strlen (host);
+ HostDesc.dsc$a_pointer = host;
+ if (!((status = tcp_connect (&sock,&HostDesc,port)) & 0x1)) {
+ sprintf (tmp,"Can't connect to %.80s,%lu: %s",host,port,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (host); /* copy official host name */
+ /* copy local host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* copy port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ int i,status;
+ /* Note: the doc says we need here dynamic descriptor, but we need static
+ * one... */
+ struct dsc$descriptor BufDesc = {BUFLEN,DSC$K_DTYPE_T,DSC$K_CLASS_S,
+ stream->ibuf};
+ static short iosb[4];
+ if (stream->tcpsi < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ if (!((status = tcp_receive(&(stream->tcpsi), &BufDesc, iosb)) & 0x1)) {
+ sprintf (tmp,"Error reading from TcpIp/NETLIB, status=%d",status);
+ mm_log (tmp,ERROR);
+ return tcp_abort (stream);
+ }
+ if (iosb[1] > BUFLEN) i = BUFLEN;
+ else i = iosb[1];
+ if (i < 1) return tcp_abort (stream);
+ stream->ictr = i; /* set new byte count */
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int status;
+ struct dsc$descriptor_s BufDesc = {strlen(string),DSC$K_DTYPE_T,
+ DSC$K_CLASS_S,string };
+ /* 2 = Do not add \r\n */
+ return ((status = tcp_send (&(stream->tcpso),&BufDesc,2)) & 0x1) ? T :
+ tcp_abort (stream);
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ /* nuke the socket */
+ tcp_disconnect (&(stream->tcpsi));
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ int status;
+ char tmp[MAILTMPLEN];
+ if (!myLocalHost) { /* have local host yet? */
+ /* receives local host name */
+ struct dsc$descriptor LocalhostDesc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,NULL};
+ if (!((status = net_get_hostname (&LocalhostDesc)) & 0x1)) {
+ sprintf (tmp,"Can't get local hostname, status=%d",status);
+ mm_log (tmp,ERROR);
+ return "UNKNOWN";
+ }
+ strncpy (tmp,LocalhostDesc.dsc$a_pointer,LocalhostDesc.dsc$w_length);
+ tmp[LocalhostDesc.dsc$w_length] = '\0';
+ str$free1_dx (&LocalhostDesc);
+ myLocalHost = cpystr (tmp);
+ }
+ return myLocalHost;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/vms/tcp_vmsm.c b/imap/src/osdep/vms/tcp_vmsm.c
new file mode 100644
index 00000000..bc9d9ca7
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsm.c
@@ -0,0 +1,479 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines for Multinet
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 13 January 2008
+ */
+
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int sock;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *host_name;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct protoent *pt = getprotobyname ("tcp");
+ struct servent *sv = NIL;
+ port &= 0xffff; /* erase flags */
+ if (service) { /* service specified? */
+ if (*service == '*') { /* yes, special alt driver kludge? */
+ sv = getservbyname (service + 1,"tcp");
+ }
+ else sv = getservbyname (service,"tcp");
+ }
+ /* user service name port */
+ if (sv) port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons (port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (hostname,host+1); /* yes, copy number part */
+ hostname[(strlen (hostname))-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host); /* hostname is user's argument */
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ else { /* lookup host name, note that brain-dead Unix
+ requires lowercase! */
+ strcpy (hostname,host); /* in case host is in write-protected memory */
+ if ((host_name = gethostbyname (lcase (hostname)))) {
+ /* copy address type */
+ sin.sin_family = host_name->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,host_name->h_name);
+ /* copy host addresses */
+ memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length);
+ }
+ else {
+ sprintf (tmp,"No such host as %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+#if 0
+ /* Maybe this test is necessary. It depends upon how VMS implements
+ * fd_set. UNIX-style fd_set needs it; Windows-style does not.
+ */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *)&sin,sizeof (sin)) < 0) {
+ sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ /* get local name */
+ gethostname (tmp,MAILTMPLEN-1);
+ stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ?
+ host_name->h_name : tmp);
+ /* init sockets */
+ stream->port = port; /* port number */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcpsi < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (getdtablesize (),&fds,0,&efds,ttmo_read ? &tmo:0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = socket_read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcpso < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (getdtablesize (),0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = socket_write (stream->tcpso,string,size)) < 0) &&
+ (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ int i;
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ /* nuke the socket */
+ socket_close (stream->tcpsi);
+ if (stream->tcpsi != stream->tcpso) socket_close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *hn;
+ if (!myLocalHost) { /* have local host yet? */
+ gethostname(tmp,MAILTMPLEN);/* get local host name */
+ myLocalHost = cpystr ((hn = gethostbyname (tmp)) ? hn->h_name : tmp);
+ }
+ return myLocalHost;
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char host[MAILTMPLEN];
+ struct hostent *he;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ /* note that Unix requires lowercase! */
+ else return (he = gethostbyname (lcase (strcpy (host,name)))) ?
+ he->h_name : name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/vms/tcp_vmsn.c b/imap/src/osdep/vms/tcp_vmsn.c
new file mode 100644
index 00000000..4648e48d
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsn.c
@@ -0,0 +1,222 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy VMS TCP/IP routines for non-TCP/IP systems
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ char tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ if (port) sprintf (tmp,"Can't connect to %.80s,%d: no TCP",host,port);
+ else sprintf (tmp,"Can't connect to %.80s,%s: no TCP",host,service);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP/IP receive line
+ * Accepts: TCP/IP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ return NIL;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return NIL;
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ return NIL;
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return 0xffffffff; /* return port number */
+}
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ /* have local host yet? */
+ if (!myLocalHost) myLocalHost = cpystr (getenv ("SYS$NODE"));
+ return myLocalHost;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/wce/drivers.bat b/imap/src/osdep/wce/drivers.bat
new file mode 100644
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/wce/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/wce/drivraux.bat b/imap/src/osdep/wce/drivraux.bat
new file mode 100644
index 00000000..30649a78
--- /dev/null
+++ b/imap/src/osdep/wce/drivraux.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C
+if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/wce/dummy.h b/imap/src/osdep/wce/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/wce/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/wce/dummywce.c b/imap/src/osdep/wce/dummywce.c
new file mode 100644
index 00000000..2440be27
--- /dev/null
+++ b/imap/src/osdep/wce/dummywce.c
@@ -0,0 +1,301 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for WCE
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <direct.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+ /* driver parameters */
+static char *file_extension = NIL;
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') &&
+ (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return value;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/wce/env_wce.c b/imap/src/osdep/wce/env_wce.c
new file mode 100644
index 00000000..ea2dbd98
--- /dev/null
+++ b/imap/src/osdep/wce/env_wce.c
@@ -0,0 +1,301 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: WCE environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myClientHost = NIL;/* client host name */
+static char *myServerHost = NIL;/* server host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *sysInbox = NIL; /* system inbox name */
+static long list_max_level = 5; /* maximum level of list recursion */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,NIL,NIL};
+static long alarm_countdown = 0;/* alarm count down */
+static void (*alarm_rang) (); /* alarm interrupt function */
+static unsigned int rndm = 0; /* initial `random' number */
+
+
+/* Dummy definitions to prevent errors */
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() ""
+#define MD5ENABLE "\\.nosuch.."
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) sprintf (date + strlen (date)," (%s)",tz);
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+/* Return default drive
+ * Returns: default drive
+ */
+
+static char *defaultDrive (void)
+{
+ char *s;
+ return ((s = getenv ("SystemDrive")) && *s) ? s : "C:";
+}
+
+
+/* Return home drive from environment variables
+ * Returns: home drive
+ */
+
+static char *homeDrive (void)
+{
+ char *s;
+ return ((s = getenv ("HOMEDRIVE")) && *s) ? s : defaultDrive ();
+}
+
+
+/* Return home path from environment variables
+ * Accepts: path to write into
+ * Returns: home path or NIL if it can't be determined
+ */
+
+static char *homePath (char *path)
+{
+ int i;
+ char *s;
+ if (!((s = getenv ("HOMEPATH")) && (i = strlen (s)))) return NIL;
+ if (((s[i-1] == '\\') || (s[i-1] == '/'))) s[i-1] = '\0';
+ sprintf (path,"%s%s",homeDrive (),s);
+ return path;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ char tmp[MAILTMPLEN];
+ /* initialize if first time */
+ if (!myHomeDir) myHomeDir = homePath (tmp);
+ return myHomeDir ? myHomeDir : homeDrive ();
+}
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s\\INBOX",myhomedir ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *dir = myhomedir ();
+ *dst = '\0'; /* default to empty string */
+ if (((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) name = NIL;
+ /* reject namespace names or names with / */
+ if (name && ((*name == '#') || strchr (name,'/'))) return NIL;
+ else if (!name) return dst; /* driver selects the INBOX name */
+ /* absolute path name? */
+ else if ((*name == '\\') || (name[1] == ':')) return strcpy (dst,name);
+ /* build resulting name */
+ sprintf (dst,"%s\\%s",dir,name);
+ return dst; /* return it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM CREATEPROTO,APPENDPROTO;
+ return type ? &APPENDPROTO : &CREATEPROTO;
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/wce/env_wce.h b/imap/src/osdep/wce/env_wce.h
new file mode 100644
index 00000000..e3c6c7f0
--- /dev/null
+++ b/imap/src/osdep/wce/env_wce.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: WCE environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ())
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+static char *defaultDrive (void);
+static char *homeDrive (void);
+static char *homePath (char *path);
+char *sysinbox ();
+long random ();
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl);
+unsigned long unix_crlflen (STRING *s);
+#define getpid random
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/wce/fs_wce.c b/imap/src/osdep/wce/fs_wce.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/wce/fs_wce.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/wce/ftl_wce.c b/imap/src/osdep/wce/ftl_wce.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/wce/ftl_wce.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/wce/makefile.wce b/imap/src/osdep/wce/makefile.wce
new file mode 100644
index 00000000..cdf8198d
--- /dev/null
+++ b/imap/src/osdep/wce/makefile.wce
@@ -0,0 +1,96 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- WCE version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 30 August 2006
+
+
+EXTRAAUTHENTICATORS=
+DEFAULTAUTHENTICATORS=ext md5 pla log
+EXTRADRIVERS =
+DRIVERS = imap nntp pop3
+DEFAULTDRIVER = dummy
+CFLAGS= /MT /W3 /D_SH3_ -nologo $(EXTRACFLAGS)
+CC = shcl
+CCLIENTLIB= cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_wce.h
+ copy os_wce.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(DEFAULTDRIVER) $(DEFAULTDRIVER)
+ mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h pop3.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_wce.obj: mail.h osdep.h env_wce.h fs.h ftl.h nl.h tcp.h tcp_wce.h \
+ os_wce.c fs_wce.c ftl_wce.c nl_wce.c env_wce.c tcp_wce.c \
+ auth_md5.c auth_log.c pmatch.c
+
+dummywce.obj: mail.h dummy.h misc.h osdep.h dummywce.c
+
+$(CCLIENTLIB): mail.obj misc.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \
+ dummywce.obj
+ erase $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \
+ dummywce.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/wce/mkautaux.bat b/imap/src/osdep/wce/mkautaux.bat
new file mode 100644
index 00000000..c65022d2
--- /dev/null
+++ b/imap/src/osdep/wce/mkautaux.bat
@@ -0,0 +1,31 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/wce/mkauths.bat b/imap/src/osdep/wce/mkauths.bat
new file mode 100644
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/wce/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/wce/nl_wce.c b/imap/src/osdep/wce/nl_wce.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/wce/nl_wce.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/wce/os_wce.c b/imap/src/osdep/wce/os_wce.c
new file mode 100644
index 00000000..7cf3ffe7
--- /dev/null
+++ b/imap/src/osdep/wce/os_wce.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- WCE version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_wce.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <winsock.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+
+#include "fs_wce.c"
+#include "ftl_wce.c"
+#include "nl_wce.c"
+#include "env_wce.c"
+#include "tcp_wce.c"
+#include "auths.c"
diff --git a/imap/src/osdep/wce/os_wce.h b/imap/src/osdep/wce/os_wce.h
new file mode 100644
index 00000000..45bd76a1
--- /dev/null
+++ b/imap/src/osdep/wce/os_wce.h
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- WCE version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+ /* missing in string.h */
+_CRTIMP char * __cdecl strpbrk (const char *, const char *);
+_CRTIMP char * __cdecl strrchr(const char *, int);
+#include <sys\types.h>
+#undef ERROR
+#include <windows.h>
+#undef ERROR
+#define ERROR (long) 2 /* must match mail.h */
+#include <time.h>
+#include <io.h>
+
+#define gethostid clock
+#define WSA_VERSION ((1 << 8) | 1)
+
+#include "env_wce.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/wce/pmatch.c b/imap/src/osdep/wce/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/wce/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/wce/setproto.bat b/imap/src/osdep/wce/setproto.bat
new file mode 100644
index 00000000..ce7cb1ef
--- /dev/null
+++ b/imap/src/osdep/wce/setproto.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Set default prototype for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 9 October 1995
+REM Last Edited: 30 August 2006
+
+REM Set the default drivers
+ECHO #define CREATEPROTO %1proto >> LINKAGE.H
+ECHO #define APPENDPROTO %2proto >> LINKAGE.H
diff --git a/imap/src/osdep/wce/tcp_wce.c b/imap/src/osdep/wce/tcp_wce.c
new file mode 100644
index 00000000..90e206a9
--- /dev/null
+++ b/imap/src/osdep/wce/tcp_wce.c
@@ -0,0 +1,818 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons ((u_short) port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(he = gethostbyname (lcase (strcpy (tmp,host)))))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* error? */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock); /* do possible cleanup action */
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port)
+{
+ int sock;
+ char *s;
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError());
+ return -1;
+ }
+ wsa_sock_open++; /* count this socket as open */
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) ==
+ SOCKET_ERROR) {
+ switch (WSAGetLastError ()){/* analyze error */
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s,
+ WSAGetLastError ());
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t tc,t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) <
+ 0) && (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ struct timeval tmo;
+ int i;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ stream->ictr = i; /* set new byte count */
+ /* point at TCP buffer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ /* block and write */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpso+1,NULL,&fds,NULL,
+ tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) &&
+ (WSAGetLastError () == WSAEINTR));
+ else while (((i = write (stream->tcpso,string,
+ min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ if (i == SOCKET_ERROR) return tcp_abort (stream);
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ string += i;
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0))) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T);
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that NT requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
+ (*bn) (BLOCK_NONE,NIL);
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/wce/tcp_wce.h b/imap/src/osdep/wce/tcp_wce.h
new file mode 100644
index 00000000..4019af10
--- /dev/null
+++ b/imap/src/osdep/wce/tcp_wce.h
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcpsi; /* input tcp socket */
+ int tcpso; /* output tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/tmail/Makefile b/imap/src/tmail/Makefile
new file mode 100644
index 00000000..ce221430
--- /dev/null
+++ b/imap/src/tmail/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: tmail Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 April 1993
+# Last Edited: 10 September 2007
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+tmail: $(CCLIENTLIB) tmail.o tquota.o
+ $(CC) $(CFLAGS) -o tmail tmail.o tquota.o $(LDFLAGS)
+
+tmail.o: $C/mail.h $C/misc.h $C/osdep.h tquota.h
+
+tquota.o: tquota.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o tmail
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/tmail/tmail.1 b/imap/src/tmail/tmail.1
new file mode 100644
index 00000000..a34d4bb9
--- /dev/null
+++ b/imap/src/tmail/tmail.1
@@ -0,0 +1,205 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH TMAIL 1 "September 27, 2007"
+.SH NAME
+tmail \- Mail Delivery Module
+.nh
+.SH SYNOPSIS
+.B tmail
+.I [-b format] [\-D] [-f from_name] [\-I inbox_specifier] user[+folder] ...
+.SH DESCRIPTION
+.I tmail
+delivers mail to a user's INBOX or a designated folder.
+.I tmail
+may be configured as a drop-in replacement for
+.IR binmail (1),
+.IR mail.local (1)
+or any program intended for use for mail delivery by a mail delivery program
+such as
+.IR sendmail (8).
+.PP
+.I tmail
+is intended to be used for direct delivery by the mailer daemon;
+.IR dmail (1)
+is the preferred tool for user applications, e.g. a mail delivery
+filter such as
+.IR procmail (1) .
+If
+.I tmail
+is used for a user application,
+then the calling program must be aware of the restrictions noted below.
+.PP
+When
+.I tmail
+exits, it returns exit status values to enable the mail delivery program
+to determine whether a message was delivered successfully or had a
+temporary (requeue for later delivery) or permanent (return to sender)
+failure.
+.PP
+If the
+.I +folder
+extension is included in the user argument,
+.I tmail
+will attempt to deliver to the designated folder. If the folder does not
+exist or the extension is not included, the message is delivered to the
+user's INBOX.
+If delivery is to INBOX and no INBOX currently exists,
+.I tmail
+will create a new INBOX, using the \fB-I\fR or \fB-b\fR flag if specified.
+.I tmail
+recognizes the format of an existing INBOX or folder, and appends the new
+message in that format.
+.PP
+The \fB-b\fR flag specifies a format to create INBOX if INBOX does not
+already exist. This flag requires privileges, and can not be used with
+\fB-I\fR. The argument is
+a format name such as mix, mbx, etc.
+.PP
+The \fB-D\fR flag specifies debugging; this enables additional message
+telemetry.
+.PP
+The \fB-f\fR or \fB-r\fR flag is used by
+the mail delivery program to specify a Return-Path. The header
+.br
+ Return-Path: <\fIfrom_name\fR>
+.br
+is prepended to the message before delivery.
+.PP
+The \fB-I\fR flag is used by the mail delivery program
+to specify an alternative INBOX name. This flag requires privileges,
+and can not be used with \fB-b\fR. This affects the location and format
+of INBOX. If specified, it should be in one of three forms:
+.sp
+The first form of argument to \fB-I\fR is the string "INBOX", which
+means to write to the system default inbox using the system default
+mailbox format. These system defaults are defined when the c-client
+library is built.
+.sp
+The second form of argument to \fB-I\fR is a delivery specification,
+consisting of "#driver.", a c-client mailbox format driver name, "/",
+and a file name. This will write to the specified file in the
+specified format. For example, #driver.mbx/INBOX will write to file
+"INBOX" in the home directory in mbx format; and
+#driver.unix/mail/incoming will write to file "incoming" in the
+user's "mail" subdirectory in unix (default UNIX) format.
+.sp
+The third form of argument to \fB-I\fR is any other name. Normally,
+this will write to the specified file on the user's home directory in
+the specified format. However, certain names are special. These are:
+.PP
+.nf
+ value equivalant to
+ ----- -------------
+ INBOX.MTX #driver.mtx/INBOX.MTX
+ mbox #driver.unix/mbox
+ mail.txt #driver.tenex/mail.txt
+.fi
+.PP
+If \fB-I\fR is not specified, the default action is \fB-I INBOX\fR.
+.PP
+If multiple recipients are specified on the command line,
+.I tmail
+spawns one child process per recipient to perform actual delivery. This
+way of calling
+.I tmail
+is not recommended; see below under
+.IR RESTRICTIONS .
+.SH INSTALLATION
+If
+.I tmail
+is to be used for mail delivery from the mail delivery program, it
+.I must
+be installed setuid root.
+.sp
+If sendmail is the mail delivery program,
+.I tmail
+is invoked from sendmail.cf. Look for the "Mlocal" line, and substitute
+the path name for the
+.I tmail
+binary in place of /bin/mail, /usr/lib/mail.local, etc. You should also
+add the flag to invoke
+.I tmail
+with CRLF style newlines; this is usually done with E=\\r\\n in the Mlocal
+line.
+.sp
+Here is an example of an Mlocal line in sendmail version 8:
+.sp
+.nf
+Mlocal, P=/usr/local/etc/tmail, F=lsDFMAw5:/|@qPrn+,
+ S=10/30, R=20/40, E=\\r\\n, T=DNS/RFC822/X-Unix,
+ A=tmail $u
+.fi
+.PP
+If
+.I tmail
+is to be called with the \fB-I\fR flag, it must be invoked with both
+real and effective UID root. Many sendmail configurations invoke the
+local mailer as the sending user when that user is local, which
+will prevent \fB-b\fR or \fB-I\fR from working.
+.SH SECURITY CONSIDERATIONS
+If
+.I tmail
+is invoked by an ordinary user, the Received: header line will
+indicate the name or UID of the user that invoked it.
+.PP
+Ordinary users are not permitted to use the \fB-b\fR or \fB-I\fR flag since
+otherwise a user could create any file on another user's directory.
+.PP
+.I tmail
+can deliver mail to home directories. In addition,
+.I tmail
+can be used to deliver mail to other mail folders in a home directory
+or an inferior directory of a home directory.
+.SH RESTRICTIONS
+The calling program should invoke
+.I tmail
+with CRLF newlines, otherwise
+.I tmail
+will complain in syslog.
+.PP
+Absolute pathnames and
+.I ~user
+specifications are not permitted in
+.I +folder
+extensions.
+.PP
+Ordinary users are not permitted to use the \fB-I\fR flag.
+.PP
+IMAP4 namespace names are not yet supported in
+.I +folder
+extensions.
+.PP
+It is not possible to use
+.I tmail
+to deliver to
+.IR mh (1)
+format mailboxes.
+.PP
+If delivery to multiple users is specified and delivery to any single user
+fails, the entire delivery will be reported as having failed, even though
+delivery to other users may have succeeded. If
+.I tmail
+is used for mail delivery from
+.IR sendmail (8),
+a separate tmail invocation should be done for each user. Otherwise a
+delivery failure for a single user in a message going to multiple users
+will cause multiple deliveries to all the other users every time
+.IR sendmail (8),
+retries.
+.SH AUTHOR
+Mark Crispin, MRC@CAC.Washington.EDU
+.SH "SEE ALSO"
+binmail(1)
+.br
+sendmail(8)
diff --git a/imap/src/tmail/tmail.c b/imap/src/tmail/tmail.c
new file mode 100644
index 00000000..ed5fc589
--- /dev/null
+++ b/imap/src/tmail/tmail.c
@@ -0,0 +1,800 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 April 1993
+ * Last Edited: 30 October 2008
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "c-client.h"
+#include "tquota.h"
+
+
+/* Globals */
+
+char *version = "22"; /* tmail edit version */
+int debug = NIL; /* debugging (don't fork) */
+int trycreate = NIL; /* flag saying gotta create before appending */
+int critical = NIL; /* flag saying in critical code */
+char *sender = NIL; /* message origin */
+char *inbox = NIL; /* inbox file */
+long precedence = 0; /* delivery precedence - used by quota hook */
+DRIVER *format = NIL; /* desired format */
+
+
+/* Function prototypes */
+
+void file_string_init (STRING *s,void *data,unsigned long size);
+char file_string_next (STRING *s);
+void file_string_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+int deliver (FILE *f,unsigned long msglen,char *user);
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp);
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
+int fail (char *string,int code);
+char *getusername (char *s,char **t);
+
+
+/* File string driver for file stringstructs */
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Cache buffer for file stringstructs */
+
+#define CHUNKLEN 16384
+char chunk[CHUNKLEN];
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note fd */
+ s->size = size; /* note size */
+ s->chunk = chunk;
+ s->chunksize = (unsigned long) CHUNKLEN;
+ SETPOS (s,0); /* set initial position */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char file_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void file_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ fseek ((FILE *) s->data,s->offset,SEEK_SET);
+ fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
+ }
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ FILE *f = NIL;
+ int pid,c,ret = 0;
+ unsigned long msglen,status = 0;
+ char *s,tmp[MAILTMPLEN];
+ uid_t ruid = getuid ();
+ struct passwd *pwd;
+ openlog ("tmail",LOG_PID,LOG_MAIL);
+#include "linkage.c"
+ /* make sure have some arguments */
+ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
+ /* process all flags */
+ while (argc && (*(s = *++argv)) == '-') {
+ argc--; /* gobble this argument */
+ switch (s[1]) { /* what is this flag? */
+ case 'D': /* debug */
+ debug = T; /* don't fork */
+ break;
+ case 'I': /* inbox specifier */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (argc--) inbox = cpystr (*++argv);
+ else _exit (fail ("missing argument to -I",EX_USAGE));
+ break;
+ case 'f': /* new name for this flag */
+ case 'r': /* flag giving return path */
+ if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE));
+ if (argc--) sender = cpystr (*++argv);
+ else _exit (fail ("missing argument to -f or -r",EX_USAGE));
+ break;
+ case 'b': /* create INBOX in this format */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE));
+ if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv)))
+ _exit (fail ("unknown format to -b",EX_USAGE));
+ else if (!(format->flags & DR_LOCAL) ||
+ !compare_cstring (format->name,"dummy"))
+ _exit (fail ("invalid format to -b",EX_USAGE));
+ break;
+ /* following flags are undocumented */
+ case 'p': /* precedence for quota */
+ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
+ else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
+ precedence = atol (s);
+ else _exit (fail ("missing argument to -p",EX_USAGE));
+ break;
+ case 'd': /* obsolete flag meaning multiple users */
+ break; /* ignore silently */
+ /* -s has been deprecated and replaced by the -s and -k flags in dmail.
+ * dmail's -k flag does what -s once did in tmail; dmail's -s flag
+ * takes no argument and just sets \Seen. Flag setting is more properly
+ * done in dmail which runs as the user and is clearly at the user's
+ * behest. Since tmail runs privileged, -s would have to be disabled
+ * unless the caller is also privileged.
+ */
+ case 's': /* obsolete flag meaning delivery flags */
+ if (!argc--) /* takes an argument */
+ _exit (fail ("missing argument to deprecated flag",EX_USAGE));
+ syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv);
+ break;
+ default: /* anything else */
+ _exit (fail ("unknown switch",EX_USAGE));
+ }
+ }
+
+ if (!argc) ret = fail ("no recipients",EX_USAGE);
+ else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
+ else { /* build delivery headers */
+ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
+ /* start Received line: */
+ fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version);
+ /* not root or daemon? */
+ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
+ pwd = getpwuid (ruid); /* get unprivileged user's information */
+ if (inbox || format) {
+ if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
+ else sprintf (tmp,"UID %ld",(long) ruid);
+ strcat (tmp," is not privileged to use -b or -I");
+ _exit (fail (tmp,EX_USAGE));
+ }
+ fputs (" (invoked by ",f);
+ if (pwd) fprintf (f,"user %s",pwd->pw_name);
+ else fprintf (f,"UID %ld",(long) ruid);
+ fputs (")",f);
+ }
+ /* write "for" if single recipient */
+ if (argc == 1) fprintf (f," for %s",*argv);
+ fputs ("; ",f);
+ rfc822_date (tmp);
+ fputs (tmp,f);
+ fputs ("\015\012",f);
+ /* copy text from standard input */
+ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
+ (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
+ if (s[-1] == '\015') { /* nuke leading "From " line */
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
+ while ((c = getchar ()) != EOF) putc (c,f);
+ }
+ else {
+ mm_log ("tmail called with LF-only newlines",WARN);
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) {
+ *s++ = '\015'; /* overwrite NL with CRLF */
+ *s++ = '\012';
+ *s = '\0'; /* tie off string */
+ fputs (tmp,f); /* write line */
+ }
+ /* copy text from standard input */
+ while ((c = getchar ()) != EOF) {
+ /* add CR if needed */
+ if (c == '\012') putc ('\015',f);
+ putc (c,f);
+ }
+ }
+ msglen = ftell (f); /* size of message */
+ fflush (f); /* make sure all changes written out */
+
+ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
+ else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
+ /* single delivery */
+ else if (argc == 1) ret = deliver (f,msglen,*argv);
+ else do { /* multiple delivery uses daughter forks */
+ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
+ else if (pid) { /* mother process */
+ grim_pid_reap_status (pid,NIL,(void *) status);
+ /* normal termination? */
+ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
+ }
+ /* daughter process */
+ else _exit (deliver (f,msglen,*argv));
+ } while (--argc && *argv++);
+ mm_dlog (ret ? "error in delivery" : "all recipients delivered");
+ }
+ if (f) fclose (f); /* all done with temporary file */
+ _exit (ret); /* normal exit */
+ return 0; /* stupid gcc */
+}
+
+/* Deliver message to recipient list
+ * Accepts: file description of message temporary file
+ * size of message temporary file in bytes
+ * recipient name
+ * Returns: NIL if success, else error code
+ */
+
+int deliver (FILE *f,unsigned long msglen,char *user)
+{
+ MAILSTREAM *ds = NIL;
+ char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
+ struct passwd *pwd;
+ STRING st;
+ struct stat sbuf;
+ uid_t duid;
+ uid_t euid = geteuid ();
+ /* get user record */
+ if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
+ sprintf (tmp,"no such user as %.80s",user);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* absurd is absurd */
+ if (mailbox && (strlen (mailbox) > 256))
+ return fail ("absurd folder name",EX_NOUSER);
+ /* big security hole if this is allowed */
+ if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
+ /* log in as user if different than euid */
+ if ((duid != euid) && !loginpw (pwd,1,&user)) {
+ sprintf (tmp,"unable to log in UID %ld from UID %ld",
+ (long) duid,(long) euid);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* can't use pwd after this point */
+ env_init (pwd->pw_name,pwd->pw_dir);
+ sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
+ mm_dlog (tmp);
+ /* prepare stringstruct */
+ INIT (&st,file_string,(void *) f,msglen);
+ if (mailbox) { /* non-INBOX name */
+ switch (mailbox[0]) { /* make sure a valid name */
+ default: /* other names, try to deliver if not INBOX */
+ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
+ !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
+ !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
+ case '%': case '*': /* wildcards not valid */
+ case '#': /* namespace name not valid */
+ case '/': /* absolute path names not valid */
+ case '~': /* user names not valid */
+ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
+ mm_log (tmp,WARN);
+ break;
+ }
+ mm_dlog ("retrying delivery to INBOX");
+ SETPOS (&st,0); /* rewind stringstruct just in case */
+ }
+
+ /* -I specified and not "-I INBOX"? */
+ if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
+ ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
+ ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
+ ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
+ ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
+ DRIVER *dv = NIL;
+ /* "-I #driver.xxx/name"? */
+ if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
+ ((inbox[2] == 'r') || (inbox[2] == 'R')) &&
+ ((inbox[3] == 'i') || (inbox[3] == 'I')) &&
+ ((inbox[4] == 'v') || (inbox[4] == 'V')) &&
+ ((inbox[5] == 'e') || (inbox[5] == 'E')) &&
+ ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
+ (s = strchr (inbox+8,'/'))) {
+ *s = '\0'; /* temporarily tie off driver name */
+ if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
+ (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
+ (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
+ path[0] = '\0'; /* bad -I argument, no path resolved */
+ sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ *s = '/'; /* restore delimiter */
+ }
+ /* resolve "-I other" specification */
+ else if (mailboxfile (path,inbox) && path[0]) {
+ /* resolution succeeded, impute driver */
+ if (!strcmp (inbox,"mail.txt"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
+ else if (!strcmp (inbox,"INBOX.MTX"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
+ else if (!strcmp (inbox,"mbox"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
+ }
+ else { /* bad -I argument */
+ path[0] = '\0'; /* no path resolved */
+ sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ if (*path) { /* -I successfully resolved a path? */
+ MAILSTREAM dpr;
+ dpr.dtb = dv;
+ if (dv) ds = &dpr;
+ /* supplicate to the Evil One if necessary */
+ if (lstat (path,&sbuf) && !path_create (ds,path)) {
+ /* the Evil One rejected the plea */
+ sprintf (tmp,"Unable to create %.80s, -I ignored",path);
+ mm_log (tmp,WARN);
+ }
+ /* now attempt delivery */
+ else return deliver_safely (ds,&st,inbox,path,duid,tmp);
+ }
+ }
+
+ /* no -I, resolve "INBOX" into path */
+ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
+ /* clear box, get generic INBOX prototype */
+ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* standard system driver? */
+ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
+ strcpy (path,sysinbox ());/* use system INBOX */
+ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ else { /* other driver, try ~/INBOX */
+ if ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
+ !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
+ }
+ }
+ /* not dummy, deliver to driver imputed path */
+ if (strcmp (ds->dtb->name,"dummy"))
+ return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
+ deliver_safely (ds,&st,mailbox,path,duid,tmp) :
+ fail ("unable to resolve INBOX path",EX_CANTCREAT);
+ /* dummy, empty imputed append path exist? */
+ if (ibxpath (ds = default_proto (T),&mailbox,path) &&
+ !lstat (path,&sbuf) && !sbuf.st_size)
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ /* impute path that we will create */
+ if (!ibxpath (ds = format ? (format->open) (NIL) : default_proto (NIL),
+ &mailbox,path))
+ return fail ("unable to resolve INBOX",EX_CANTCREAT);
+ }
+ /* black box, must create, get create proto */
+ else if (lstat (path,&sbuf)) ds = default_proto (NIL);
+ else { /* black box, existing file */
+ /* empty file, get append prototype */
+ if (!sbuf.st_size) ds = default_proto (T);
+ /* non-empty, get prototype from its data */
+ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* error if unknown format */
+ if (!strcmp (ds->dtb->name,"phile"))
+ return fail ("unknown format INBOX",EX_UNAVAILABLE);
+ /* otherwise can deliver to it */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
+ mm_dlog (tmp);
+ /* supplicate to the Evil One */
+ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
+ sprintf (tmp,"created %.80s",path);
+ mm_dlog (tmp);
+ /* deliver the message */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+}
+
+/* Resolve INBOX from driver prototype into mailbox name and filesystem path
+ * Accepts: driver prototype
+ * pointer to mailbox name string pointer
+ * buffer to return mailbox path
+ * Returns: T if success, NIL if error
+ */
+
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = T;
+ if (!ds) ret = NIL;
+ else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
+ strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
+ else if (!strcmp (ds->dtb->name,"tenex"))
+ ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mtx"))
+ ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mbox"))
+ ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
+ /* better not be a namespace driver */
+ else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
+ /* INBOX in home directory */
+ else ret = ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
+ if (ret) { /* don't bother if lossage */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
+ }
+ return ret;
+}
+
+/* Deliver safely
+ * Accepts: prototype stream to force mailbox format
+ * stringstruct of message temporary file
+ * mailbox name
+ * filesystem path name
+ * user id
+ * scratch buffer for messages
+ * Returns: NIL if success, else error code
+ */
+
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp)
+{
+ struct stat sbuf;
+ int i = delivery_unsafe (path,uid,&sbuf,tmp);
+ if (i) return i; /* give up now if delivery unsafe */
+ /* directory, not file */
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
+ sprintf (tmp,"WARNING: directory %.80s is listable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ else { /* file, not directory */
+ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
+ sprintf (tmp,"WARNING: multiple links to file %.80s",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is executable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
+ mm_log (tmp,WARN);
+ }
+ /* check site-written quota procedure */
+ if (!tmail_quota (st,path,uid,tmp,sender,precedence)) return fail (tmp,-1);
+ /* so far, so good */
+ sprintf (tmp,"%s appending to %.80s (%s %.80s)",
+ prt ? prt->dtb->name : "default",mailbox,
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
+ mm_dlog (tmp);
+ /* do the append now! */
+ if (!mail_append (prt,mailbox,st)) {
+ sprintf (tmp,"message delivery failed to %.80s",path);
+ return fail (tmp,EX_CANTCREAT);
+ }
+ /* note success */
+ sprintf (tmp,"delivered to %.80s",path);
+ mm_log (tmp,NIL);
+ /* make sure nothing evil this way comes */
+ return delivery_unsafe (path,uid,&sbuf,tmp);
+}
+
+/* Verify that delivery is safe
+ * Accepts: path name
+ * user id
+ * stat buffer
+ * scratch buffer for messages
+ * Returns: NIL if delivery is safe, error code if unsafe
+ */
+
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
+{
+ u_short type;
+ sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
+ mm_dlog (tmp);
+ /* prepare message just in case */
+ sprintf (tmp,"delivery to %.80s unsafe: ",path);
+ /* unsafe if can't get its status */
+ if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
+ else if (sbuf->st_uid != uid) /* unsafe if UID does not match */
+ sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
+ (long) sbuf->st_uid,(long) uid);
+ /* check file type */
+ else switch (sbuf->st_mode & S_IFMT) {
+ case S_IFDIR: /* directory is always OK */
+ return NIL;
+ case S_IFREG: /* file is unsafe if setuid */
+ if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
+ /* or setgid */
+ else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
+ else return NIL; /* otherwise safe */
+ break;
+ case S_IFCHR: strcat (tmp,"character special"); break;
+ case S_IFBLK: strcat (tmp,"block special"); break;
+ case S_IFLNK: strcat (tmp,"symbolic link"); break;
+ case S_IFSOCK: strcat (tmp,"socket"); break;
+ default:
+ sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
+ }
+ return fail (tmp,EX_CANTCREAT);
+}
+
+/* Report an error
+ * Accepts: string to output
+ */
+
+int fail (char *string,int code)
+{
+ mm_log (string,ERROR); /* pass up the string */
+ switch (code) {
+#if T
+ case EX_USAGE:
+ case EX_OSERR:
+ case EX_SOFTWARE:
+ case EX_NOUSER:
+ case EX_CANTCREAT:
+ case EX_UNAVAILABLE:
+ code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
+#endif
+ break;
+ case -1: /* quota failure... */
+ code = EX_CANTCREAT; /* ...really returns this code */
+ break;
+ default:
+ break;
+ }
+ return code; /* error code to return */
+}
+
+
+/* Get user name from username+mailbox specifier
+ * Accepts: username/mailbox specifier
+ * pointer to return location for mailbox specifier
+ * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
+ */
+
+char *getusername (char *s,char **t)
+{
+ if (*t = strchr (s,'+')) { /* have a mailbox specifier? */
+ *(*t)++ = '\0'; /* yes, tie off user name */
+ /* user+ and user+INBOX same as user */
+ if (!**t || !compare_cstring ((unsigned char *) *t,"INBOX")) *t = NIL;
+ }
+ return s; /* return user name */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ fatal ("mm_searched() call");
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_exists() call");
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_expunged() call");
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_list() call");
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_lsub() call");
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ fatal ("mm_status() call");
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ tmp[11] = '\0'; /* see if TRYCREATE */
+ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
+ else { /* ordinary logging */
+ fprintf (stderr,"%s\n",string);
+ switch (errflg) {
+ case NIL: /* no error */
+ syslog (LOG_INFO,"%s",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ syslog (LOG_WARNING,"%s",string);
+ break;
+ case ERROR: /* error */
+ default:
+ syslog (LOG_ERR,"%s",string);
+ break;
+ }
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ if (debug) fprintf (stderr,"%s\n",string);
+ syslog (LOG_DEBUG,"%s",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ fatal ("mm_login() call");
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string); /* shouldn't happen normally */
+}
diff --git a/imap/src/tmail/tquota.c b/imap/src/tmail/tquota.c
new file mode 100644
index 00000000..16552eb2
--- /dev/null
+++ b/imap/src/tmail/tquota.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+#include "c-client.h"
+
+/* Site-written routine to validate delivery per quota and policy
+ * Accepts: stringstruct of message temporary file
+ * filesystem path
+ * recipient user id
+ * return path
+ * buffer to write error message
+ * precedence setting
+ * Returns: T if can deliver, or NIL if quota issue and must bounce
+ */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence)
+{
+ return LONGT; /* dummy success return */
+}
diff --git a/imap/src/tmail/tquota.h b/imap/src/tmail/tquota.h
new file mode 100644
index 00000000..1cd11c01
--- /dev/null
+++ b/imap/src/tmail/tquota.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+/* Function prototypes */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence);