����JFIFXX�����    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222����"��4�� ���,�PG"Z_�4�˷����kjز�Z�,F+��_z�,�© �����zh6�٨�ic�fu���#ډb���_�N�?��wQ���5-�~�I���8����TK<5o�Iv-�����k�_U_�����~b�M��d����Ӝ�U�Hh��?]��E�w��Q���k�{��_}qFW7HTՑ��Y��F�?_�'ϔ��_�Ջt��=||I ��6�έ"�����D���/[�k�9���Y�8ds|\���Ҿp6�Ҵ���]��.����6�z<�v��@]�i%��$j��~�g��J>��no����pM[me�i$[����s�o�ᘨ�˸ nɜG-�ĨU�ycP�3.DB�li�;��hj���x7Z^�N�h������N3u{�:j�x�힞��#M&��jL P@_���� P��&��o8������9�����@Sz6�t7#O�ߋ �s}Yf�T���lmr����Z)'N��k�۞p����w\�Tȯ?�8`�O��i{wﭹW�[�r�� ��Q4F�׊���3m&L�=��h3����z~��#�\�l :�F,j@�� ʱ�wQT����8�"kJO���6�֚l����}���R�>ډK���]��y����&����p�}b��;N�1�m�r$�|��7�>e�@B�TM*-iH��g�D�)� E�m�|�ؘbҗ�a��Ҿ����t4���o���G��*oCN�rP���Q��@z,|?W[0�����:�n,jWiE��W��$~/�hp\��?��{(�0���+�Y8rΟ�+����>S-S����VN;�}�s?.����� w�9��˟<���Mq4�Wv'��{)0�1mB��V����W[�����8�/<� �%���wT^�5���b��)iM� pg�N�&ݝ��VO~�q���u���9� ����!��J27����$O-���! �:�%H��� ـ����y�ΠM=t{!S�� oK8������t<����è:a������[�����ա�H���~��w��Qz`�po�^ ����Q��n� �,uu�C�$ ^���,������8�#��:�6��e�|~���!�3�3.�\0��q��o�4`.|� ����y�Q�`~;�d�ׯ,��O�Zw�������`73�v�܋�<���Ȏ�� ـ4k��5�K�a�u�=9Yd��$>x�A�&�� j0� ���vF��� Y�|�y��� ~�6�@c��1vOp�Ig����4��l�OD���L����� R���c���j�_�uX6��3?nk��Wy�f;^*B� ��@�~a�`��Eu������+���6�L��.ü>��}y���}_�O�6�͐�:�YrG�X��kG�����l^w���~㒶sy��Iu�!� W ��X��N�7BV��O��!X�2����wvG�R�f�T#�����t�/?���%8�^�W�aT��G�cL�M���I��(J����1~�8�?aT ���]����AS�E��(��*E}� 2��#I/�׍qz��^t�̔���b�Yz4x���t�){ OH��+(E��A&�N�������XT��o��"�XC��'���)}�J�z�p� ��~5�}�^����+�6����w��c��Q�|Lp�d�H��}�(�.|����k��c4^�"�����Z?ȕ ��a<�L�!039C� �Eu�C�F�Ew�ç ;�n?�*o���B�8�bʝ���'#Rqf���M}7����]����s2tcS{�\icTx;�\��7K���P���ʇ Z O-��~��c>"��?�������P��E��O�8��@�8��G��Q�g�a�Վ���󁶠�䧘��_%#r�>�1�z�a��eb��qcPѵ��n���#L��� =��׀t� L�7�`��V���A{�C:�g���e@�w1 Xp3�c3�ġ����p��M"'-�@n4���fG��B3�DJ�8[Jo�ߐ���gK)ƛ��$���� ���8�3�����+���� �����6�ʻ���� ���S�kI�*KZlT _`���?��K����QK�d����B`�s}�>���`��*�>��,*@J�d�oF*����弝��O}�k��s��]��y�ߘ��c1G�V���<=�7��7����6�q�PT��tXԀ�!9*4�4Tހ3XΛex�46���Y��D ����� �BdemDa����\�_l,��G�/���֌7���Y�](�xTt^%�GE�����4�}bT���ڹ�����;Y)���B�Q��u��>J/J �⮶.�XԄ��j�ݳ�+E��d ��r�5�_D�1 ��o�� �B�x�΢�#���<��W�����8���R6�@g�M�.��� dr�D��>(otU��@x=��~v���2� ӣ�d�oBd��3�eO�6�㣷�����ݜ6��6Y��Qz`��S��{���\P�~z m5{J/L��1������<�e�ͅPu�b�]�ϔ���'������f�b� Zpw��c`"��i���BD@:)ִ�:�]��hv�E�w���T�l��P���"Ju�}��وV J��G6��. J/�Qgl߭�e�����@�z�Zev2u�)]կ�����7x���s�M�-<ɯ�c��r�v�����@��$�ޮ}lk���a���'����>x��O\�ZFu>�����ck#��&:��`�$�ai�>2Δ����l���oF[h��lE�ܺ�Πk:)���`�� $[6�����9�����kOw�\|���8}������ބ:��񶐕��I�A1/�=�2[�,�!��.}gN#�u����b��� ~��݊��}34q����d�E��Lc��$��"�[q�U�硬g^��%B �z���r�pJ�ru%v\h1Y�ne`ǥ:g���pQM~�^�Xi� ��`S�:V29.�P���V�?B�k�� AEvw%�_�9C�Q����wKekPؠ�\�;Io d�{ ߞo�c1eP����\� `����E=���@K<�Y���eڼ�J���w����{av�F�'�M�@/J��+9p���|]�����Iw &`��8���&M�hg��[�{��Xj��%��Ӓ�$��(����ʹN���<>�I���RY���K2�NPlL�ɀ)��&e����B+ь����( � �JTx���_?EZ� }@ 6�U���뙢ط�z��dWI�n` D����噥�[��uV��"�G&Ú����2g�}&m��?ċ�"����Om#��������� ��{�ON��"S�X��Ne��ysQ���@Fn��Vg���dX�~nj�]J�<�K]:��FW��b�������62�=��5f����JKw��bf�X�55��~J �%^����:�-�QIE��P��v�nZum� z � ~ə ���� ���ة����;�f��\v���g�8�1��f24;�V���ǔ�)����9���1\��c��v�/'Ƞ�w�������$�4�R-��t���� e�6�/�ġ �̕Ecy�J���u�B���<�W�ַ~�w[B1L۲�-JS΂�{���΃������A��20�c#��@ 0!1@AP"#2Q`$3V�%45a6�FRUq��� ����^7ׅ,$n�������+��F�`��2X'��0vM��p�L=������5��8������u�p~���.�`r�����\���O��,ư�0oS ��_�M�����l���4�kv\JSd���x���SW�<��Ae�IX����������$I���w�:S���y���›R��9�Q[���,�5�;�@]�%���u�@ *ro�lbI �� ��+���%m:�͇ZV�����u�̉����θau<�fc�.����{�4Ա� �Q����*�Sm��8\ujqs]{kN���)qO�y�_*dJ�b�7���yQqI&9�ԌK!�M}�R�;������S�T���1���i[U�ɵz�]��U)V�S6���3$K{�ߊ<�(� E]Զ[ǼENg�����'�\?#)Dkf��J���o��v���'�%ƞ�&K�u�!��b�35LX�Ϸ��63$K�a�;�9>,R��W��3�3� d�JeTYE.Mϧ��-�o�j3+y��y^�c�������VO�9NV\nd�1 ��!͕_)a�v;����թ�M�lWR1��)El��P;��yوÏ�u 3�k�5Pr6<�⒲l�!˞*��u־�n�!�l:����UNW ��%��Chx8vL'��X�@��*��)���̮��ˍ��� ���D-M�+J�U�kvK����+�x8��cY������?�Ԡ��~3mo��|�u@[XeY�C�\Kp�x8�oC�C�&����N�~3-H���� ��MX�s�u<`���~"WL��$8ξ��3���a�)|:@�m�\���^�`�@ҷ)�5p+��6���p�%i)P M���ngc�����#0Aruz���RL+xSS?���ʮ}()#�t��mˇ!��0}}y����<�e� �-ή�Ԩ��X������ MF���ԙ~l L.3���}�V뽺�v�����멬��Nl�)�2����^�Iq��a��M��qG��T�����c3#������3U�Ǎ���}��לS�|qa��ڃ�+���-��2�f����/��bz��ڐ�� �ݼ[2�ç����k�X�2�* �Z�d���J�G����M*9W���s{��w���T��x��y,�in�O�v��]���n����P�$�JB@=4�OTI�n��e�22a\����q�d���%�$��(���:���: /*�K[PR�fr\nڙdN���F�n�$�4�[�� U�zƶ����� �mʋ���,�ao�u 3�z� �x��Kn����\[��VFmbE;�_U��&V�Gg�]L�۪&#n%�$ɯ�dG���D�TI=�%+AB�Ru#��b4�1�»x�cs�YzڙJG��f��Il��d�eF'T� iA��T���uC�$����Y��H?����[!G`}���ͪ� �纤Hv\������j�Ex�K���!���OiƸ�Yj�+u-<���'q����uN�*�r\��+�]���<�wOZ.fp�ێ��,-*)V?j-kÊ#�`�r��dV����(�ݽBk�����G�ƛk�QmUڗe��Z���f}|����8�8��a���i��3'J�����~G_�^���d�8w������ R�`(�~�.��u���l�s+g�bv���W���lGc}��u���afE~1�Ue������Z�0�8�=e�� f@/�jqEKQQ�J��oN��J���W5~M>$6�Lt�;$ʳ{���^��6�{����v6���ķܰg�V�cnn �~z�x�«�,2�u�?cE+Ș�H؎�%�Za�)���X>uW�Tz�Nyo����s���FQƤ��$��*�&�LLXL)�1�" L��eO��ɟ�9=���:t��Z���c��Ž���Y?�ӭV�wv�~,Y��r�ۗ�|�y��GaF�����C�����.�+� ���v1���fήJ�����]�S��T��B��n5sW}y�$��~z�'�c ��8 ��� ,! �p��VN�S��N�N�q��y8z˱�A��4��*��'������2n<�s���^ǧ˭P�Jޮɏ�U�G�L�J�*#��<�V��t7�8����TĜ>��i}K%,���)[��z�21z ?�N�i�n1?T�I�R#��m-�����������������1����lA�`��fT5+��ܐ�c�q՝��ʐ��,���3�f2U�եmab��#ŠdQ�y>\��)�SLY����w#��.���ʑ�f��� ,"+�w�~�N�'�c�O�3F�������N<���)j��&��,-� �љ���֊�_�zS���TǦ����w�>��?�������n��U仆�V���e�����0���$�C�d���rP �m�׈e�Xm�Vu� �L��.�bֹ��� �[Դaզ���*��\y�8�Է:�Ez\�0�Kq�C b��̘��cө���Q��=0Y��s�N��S.���3.���O�o:���#���v7�[#߫ ��5�܎�L���Er4���9n��COWlG�^��0k�%<���ZB���aB_���������'=��{i�v�l�$�uC���mƎҝ{�c㱼�y]���W�i ��ߧc��m�H� m�"�"�����;Y�ߝ�Z�Ǔ�����:S#��|}�y�,/k�Ld� TA�(�AI$+I3��;Y*���Z��}|��ӧO��d�v��..#:n��f>�>���ȶI�TX��� 8��y����"d�R�|�)0���=���n4��6ⲑ�+��r<�O�܂~zh�z����7ܓ�HH�Ga롏���nCo�>������a ���~]���R���̲c?�6(�q�;5%� |�uj�~z8R=X��I�V=�|{v�Gj\gc��q����z�؋%M�ߍ����1y��#��@f^���^�>N�����#x#۹��6�Y~�?�dfPO��{��P�4��V��u1E1J �*|���%���JN��`eWu�zk M6���q t[�� ��g�G���v��WIG��u_ft����5�j�"�Y�:T��ɐ���*�;� e5���4����q$C��2d�}���� _S�L#m�Yp��O�.�C�;��c����Hi#֩%+) �Ӎ��ƲV���SYź��g |���tj��3�8���r|���V��1#;.SQ�A[���S������#���`n�+���$��$I �P\[�@�s��(�ED�z���P��])8�G#��0B��[ى��X�II�q<��9�~[Z멜�Z�⊔IWU&A>�P~�#��dp<�?����7���c��'~���5 ��+$���lx@�M�dm��n<=e�dyX��?{�|Aef ,|n3�<~z�ƃ�uۧ�����P��Y,�ӥQ�*g�#먙R�\���;T��i,��[9Qi歉����c>]9�� ��"�c��P�� �Md?٥��If�ت�u��k��/����F��9�c*9��Ǎ:�ØF���z�n*�@|I�ށ9����N3{'��[�'ͬ�Ҳ4��#}��!�V� Fu��,�,mTIk���v C�7v���B�6k�T9��1�*l� '~��ƞF��lU��'�M ����][ΩũJ_�{�i�I�n��$���L�� j��O�dx�����kza۪��#�E��Cl����x˘�o�����V���ɞ�ljr��)�/,�߬h�L��#��^��L�ф�,íMƁe�̩�NB�L�����iL����q�}��(��q��6IçJ$�W�E$��:������=#����(�K�B����zђ <��K(�N�۫K�w��^O{!����)�H���>x�������lx�?>Պ�+�>�W���,Ly!_�D���Ō�l���Q�!�[ �S����J��1��Ɛ�Y}��b,+�Lo�x�ɓ)����=�y�oh�@�꥟/��I��ѭ=��P�y9��� �ۍYӘ�e+�p�Jnϱ?V\SO%�(�t� ���=?MR�[Ș�����d�/ ��n�l��B�7j� ��!�;ӥ�/�[-���A�>�dN�sLj ��,ɪv��=1c�.SQ�O3�U���ƀ�ܽ�E����������̻��9G�ϷD�7(�}��Ävӌ\�y�_0[w ���<΍>����a_��[0+�L��F.�޺��f�>oN�T����q;���y\��bՃ��y�jH�<|q-eɏ�_?_9+P���Hp$�����[ux�K w�Mw��N�ی'$Y2�=��q���KB��P��~������Yul:�[<����F1�2�O���5=d����]Y�sw:���Ϯ���E��j,_Q��X��z`H1,#II ��d�wr��P˂@�ZJV����y$�\y�{}��^~���[:N����ߌ�U�������O��d�����ؾe��${p>G��3c���Ė�lʌ�� ת��[��`ϱ�-W����dg�I��ig2��� ��}s ��ؤ(%#sS@���~���3�X�nRG�~\jc3�v��ӍL��M[JB�T��s3}��j�Nʖ��W����;7��ç?=X�F=-�=����q�ߚ���#���='�c��7���ڑW�I(O+=:uxq�������������e2�zi+�kuG�R��������0�&e�n���iT^J����~\jy���p'dtG��s����O��3����9* �b#Ɋ�� p������[Bws�T�>d4�ۧs���nv�n���U���_�~,�v����ƜJ1��s�� �QIz��)�(lv8M���U=�;����56��G���s#�K���MP�=��LvyGd��}�VwWBF�'�à �?MH�U�g2�� ����!�p�7Q��j��ڴ����=��j�u��� Jn�A s���uM������e��Ɔ�Ҕ�!)'��8Ϣ�ٔ��ޝ(��Vp���צ֖d=�IC�J�Ǡ{q������kԭ�߸���i��@K����u�|�p=..�*+����x�����z[Aqġ#s2a�Ɗ���RR�)*HRsi�~�a &f��M��P����-K�L@��Z��Xy�'x�{}��Zm+���:�)�) IJ�-i�u���� ���ܒH��'�L(7�y�GӜq���� j��� 6ߌg1�g�o���,kر���tY�?W,���p���e���f�OQS��!K�۟cҒA�|ս�j�>��=⬒��˧L[�� �߿2JaB~R��u�:��Q�] �0H~���]�7��Ƽ�I���(}��cq '�ήET���q�?f�ab���ӥvr� �)o��-Q��_'����ᴎo��K������;��V���o��%���~OK ����*��b�f:���-ťIR��`B�5!RB@���ï�� �u �̯e\�_U�_������� g�ES��3�������QT��a����x����U<~�c?�*�#]�MW,[8O�a�x��]�1bC|踤�P��lw5V%�)�{t�<��d��5���0i�XSU��m:��Z�┵�i�"��1�^B�-��P�hJ��&)O��*�D��c�W��vM��)����}���P��ܗ-q����\mmζZ-l@�}��a��E�6��F�@��&Sg@���ݚ�M����� ȹ 4����#p�\H����dYDo�H���"��\��..R�B�H�z_�/5˘����6��KhJR��P�mƶi�m���3�,#c�co��q�a)*Pt����R�m�k�7x�D�E�\Y�閣_X�<���~�)���c[[�BP����6�Yq���S��0����%_����;��Àv�~�| VS؇ ��'O0��F0��\���U�-�d@�����7�SJ*z��3n��y��P����O���������m�~�P�3|Y��ʉr#�C�<�G~�.,! ���bqx���h~0=��!ǫ�jy����l�O,�[B��~��|9��ٱ����Xly�#�i�B��g%�S��������tˋ���e���ې��\[d�t)��.+u�|1 ������#�~Oj����hS�%��i.�~X���I�H�m��0n���c�1uE�q��cF�RF�o���7� �O�ꮧ� ���ۛ{��ʛi5�rw?׌#Qn�TW��~?y$��m\�\o����%W� ?=>S�N@�� �Ʈ���R����N�)�r"C�:��:����� �����#��qb��Y�. �6[��2K����2u�Ǧ�HYR��Q�MV��� �G�$��Q+.>�����nNH��q�^��� ����q��mM��V��D�+�-�#*�U�̒ ���p욳��u:�������IB���m���PV@O���r[b= �� ��1U�E��_Nm�yKbN�O���U�}�the�`�|6֮P>�\2�P�V���I�D�i�P�O;�9�r�mAHG�W�S]��J*�_�G��+kP�2����Ka�Z���H�'K�x�W�MZ%�O�YD�Rc+o��?�q��Ghm��d�S�oh�\�D�|:W������UA�Qc yT�q������~^�H��/��#p�CZ���T�I�1�ӏT����4��"�ČZ�����}��`w�#�*,ʹ�� ��0�i��課�Om�*�da��^gJ݅{���l�e9uF#T�ֲ��̲�ٞC"�q���ߍ ոޑ�o#�XZTp����@ o�8��(jd��xw�]�,f���`~�|,s��^����f�1���t��|��m�򸄭/ctr��5s��7�9Q�4�H1꠲BB@l9@���C�����+�wp�xu�£Yc�9��?`@#�o�mH�s2��)�=��2�.�l����jg�9$�Y�S�%*L������R�Y������7Z���,*=�䷘$�������arm�o�ϰ���UW.|�r�uf����IGw�t����Zwo��~5 ��YյhO+=8fF�)�W�7�L9lM�̘·Y���֘YLf�큹�pRF���99.A �"wz��=E\Z���'a� 2��Ǚ�#;�'}�G���*��l��^"q��+2FQ� hj��kŦ��${���ޮ-�T�٭cf�|�3#~�RJ����t��$b�(R��(����r���dx� >U b�&9,>���%E\� Ά�e�$��'�q't��*�א���ެ�b��-|d���SB�O�O��$�R+�H�)�܎�K��1m`;�J�2�Y~9��O�g8=vqD`K[�F)k�[���1m޼c��n���]s�k�z$@��)!I �x՝"v��9=�ZA=`Ɠi �:�E��)`7��vI��}d�YI�_ �o�:ob���o ���3Q��&D&�2=�� �Ά��;>�h����y.*ⅥS������Ӭ�+q&����j|UƧ����}���J0��WW< ۋS�)jQR�j���Ư��rN)�Gű�4Ѷ(�S)Ǣ�8��i��W52���No˓� ۍ%�5brOn�L�;�n��\G����=�^U�dI���8$�&���h��'���+�(������cȁ߫k�l��S^���cƗjԌE�ꭔ��gF���Ȓ��@���}O���*;e�v�WV���YJ\�]X'5��ղ�k�F��b 6R�o՜m��i N�i����>J����?��lPm�U��}>_Z&�KK��q�r��I�D�Չ~�q�3fL�:S�e>���E���-G���{L�6p�e,8��������QI��h��a�Xa��U�A'���ʂ���s�+טIjP�-��y�8ۈZ?J$��W�P� ��R�s�]��|�l(�ԓ��sƊi��o(��S0��Y� 8�T97.�����WiL��c�~�dxc�E|�2!�X�K�Ƙਫ਼�$((�6�~|d9u+�qd�^3�89��Y�6L�.I�����?���iI�q���9�)O/뚅����O���X��X�V��ZF[�یgQ�L��K1���RҖr@v�#��X�l��F���Нy�S�8�7�kF!A��sM���^rkp�jP�DyS$N���q��nxҍ!U�f�!eh�i�2�m���`�Y�I�9r�6� �TF���C}/�y�^���Η���5d�'��9A-��J��>{�_l+�`��A���[�'��յ�ϛ#w:݅�%��X�}�&�PSt�Q�"�-��\縵�/����$Ɨh�Xb�*�y��BS����;W�ջ_mc�����vt?2}1�;qS�d�d~u:2k5�2�R�~�z+|HE!)�Ǟl��7`��0�<�,�2*���Hl-��x�^����'_TV�gZA�'j� ^�2Ϊ��N7t�����?w�� �x1��f��Iz�C-Ȗ��K�^q�;���-W�DvT�7��8�Z�������� hK�(P:��Q- �8�n�Z���܃e貾�<�1�YT<�,�����"�6{/ �?�͟��|1�:�#g��W�>$����d��J��d�B��=��jf[��%rE^��il:��B���x���Sּ�1հ��,�=��*�7 fcG��#q� �eh?��2�7�����,�!7x��6�n�LC�4x��},Geǝ�tC.��vS �F�43��zz\��;QYC,6����~;RYS/6���|2���5���v��T��i����������mlv��������&� �nRh^ejR�LG�f���? �ۉҬܦƩ��|��Ȱ����>3����!v��i�ʯ�>�v��オ�X3e���_1z�Kȗ\<������!�8���V��]��?b�k41�Re��T�q��mz��TiOʦ�Z��Xq���L������q"+���2ۨ��8}�&N7XU7Ap�d�X��~�׿��&4e�o�F��� �H����O���č�c�� 懴�6���͉��+)��v;j��ݷ�� �UV�� i��� j���Y9GdÒJ1��詞�����V?h��l����l�cGs�ځ�������y�Ac�����\V3�? �� ܙg�>qH�S,�E�W�[�㺨�uch�⍸�O�}���a��>�q�6�n6����N6�q������N ! 1AQaq�0@����"2BRb�#Pr���3C`��Scst���$4D���%Td�� ?���N����a��3��m���C���w��������xA�m�q�m���m������$����4n淿t'��C"w��zU=D�\R+w�p+Y�T�&�պ@��ƃ��3ޯ?�Aﶂ��aŘ���@-�����Q�=���9D��ռ�ѻ@��M�V��P��܅�G5�f�Y<�u=,EC)�<�Fy'�"�&�չ�X~f��l�KԆV��?�� �W�N����=(� �;���{�r����ٌ�Y���h{�١������jW����P���Tc�����X�K�r��}���w�R��%��?���E��m�� �Y�q|����\lEE4���r���}�lsI�Y������f�$�=�d�yO����p�����yBj8jU�o�/�S��?�U��*������ˍ�0������u�q�m [�?f����a�� )Q�>����6#������� ?����0UQ����,IX���(6ڵ[�DI�MNލ�c&���υ�j\��X�R|,4��� j������T�hA�e��^���d���b<����n�� �즇�=!���3�^�`j�h�ȓr��jẕ�c�,ٞX����-����a�ﶔ���#�$��]w�O��Ӫ�1y%��L�Y<�wg#�ǝ�̗`�x�xa�t�w��»1���o7o5��>�m뭛C���Uƃߜ}�C���y1Xνm�F8�jI���]����H���ۺиE@I�i;r�8ӭ����V�F�Շ| ��&?�3|x�B�MuS�Ge�=Ӕ�#BE5G�����Y!z��_e��q�р/W>|-�Ci߇�t�1ޯќd�R3�u��g�=0 5��[?�#͏��q�cf���H��{ ?u�=?�?ǯ���}Z��z���hmΔ�BFTW�����<�q�(v� ��!��z���iW]*�J�V�z��gX֧A�q�&��/w���u�gYӘa���; �i=����g:��?2�dž6�ى�k�4�>�Pxs����}������G�9��3 ���)gG�R<>r h�$��'nc�h�P��Bj��J�ҧH� -��N1���N��?��~��}-q!=��_2hc�M��l�vY%UE�@|�v����M2�.Y[|y�"Eï��K�ZF,�ɯ?,q�?v�M 80jx�"�;�9vk�����+ ֧�� �ȺU��?�%�vcV��mA�6��Qg^M����A}�3�nl� QRN�l8�kkn�'�����(��M�7m9و�q���%ޟ���*h$Zk"��$�9��: �?U8�Sl��,,|ɒ��xH(ѷ����Gn�/Q�4�P��G�%��Ա8�N��!� �&�7�;���eKM7�4��9R/%����l�c>�x;������>��C�:�����t��h?aKX�bhe�ᜋ^�$�Iհ �hr7%F$�E��Fd���t��5���+�(M6�t����Ü�UU|zW�=a�Ts�Tg������dqP�Q����b'�m���1{|Y����X�N��b �P~��F^F:����k6�"�j!�� �I�r�`��1&�-$�Bevk:y���#yw��I0��x��=D�4��tU���P�ZH��ڠ底taP��6����b>�xa����Q�#� WeF��ŮNj�p�J* mQ�N����*I�-*�ȩ�F�g�3 �5��V�ʊ�ɮ�a��5F���O@{���NX��?����H�]3��1�Ri_u��������ѕ�� ����0��� F��~��:60�p�͈�S��qX#a�5>���`�o&+�<2�D����: �������ڝ�$�nP���*)�N�|y�Ej�F�5ټ�e���ihy�Z �>���k�bH�a�v��h�-#���!�Po=@k̆IEN��@��}Ll?j�O������߭�ʞ���Q|A07x���wt!xf���I2?Z��<ץ�T���cU�j��]��陎Ltl �}5�ϓ��$�,��O�mˊ�;�@O��jE��j(�ا,��LX���LO���Ц�90�O �.����a��nA���7������j4 ��W��_ٓ���zW�jcB������y՗+EM�)d���N�g6�y1_x��p�$Lv:��9�"z��p���ʙ$��^��JԼ*�ϭ����o���=x�Lj�6�J��u82�A�H�3$�ٕ@�=Vv�]�'�qEz�;I˼��)��=��ɯ���x �/�W(V���p�����$ �m�������u�����񶤑Oqˎ�T����r��㠚x�sr�GC��byp�G��1ߠ�w e�8�$⿄����/�M{*}��W�]˷.�CK\�ުx���/$�WPw���r� |i���&�}�{�X� �>��$-��l���?-z���g����lΆ���(F���h�vS*���b���߲ڡn,|)mrH[���a�3�ר�[1��3o_�U�3�TC�$��(�=�)0�kgP���� ��u�^=��4 �WYCҸ:��vQ�ר�X�à��tk�m,�t*��^�,�}D*� �"(�I��9R����>`�`��[~Q]�#af��i6l��8���6�:,s�s�N6�j"�A4���IuQ��6E,�GnH��zS�HO�uk�5$�I�4��ؤ�Q9�@��C����wp�BGv[]�u�Ov���0I4���\��y�����Q�Ѹ��~>Z��8�T��a��q�ޣ;z��a���/��S��I:�ܫ_�|������>=Z����8:�S��U�I�J��"IY���8%b8���H��:�QO�6�;7�I�S��J��ҌAά3��>c���E+&jf$eC+�z�;��V����� �r���ʺ������my�e���aQ�f&��6�ND��.:��NT�vm�<- u���ǝ\MvZY�N�NT��-A�>jr!S��n�O 1�3�Ns�%�3D@���`������ܟ 1�^c<���� �a�ɽ�̲�Xë#�w�|y�cW�=�9I*H8�p�^(4���՗�k��arOcW�tO�\�ƍR��8����'�K���I�Q�����?5�>[�}��yU�ײ -h��=��% q�ThG�2�)���"ו3]�!kB��*p�FDl�A���,�eEi�H�f�Ps�����5�H:�Փ~�H�0Dت�D�I����h�F3�������c��2���E��9�H��5�zԑ�ʚ�i�X�=:m�xg�hd(�v����׊�9iS��O��d@0ڽ���:�p�5�h-��t�&���X�q�ӕ,��ie�|���7A�2���O%P��E��htj��Y1��w�Ѓ!����  ���� ࢽ��My�7�\�a�@�ţ�J �4�Ȼ�F�@o�̒?4�wx��)��]�P��~�����u�����5�����7X ��9��^ܩ�U;Iꭆ 5 �������eK2�7(�{|��Y׎ �V��\"���Z�1� Z�����}��(�Ǝ"�1S���_�vE30>���p;� ΝD��%x�W�?W?v����o�^V�i�d��r[��/&>�~`�9Wh��y�;���R��� ;;ɮT��?����r$�g1�K����A��C��c��K��l:�'��3 c�ﳯ*"t8�~l��)���m��+U,z��`(�>yJ�?����h>��]��v��ЍG*�{`��;y]��I�T� ;c��NU�fo¾h���/$���|NS���1�S�"�H��V���T���4��uhǜ�]�v;���5�͠x��'C\�SBpl���h}�N����� A�Bx���%��ޭ�l��/����T��w�ʽ]D�=����K���ž�r㻠l4�S�O?=�k �M:� ��c�C�a�#ha���)�ѐxc�s���gP�iG��{+���x���Q���I= �� z��ԫ+ �8"�k�ñ�j=|����c ��y��CF��/��*9ж�h{ �?4�o� ��k�m�Q�N�x��;�Y��4膚�a�w?�6�>e]�����Q�r�:����g�,i"�����ԩA�*M�<�G��b�if��l^M��5� �Ҩ�{����6J��ZJ�����P�*�����Y���ݛu�_4�9�I8�7���������,^ToR���m4�H��?�N�S�ѕw��/S��甍�@�9H�S�T��t�ƻ���ʒU��*{Xs�@����f�����֒Li�K{H�w^���������Ϥm�tq���s� ���ք��f:��o~s��g�r��ט� �S�ѱC�e]�x���a��) ���(b-$(�j>�7q�B?ӕ�F��hV25r[7 Y� }L�R��}����*sg+��x�r�2�U=�*'WS��ZDW]�WǞ�<��叓���{�$�9Ou4��y�90-�1�'*D`�c�^o?(�9��u���ݐ��'PI&� f�Jݮ�������:wS����jfP1F:X �H�9dԯ���˝[�_54 �}*;@�ܨ�� ð�yn�T���?�ןd�#���4rG�ͨ��H�1�|-#���Mr�S3��G�3�����)�.᧏3v�z֑��r����$G"�`j �1t��x0<Ɔ�Wh6�y�6��,œ�Ga��gA����y��b��)��h�D��ß�_�m��ü �gG;��e�v��ݝ�nQ� ��C����-�*��o���y�a��M��I�>�<���]obD��"�:���G�A��-\%LT�8���c�)��+y76���o�Q�#*{�(F�⽕�y����=���rW�\p���۩�c���A���^e6��K������ʐ�cVf5$�'->���ՉN"���F�"�UQ@�f��Gb~��#�&�M=��8�ט�JNu9��D��[̤�s�o�~������ G��9T�tW^g5y$b��Y'��س�Ǵ�=��U-2 #�MC�t(�i� �lj�@Q 5�̣i�*�O����s�x�K�f��}\��M{E�V�{�υ��Ƈ�����);�H����I��fe�Lȣr�2��>��W�I�Ȃ6������i��k�� �5�YOxȺ����>��Y�f5'��|��H+��98pj�n�.O�y�������jY��~��i�w'������l�;�s�2��Y��:'lg�ꥴ)o#'Sa�a�K��Z� �m��}�`169�n���"���x��I ��*+� }F<��cГ���F�P�������ֹ*�PqX�x۩��,� ��N�� �4<-����%����:��7����W���u�`����� $�?�I��&����o��o��`v�>��P��"��l���4��5'�Z�gE���8���?��[�X�7(��.Q�-��*���ތL@̲����v��.5���[��=�t\+�CNܛ��,g�SQnH����}*F�G16���&:�t��4ُ"A��̣��$�b �|����#rs��a�����T�� ]�<�j��BS�('$�ɻ� �wP;�/�n��?�ݜ��x�F��yUn�~mL*-�������Xf�wd^�a�}��f�,=t�׵i�.2/wpN�Ep8�OР���•��R�FJ� 55TZ��T �ɭ�<��]��/�0�r�@�f��V��V����Nz�G��^���7hZi����k��3�,kN�e|�vg�1{9]_i��X5y7� 8e]�U����'�-2,���e"����]ot�I��Y_��n�(JҼ��1�O ]bXc���Nu�No��pS���Q_���_�?i�~�x h5d'�(qw52] ��'ޤ�q��o1�R!���`ywy�A4u���h<קy���\[~�4�\ X�Wt/� 6�����n�F�a8��f���z �3$�t(���q��q�x��^�XWeN'p<-v�!�{�(>ӽDP7��ո0�y)�e$ٕv�Ih'Q�EA�m*�H��RI��=:��� ���4牢) �%_iN�ݧ�l]� �Nt���G��H�L��� ɱ�g<���1V�,�J~�ٹ�"K��Q�� 9�HS�9�?@��k����r�;we݁�]I�!{ �@�G�[�"��`���J:�n]�{�cA�E����V��ʆ���#��U9�6����j�#Y�m\��q�e4h�B�7��C�������d<�?J����1g:ٳ���=Y���D�p�ц� ׈ǔ��1�]26؜oS�'��9�V�FVu�P�h�9�xc�oq�X��p�o�5��Ա5$�9W�V(�[Ak�aY錎qf;�'�[�|���b�6�Ck��)��#a#a˙��8���=äh�4��2��C��4tm^ �n'c���]GQ$[Wҿ��i���vN�{Fu ��1�gx��1┷���N�m��{j-,��x�� Ūm�ЧS�[�s���Gna���䑴�� x�p 8<������97�Q���ϴ�v�aϚG��Rt�Һ׈�f^\r��WH�JU�7Z���y)�vg=����n��4�_)y��D'y�6�]�c�5̪�\� �PF�k����&�c;��cq�$~T�7j ���nç]�<�g ":�to�t}�159�<�/�8������m�b�K#g'I'.W�����6��I/��>v��\�MN��g���m�A�yQL�4u�Lj�j9��#44�t��l^�}L����n��R��!��t��±]��r��h6ٍ>�yҏ�N��fU�� ���� Fm@�8}�/u��jb9������he:A�y�ծw��GpΧh�5����l}�3p468��)U��d��c����;Us/�֔�YX�1�O2��uq�s��`hwg�r~�{ R��mhN��؎*q 42�*th��>�#���E����#��Hv�O����q�}�����6�e��\�,Wk�#���X��b>��p}�դ��3���T5��†��6��[��@�P�y*n��|'f�֧>�lư΂�̺����SU�'*�q�p�_S�����M�� '��c�6�����m�� ySʨ;M��r���Ƌ�m�Kxo,���Gm�P��A�G�:��i��w�9�}M(�^�V��$ǒ�ѽ�9���|���� �a����J�SQ�a���r�B;����}���ٻ֢�2�%U���c�#�g���N�a�ݕ�'�v�[�OY'��3L�3�;,p�]@�S��{ls��X�'���c�jw�k'a�.��}�}&�� �dP�*�bK=ɍ!����;3n�gΊU�ߴmt�'*{,=SzfD� A��ko~�G�aoq�_mi}#�m�������P�Xhύ����mxǍ�΂���巿zf��Q���c���|kc�����?���W��Y�$���_Lv����l߶��c���`?����l�j�ݲˏ!V��6����U�Ђ(A���4y)H���p�Z_�x��>���e��R��$�/�`^'3qˏ�-&Q�=?��CFVR �D�fV�9��{�8g�������n�h�(P"��6�[�D���< E�����~0<@�`�G�6����Hг�cc�� �c�K.5��D��d�B���`?�XQ��2��ٿyqo&+�1^� DW�0�ꊩ���G�#��Q�nL3��c���������/��x ��1�1[y�x�პCW��C�c�UĨ80�m�e�4.{�m��u���I=��f�����0QRls9���f���������9���~f�����Ǩ��a�"@�8���ȁ�Q����#c�ic������G��$���G���r/$W�(��W���V�"��m�7�[m�A�m����bo��D� j����۳� l���^�k�h׽����� ��#� iXn�v��eT�k�a�^Y�4�BN��ĕ��0 !01@Q"2AaPq3BR������?���@4�Q�����T3,���㺠�W�[=JK�Ϟ���2�r^7��vc�:�9 �E�ߴ�w�S#d���Ix��u��:��Hp��9E!�� V 2;73|F��9Y���*ʬ�F��D����u&���y؟��^EA��A��(ɩ���^��GV:ݜDy�`��Jr29ܾ�㝉��[���E;Fzx��YG��U�e�Y�C���� ����v-tx����I�sם�Ę�q��Eb�+P\ :>�i�C'�;�����k|z�رn�y]�#ǿb��Q��������w�����(�r|ӹs��[�D��2v-%��@;�8<a���[\o[ϧw��I!��*0�krs)�[�J9^��ʜ��p1)� "��/_>��o��<1����A�E�y^�C��`�x1'ܣn�p��s`l���fQ��):�l����b>�Me�jH^?�kl3(�z:���1ŠK&?Q�~�{�ٺ�h�y���/�[��V�|6��}�KbX����mn[-��7�5q�94�������dm���c^���h� X��5��<�eޘ>G���-�}�دB�ޟ� ��|�rt�M��V+�]�c?�-#ڛ��^ǂ}���Lkr���O��u�>�-D�ry� D?:ޞ�U��ǜ�7�V��?瓮�"�#���r��չģVR;�n���/_� ؉v�ݶe5d�b9��/O��009�G���5n�W����JpA�*�r9�>�1��.[t���s�F���nQ� V 77R�]�ɫ8����_0<՜�IF�u(v��4��F�k�3��E)��N:��yڮe��P�`�1}�$WS��J�SQ�N�j�ٺ��޵�#l���ј(�5=��5�lǏmoW�v-�1����v,W�mn��߀$x�<����v�j(����c]��@#��1������Ǔ���o'��u+����;G�#�޸��v-lη��/(`i⣍Pm^���ԯ̾9Z��F��������n��1��� ��]�[��)�'������:�֪�W��FC����� �B9،!?���]��V��A�Վ�M��b�w��G F>_DȬ0¤�#�QR�[V��kz���m�w�"��9ZG�7'[��=�Q����j8R?�zf�\a�=��O�U����*oB�A�|G���2�54 �p��.w7� �� ��&������ξxGHp� B%��$g�����t�Џ򤵍z���HN�u�Я�-�'4��0��;_��3 !01"@AQa2Pq#3BR������?��ʩca��en��^��8���<�u#��m*08r��y�N"�<�Ѳ0��@\�p��� �����Kv�D��J8�Fҽ� �f�Y��-m�ybX�NP����}�!*8t(�OqѢ��Q�wW�K��ZD��Δ^e��!� ��B�K��p~�����e*l}z#9ң�k���q#�Ft�o��S�R����-�w�!�S���Ӥß|M�l޶V��!eˈ�8Y���c�ЮM2��tk���� ������J�fS����Ö*i/2�����n]�k�\���|4yX�8��U�P.���Ы[���l��@"�t�<������5�lF���vU�����W��W��;�b�cД^6[#7@vU�xgZv��F�6��Q,K�v��� �+Ъ��n��Ǣ��Ft���8��0��c�@�!�Zq s�v�t�;#](B��-�nῃ~���3g������5�J�%���O������n�kB�ĺ�.r��+���#�N$?�q�/�s�6��p��a����a��J/��M�8��6�ܰ"�*������ɗud"\w���aT(����[��F��U՛����RT�b���n�*��6���O��SJ�.�ij<�v�MT��R\c��5l�sZB>F��<7�;EA��{��E���Ö��1U/�#��d1�a�n.1ě����0�ʾR�h��|�R��Ao�3�m3 ��%�� ���28Q� ��y��φ���H�To�7�lW>����#i`�q���c����a��� �m,B�-j����݋�'mR1Ήt�>��V��p���s�0IbI�C.���1R�ea�����]H�6����������4B>��o��](��$B���m�����a�!=��?�B� K�Ǿ+�Ծ"�n���K��*��+��[T#�{E�J�S����Q�����s�5�:�U�\wĐ�f�3����܆&�)����I���Ԇw��E T�lrTf6Q|R�h:��[K�� �z��c֧�G�C��%\��_�a�84��HcO�bi��ؖV��7H �)*ģK~Xhչ0��4?�0��� �E<���}3���#���u�?�� ��|g�S�6ꊤ�|�I#Hڛ� �ա��w�X��9��7���Ŀ%�SL��y6č��|�F�a 8���b��$�sק�h���b9RAu7�˨p�Č�_\*w��묦��F ����4D~�f����|(�"m���NK��i�S�>�$d7SlA��/�²����SL��|6N�}���S�˯���g��]6��; �#�.��<���q'Q�1|KQ$�����񛩶"�$r�b:���N8�w@��8$�� �AjfG|~�9F ���Y��ʺ��Bwؒ������M:I岎�G��`s�YV5����6��A �b:�W���G�q%l�����F��H���7�������Fsv7��k�� 403WebShell
403Webshell
Server IP : 51.161.54.47  /  Your IP : 216.73.216.139
Web Server : Apache/2.4.68 (Unix) OpenSSL/1.1.1k
System : Linux host.ditinformatica.ar 4.18.0-553.144.1.el8_10.x86_64 #1 SMP Tue Jul 14 09:26:58 EDT 2026 x86_64
User : kalaycom ( 1021)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/local/imap-2007f/lib/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/local/imap-2007f/lib/utf8.c
/* ========================================================================
 * 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;
}

Youez - 2016 - github.com/yon3zu
LinuXploit