From: handa Date: Tue, 9 Mar 2004 05:26:35 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: REL-1-0-base X-Git-Url: http://git.chise.org/gitweb/?a=commitdiff_plain;h=1359a5c46aaaa266d1d6a0f30f2478a18e439b5c;p=m17n%2Fm17n-lib.git *** empty log message *** --- 1359a5c46aaaa266d1d6a0f30f2478a18e439b5c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..66ce178 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,14 @@ +stamp-h* +aclocal.m4 +autoscan.log +configure +configure.scan +*.gz +autom4te.cache +config.* +libtool +m17n-config +Makefile.in +Makefile +.deps +.libs diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d0cb331 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,11 @@ +Kenichi Handa + Core developper. + +Mikiko Nishikimi + Core developper. + +Naoto Takahashi + Core developper. + +Satoru Tomura + Core developper. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ba13825 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,25 @@ +2004-03-01 Kenichi Handa + + * Version 1.0 released. + + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..449d8e6 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,27 @@ +# Makefile.am -- top level Makefile for the m17n library. +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 + +# This file is part of the m17n library. + +# The m17n library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. + +# The m17n library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the m17n library; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. + +## Process this file with Automake to create Makefile.in + +SUBDIRS = src example + +bin_SCRIPTS = m17n-config diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..d907f43 --- /dev/null +++ b/NEWS @@ -0,0 +1,34 @@ +NEWS -- What's new in the m17n library. -*- outline -*- +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 +See the end for copying conditions. + + +* Changes in the m17n library 0.9 + +** Released. + + +* Copyright information + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/README b/README new file mode 100644 index 0000000..b410250 --- /dev/null +++ b/README @@ -0,0 +1,138 @@ +This directory tree holds version 0.9 of the m17n library. -*- text -*- + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 +See the end for copying conditions. + +The m17n library is a multilingual text processing library for the C +language. + +(1) INSTALLATION + +(1-1) From CVS working directory. + +Run the script "bootstrap.sh" in this directory. It is tested that +the script run successfully with these versions of auto tools. + + libtool-1.4 + automake-1.4p5 + autoconf-2.52 + +Then, proceed to the next step. + +(1-2) From the tarball. + +The m17n library utilizes these extra libraries. It is recommened to +install them before running the "configure" script. The script will +find out the existence of them automatically. + + libxml2 -- http://xmlsoft.org/ + fribidi -- http://fribidi.sourceforge.net/ + freetype -- http://www.freetype.org/ + libotf -- http://www.m17n.org/libotf/ + +The sample program medit utilizes this Japanese inputting system. It +is also recommened to install it. + + anthy -- http://anthy.sourceforge.jp/ + +The sample program medit and mdump utilize this Thai word-boundary +finder. It is also recommened to install it. + + wordcut -- http://thaiwordseg.sourceforge.net/ + +Then, type the followings on the command line. + + % ./configure + % make + % make install + +Note that this package assumes an ANSI C compiler such as gcc. It +will not compile with an old-style K&R compiler. + +The default installation path is "/usr/local". +Thus, these header files are installed in /usr/local/include: + m17n.h, m17n-core.h, m17n-gui.h, m17n-err.h, m17n-X.h +These library files are installed in /usr/local/lib: + libm17n.{a,so,la} + libm17n-core.{a,so,la} + libm17n-X.{a,so,la} + libimx-anthy.{a,so,la}, + libimx-ispell.{a,so,la}, +This shell script is installed in /usr/local/bin: + m17n-config +These sample programs are installed in /usr/local/bin too: + mconv, mdate, mview, mdump, medit + +This file under `example' sub-directory is a Japanese resource file +for medit. It is not installed but useful in Japanese locale. Copy +it to your home directory (or, for instance, +/usr/X11R6/lib/X11/ja/app-defaults) and rename it to "MEdit" if you +want to see labels in Japanese: + MEdit.ja + +These text files under `example' sub-directory are not installed but +useful for testing the rendering engine of the m17n library: + HELLO.utf8 HELLO.xml HELLO-ja.utf8 HELLO-ja.xml +XXX.xml are generated from XXX.utf8 by attaching text property +`langauge' and serializing. + +Please read also INSTALL for the generic installation instructions. + + +(2) DATABASE + +The m17n library utilizes the m17n database avairable at: + http://www.m17n.org/m17n-lib/m17n-db +Without this database, the m17n library loses half its value. Please +install it too before you try the above sample programs or develop a +program that uses the m17n library. + + +(3) DOCUMENTATION + +This page has a link to full documentaion of the m17n library: + http://www.m17n.org/m17n-lib + +Actually, the documentation was generated by Doxygen using comments in +the source files. There are English and Japanese comments in +parallel, but plese note that Japanese comments are not updated for +long. + +(4) USAGE + +The library provides three levels of APIs, CORE, SHELL, and GUI. For +CORE API, include , for SHELL API, include , and +for GUI API, include and . See the +documementation above, or the manual of m17nIntro(3) for more detail. + +The shell script "m17n-config" helps compiling and linking of a +program that uses the m17n library. For instance this compiles PROG.c +that uses SHELL API and builds executable PROG. + + % gcc -o PROG `m17n-config --clags` `m17n-config --libs` PROG.c + +---------------------------------------------------------------------- +Copyright information + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/TODO b/TODO new file mode 100644 index 0000000..9b07fef --- /dev/null +++ b/TODO @@ -0,0 +1,60 @@ +TODO -- todo list for the m17n library. -*- outline -*- +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 +See the end for copying conditions. + + +* soon + +** Explicitely list up managed objects and describe them. + +** Allow properties to M-text itself, not to a specific region. + + +* later + +** Add in MFont to allow any property. + +** Make such an M-text modifiable that has a format other than +MTEXT_FORMAT_UTF_8. + +** Allow regex for font properties. + +** Should we change the type of property value from (void *) to +MPropValue? + +** Vietnames FLT needs compositions. + +** Lock and unlock M-text. + +** Improve the handling of scratch gstring. + +** Imporve the font selection for characters of the same script. + + +* done + + +* Copyright information + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..38e046a --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# bootstrap.sh -- shell script to build the m17n library from CVS. +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 +# See the end for copying conditions. + +echo "Running libtoolize..." +libtoolize --automake +echo "Running aclocal..." +aclocal +echo "Running autoheader..." +autoheader +echo "Running automake..." +automake -a +echo "Running autoconf..." +autoconf +echo "The remaining steps to install this library are:" +echo " % ./configure" +echo " % make" +echo " % make install" + +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 + +# This file is part of the m17n library. + +# The m17n library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. + +# The m17n library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the m17n library; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..aa89e21 --- /dev/null +++ b/configure.ac @@ -0,0 +1,212 @@ +dnl configure.ac -- autoconf script for the m17n library. + +dnl Copyright (C) 2003, 2004 +dnl National Institute of Advanced Industrial Science and Technology (AIST) +dnl Registration Number H15PRO112 + +dnl This file is part of the m17n library. + +dnl The m17n library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public License +dnl as published by the Free Software Foundation; either version 2.1 of +dnl the License, or (at your option) any later version. + +dnl The m17n library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. + +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with the m17n library; if not, write to the Free +dnl Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +dnl 02111-1307, USA. + +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(m17n-lib, 1.0, m17n-lib-bug@m17n.org) +AM_INIT_AUTOMAKE(m17n-lib, 1.0) +AM_CONFIG_HEADER(src/config.h) + +# Checks for programs for compiling. +AC_PROG_CC +AC_LIBTOOL_DLOPEN +AC_LIBLTDL_CONVENIENCE +AM_PROG_LIBTOOL + +# Checks for X libraries. +AC_PATH_XTRA + +# Checks for standard header files. +AC_HEADER_STDC +AC_HEADER_DIRENT +AC_HEADER_TIME +AC_CHECK_HEADERS([fcntl.h langinfo.h limits.h locale.h stdlib.h \ + string.h strings.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_STRUCT_TM + +# Checks for endian. This influence the default UTF-16 definition. +AC_C_BIGENDIAN + +# Checks for library functions. +AC_FUNC_ALLOCA +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_FUNC_MEMCMP +AC_FUNC_STAT +AC_FUNC_STRCOLL +AC_FUNC_STRFTIME +AC_FUNC_STRTOD +AC_CHECK_FUNCS(memmove memset nl_langinfo putenv regcomp setlocale) +AC_CHECK_FUNCS(strchr strdup gettimeofday) + +# Check several libraries without adding -lxxx to LIBS, without +# defining HAVE_LIBXXX nor HAVE_XXX_H. Instead, define XXX_LD_FLAGS +# and HAVE_XXX if library XXX is available. + +# Check for fribidi library. +AC_CHECK_LIB(fribidi, fribidi_set_mirroring, HAVE_FRIBIDI=yes, HAVE_FRIBIDI=no) +AC_CHECK_HEADER(fribidi/fribidi.h,, HAVE_FRIBIDI=no) +if test "x$HAVE_FRIBIDI" = "xyes"; then + AC_DEFINE(HAVE_FRIBIDI, 1, + [Define to 1 if you have Fribidi library and header file.]) + FRIBIDI_LD_FLAGS="-lfribidi"; +fi +AC_SUBST(FRIBIDI_LD_FLAGS) + +# Check for otflib usability. +AC_CHECK_LIB(otf, OTF_open, HAVE_OTF=yes, HAVE_OTF=no) +save_CPPFLAGS=$CPPFLAGS +AC_CHECK_PROG(HAVE_OTFLIB_CONFIG, libotf-config, yes) +if test "x$HAVE_OTFLIB_CONFIG" = "xyes"; then + OTFLIB_INC=`libotf-config --cflags` + CPPFLAGS="$CPPFLAGS $OTFLIB_INC" +fi +AC_CHECK_HEADER(otf.h,, HAVE_OTF=no) +CPPFLAGS=$save_CPPFLAGS +if test "x$HAVE_OTF" = "xyes"; then + AC_DEFINE(HAVE_OTF, 1, + [Define to 1 if you have OTF library and header file.]) + OTF_LD_FLAGS="-lotf"; +fi +AC_SUBST(OTF_LD_FLAGS) + +# Check for Freetype2 usability. +AC_CHECK_PROG(HAVE_FREETYPE_CONFIG, freetype-config, yes) +if test "x$HAVE_FREETYPE_CONFIG" = "xyes"; then + FREETYPE_INC=`freetype-config --cflags` + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $FREETYPE_INC" + AC_CHECK_HEADER(ft2build.h, HAVE_FREETYPE=yes, + HAVE_FREETYPE=no CPPFLAGS=$save_CPPFLAGS) + if test "x$HAVE_FREETYPE" = "xyes" ; then + FREETYPE_LD_FLAGS=`freetype-config --libs`; + save_LIBS=$LIBS + LIBS="$LIBS $FREETYPE_LD_FLAGS" + AC_CHECK_LIB(freetype, FT_Init_FreeType, HAVE_FREETYPE=yes, + HAVE_FREETYPE=no) + LIBS=$save_LIBS + if test "x$HAVE_FREETYPE" = "xyes"; then + AC_DEFINE(HAVE_FREETYPE, 1, + [Define to 1 if you have FreeType library and header file.]) + fi + fi +fi +AC_SUBST(FREETYPE_LD_FLAGS) + +# Check for libxml2 usability. +AC_CHECK_LIB(xml2, xmlParseMemory, HAVE_XML2=yes, HAVE_XML2=no) +if test "x$HAVE_XML2" = "xyes"; then + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS -I/usr/include/libxml2" + AC_CHECK_HEADER(libxml/tree.h,, HAVE_XML2=no, /**/) + if test "x$HAVE_XML2" = "xyes"; then + AC_DEFINE(HAVE_XML2, 1, + [Define to 1 if you have libxml2 library and header file]) + XML2_LD_FLAGS="-lxml2" + else + CPPFLAGS=$save_CPPFLAGS + fi +fi +AC_SUBST(XML2_LD_FLAGS) + +# Check for Anthy usability. +AC_CHECK_LIB(anthydic, anthy_init_sessions, HAVE_ANTHY=yes, HAVE_ANTHY=no) +if test "x$HAVE_ANTHY" = "xyes"; then + AC_CHECK_LIB(anthy, anthy_init, HAVE_ANTHY=yes, HAVE_ANTHY=no, -lanthydic) + if test "x$HAVE_ANTHY" = "xyes"; then + AC_CHECK_HEADER(anthy/anthy.h, HAVE_ANTHY=yes, HAVE_ANTHY=no) + if test "x$HAVE_ANTHY" = "xyes"; then + AC_DEFINE(HAVE_ANTHY, 1, + [Define to 1 if you have Anthy library and header file]) + ANTHY_LD_FLAGS="-lanthy -lanthydic" + fi + fi +fi +AC_SUBST(ANTHY_LD_FLAGS) + +# Check for Ispell usability. +AC_CHECK_PROG(HAVE_ISPELL, ispell, yes) +if test "x$HAVE_ISPELL" = "xyes"; then + AC_DEFINE(HAVE_ISPELL, 1, [Define if ispell is available.]) +fi + +# Check for libwordcut (for Thai). +AC_CHECK_LIB(wordcut, wordcut_init, HAVE_WORDCUT=yes, HAVE_WORDCUT=no) +if test "x$HAVE_WORDCUT" = "xyes"; then + if test -f "/usr/share/wordcut/tdict.wcd"; then + tdict="/usr/share/wordcut/tdict.wcd" + elif test -f "/usr/local/share/wordcut/tdict.wcd"; then + tdict="/usr/local/share/wordcut/tdict.wcd" + fi + echo "TDICT=$tdict" + if test "x$tdict" != "x"; then + AC_DEFINE(HAVE_WORDCUT, 1, + [Define if you have the wordcut library and header file]) + AC_DEFINE_UNQUOTED(WORDCUT_TDICT, "$tdict", [Define to tdict file name]) + WORDCUT_LD_FLAGS=-lwordcut + fi +fi +AC_SUBST(WORDCUT_LD_FLAGS) + +AC_ARG_ENABLE(xom, + [ --enable-xom build and install XOM library.], + XOM="$enableval") + +AC_ARG_WITH(efence, + [ --with-efence build example programs with efence.], + EFENCE="$withval") +AC_CHECK_LIB(efence, malloc, HAVE_EFENCE=yes) + +if test "x$EFENCE" = "xyes" && test "x$HAVE_EFENCE" = "xyes"; then + EFENCE_LIB=-lefence +fi +AC_SUBST(EFENCE_LIB) + +case $host_os in +darwin* | rhapsody*) + AC_DEFINE(DLOPEN_SHLIB_EXT, ".dylib", + [Define to loadable module extention]);; +esac + +AC_CONFIG_FILES([Makefile + src/Makefile + example/Makefile + m17n-config + ]) + +if test "x$XOM" = "xyes"; then + AC_CONFIG_FILES(omM17N/Makefile) +fi + +AC_OUTPUT + +dnl Local Variables: +dnl comment-start: "dnl " +dnl comment-end: "" +dnl comment-start-skip: "\\bdnl\\b\\s *" +dnl End: diff --git a/example/.cvsignore b/example/.cvsignore new file mode 100644 index 0000000..4737409 --- /dev/null +++ b/example/.cvsignore @@ -0,0 +1,15 @@ +*.lo +*.la +mconv +mdate +mdump +medit +mview +a.out +stamp-h* +config.h +config.h.in +Makefile.in +Makefile +.deps +.libs diff --git a/example/.gdb.util b/example/.gdb.util new file mode 100644 index 0000000..9fafb7c --- /dev/null +++ b/example/.gdb.util @@ -0,0 +1,57 @@ +define xchartab +set mdebug_dump_chartab ($, 0) +echo \n +end + +define xsymbol +set mdebug_dump_symbol ($, 0) +echo \n +end + +define xmtext +set mdebug_dump_mtext ($, 1, 0) +echo \n +end + +define xplist +set mdebug_dump_plist ($, 0) +echo \n +end + +define xinterval +set dump_interval ($arg0, 0) +echo \n +end + +define xfont +set mdebug_dump_font ($) +echo \n +end + +define xfontset +set mdebug_dump_fontset ($, 0) +echo \n +end + +define xface +set mdebug_dump_face ($, 0) +echo \n +end + +define xgstring +set dump_gstring (gstring, 0) +echo \n +end + +define xim +set mdebug_dump_im ($, 0) +echo \n +end + +define xflush +call XFlush (display) +end + +define xsynch +call XSynchronize (display, 1) +end diff --git a/example/.gdbinit b/example/.gdbinit new file mode 100644 index 0000000..db736ae --- /dev/null +++ b/example/.gdbinit @@ -0,0 +1,23 @@ +set main + +# br mdebug_hook + +source .gdb.util + +set env LD_LIBRARY_PATH=../src/.libs:./.libs:/usr/local/lib + +set env EF_ALLOW_MALLOC_0=1 +set env EF_PROTECT_FREE=1 +set env EF_OVERRUN_DETECTION=1 +set env EF_FREE_WIPES=1 + +set env MDEBUG_INIT=1 +set env MDEBUG_FINI=1 +set env MDEBUG_CHARSET=1 +set env MDEBUG_CODING=1 +set env MDEBUG_DATABASE=1 +set env MDEBUG_FONT=1 +set env MDEBUG_FONT_FLT=0 +set env MDEBUG_FONT_OTF=1 + +set env PURIFYOPTIONS=-chain-length="20" diff --git a/example/ChangeLog b/example/ChangeLog new file mode 100644 index 0000000..ba13825 --- /dev/null +++ b/example/ChangeLog @@ -0,0 +1,25 @@ +2004-03-01 Kenichi Handa + + * Version 1.0 released. + + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/example/HELLO-ja.utf8 b/example/HELLO-ja.utf8 new file mode 100644 index 0000000..102fcbc --- /dev/null +++ b/example/HELLO-ja.utf8 @@ -0,0 +1,30 @@ +ヨーロッパ + 英語 (English) Hello + フランス語 (français) Bonjour, Salut + ドイツ語 (Deutsch) Guten Tag, Grüß Gott + スロバキア語 (slovensky) Dobrý deň + ロシア語 (русский) Здравствуйте! + ギリシャ語 (ελληνικά) Γειάσας + +中近東・アフリカ + トルコ語 (Türkçe) Merhaba + グルジア語 (ქართული) გამარჯობათ! + アラビア語 (اَلْعَرَبِيَّةُ)‎ اَلسَّلَامُ عَلَيْكُم + ペルシャ語 (فارسی)‎ سلام عليکم + ヘブライ語 (עִבְרִית)‎ שָׁלוֹם + アムハラ語 (አማርኛ) ሠላም + +南・東南アジア + ヒンディー語 (हिंदी) नमस्ते, नमस्कार । + マラヤラム語(മലയാളം) നമസ്കാരം + タミール語 (தமிழ்) வணக்கம் + チベット語 (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎ (ex: བསྒྲུབས, ཧཱུཾ) + クメール語 (ខ្មៃរ) ជំរាបសួរ (ex: ក្ក្រឿ៌) + ベトナム語 (Tiếng Việt) Chào bạn + タイ語 (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ + ラオ語 (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ + +東アジア + 日本語 こんにちは + 中国語 (汉语,普通话) 你好 + 韓国語 (한글) 안녕하세요 diff --git a/example/HELLO-ja.xml b/example/HELLO-ja.xml new file mode 100644 index 0000000..9a1eeaf --- /dev/null +++ b/example/HELLO-ja.xml @@ -0,0 +1,66 @@ + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + +ヨーロッパ + 英語 (English) Hello + フランス語 (français) Bonjour, Salut + ドイツ語 (Deutsch) Guten Tag, Grüß Gott + スロバキア語 (slovensky) Dobrý deň + ロシア語 (русский) Здравствуйте! + ギリシャ語 (ελληνικά) Γειάσας + +中近東・アフリカ + トルコ語 (Türkçe) Merhaba + グルジア語 (ქართული) გამარჯობათ! + アラビア語 (اَلْعَرَبِيَّةُ)‎ اَلسَّلَامُ عَلَيْكُم + ペルシャ語 (فارسی)‎ سلام عليکم + ヘブライ語 (עִבְרִית)‎ שָׁלוֹם + アムハラ語 (አማርኛ) ሠላም + +南・東南アジア + ヒンディー語 (हिंदी) नमस्ते, नमस्कार । + マラヤラム語(മലയാളം) നമസ്കാരം + タミール語 (தமிழ்) வணக்கம் + チベット語 (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎ (ex: བསྒྲུབས, ཧཱུཾ) + クメール語 (ខ្មៃរ) ជំរាបសួរ (ex: ក្ក្រឿ៌) + ベトナム語 (Tiếng Việt) Chào bạn + タイ語 (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ + ラオ語 (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ + +東アジア + 日本語 こんにちは + 中国語 (汉语,普通话) 你好 + 韓国語 (한글) 안녕하세요 + diff --git a/example/HELLO.utf8 b/example/HELLO.utf8 new file mode 100644 index 0000000..dac8287 --- /dev/null +++ b/example/HELLO.utf8 @@ -0,0 +1,30 @@ +Europe + English Hello + French (français) Bonjour, Salut + German (Deutsch) Guten Tag, Grüß Gott + Slovak (slovensky) Dobrý deň + Russian (русский) Здравствуйте! + Greek (ελληνικά) Γειάσας + +Middle Near East/Africa + Turkish (Türkçe) Merhaba + Georgian (ქართული) გამარჯობათ! + Arabic (اَلْعَرَبِيَّةُ)‎ اَلسَّلَامُ عَلَيْكُم + Persian (فارسی)‎ سلام عليکم + Hebrew (עִבְרִית)‎ שָׁלוֹם + Amharic (አማርኛ) ሠላም + +South/South-East Asia + Hindi (हिंदी) नमस्ते, नमस्कार । + Malayalam (മലയാളം) നമസ്കാരം (ex: à´¨+്+à´°=ന്ര) + Tamil (தமிழ்) வணக்கம் + Tibetan (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎ (ex: བསྒྲུབས, ཧཱུཾ) + Khmer (ខ្មៃរ) ជំរាបសួរ (ex: ក្ក្រឿ៌) + Vietnamese (Tiếng Việt) Chào bạn + Thai (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ + Lao (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ + +East Asia + Japanese (日本語) こんにちは + Chinese (汉语,普通话) 你好 + Korean (한글) 안녕하세요 diff --git a/example/HELLO.xml b/example/HELLO.xml new file mode 100644 index 0000000..dca1e76 --- /dev/null +++ b/example/HELLO.xml @@ -0,0 +1,66 @@ + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + +Europe + English Hello + French (français) Bonjour, Salut + German (Deutsch) Guten Tag, Grüß Gott + Slovak (slovensky) Dobrý deň + Russian (русский) Здравствуйте! + Greek (ελληνικά) Γειάσας + +Middle Near East/Africa + Turkish (Türkçe) Merhaba + Georgian (ქართული) გამარჯობათ! + Arabic (اَلْعَرَبِيَّةُ)‎ اَلسَّلَامُ عَلَيْكُم + Persian (فارسی)‎ سلام عليکم + Hebrew (עִבְרִית)‎ שָׁלוֹם + Amharic (አማርኛ) ሠላም + +South/South-East Asia + Hindi (हिंदी) नमस्ते, नमस्कार । + Malayalam (മലയാളം) നമസ്കാരം (ex: à´¨+്+à´°=ന്ര) + Tamil (தமிழ்) வணக்கம் + Tibetan (བོད་སྐད་) བཀྲ་ཤིས་བདེ་ལེགས༎ (ex: བསྒྲུབས, ཧཱུཾ) + Khmer (ខ្មៃរ) ជំរាបសួរ (ex: ក្ក្រឿ៌) + Vietnamese (Tiếng Việt) Chào bạn + Thai (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ + Lao (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ + +East Asia + Japanese (日本語) こんにちは + Chinese (汉语,普通话) 你好 + Korean (한글) 안녕하세요 + diff --git a/example/MEdit.ja b/example/MEdit.ja new file mode 100644 index 0000000..5c8e6cf --- /dev/null +++ b/example/MEdit.ja @@ -0,0 +1,153 @@ +! -*- coding: euc-jp; -*- +*.fontSet: -etl-fixed-medium-r-normal--24-*-*-*-*,-*-*-medium-r-normal--24-*-*-*-* +*.international: True +*.File.label: ¥Õ¥¡¥¤¥ë +*.Cursor.label: ¥«¡¼¥½¥ë +*.Bidi.label: ½ñ»úÊý¸þ +*.LineBreak.label: ¹Ôʬ³ä +*.InputMethod.label: ÆþÎϥ᥽¥Ã¥É +*.Face.label: ¥Õ¥§¡¼¥¹ +*.Lang.label: ¸À¸ì +*.Size.label: ¥µ¥¤¥º +*.Family.label: ¥Õ¥¡¥ß¥ê¡¼ +*.Style.label: ¥¹¥¿¥¤¥ë +*.Color.label: ¿§ +*.Misc.label: ¤½¤Î¾ +*.Pop Face.label: ¥Ý¥Ã¥× +*Abkhazian.label:¥¢¥Ö¥Ï¥º¸ì +*Afar.label:¥¢¥Õ¥¡¥ë¸ì +*Afrikaans.label:¥¢¥Õ¥ê¥«¡¼¥ó¥¹¸ì +*Albanian.label:¥¢¥ë¥Ð¥Ë¥¢¸ì +*Amharic.label:¥¢¥à¥Ï¥é¸ì +*Arabic.label:¥¢¥é¥Ó¥¢¸ì +*Armenian.label:¥¢¥ë¥á¥Ë¥¢¸ì +*Assamese.label:¥¢¥Ã¥µ¥à¸ì +*Aymara.label:¥¢¥¤¥Þ¥é¸ì +*Azerbaijani.label:¥¢¥¼¥ë¥Ð¥¤¥¸¥ã¥ó¸ì +*Bashkir.label:¥Ð¥·¥å¥­¡¼¥ë¸ì +*Basque.label:¥Ð¥¹¥¯¸ì +*Bengali.label:¥Ù¥ó¥¬¥ë¸ì +*Bhutani.label:¥Ö¡¼¥¿¥ó¸ì +*Bihari.label:¥Ó¥Ï¡¼¥ë¸ì +*Bislama.label:¥Ó¥¹¥é¥Þ¸ì +*Breton.label:¥Ö¥ë¥È¥ó¸ì +*Bulgarian.label:¥Ö¥ë¥¬¥ê¥¢¸ì +*Burmese.label:¥Ó¥ë¥Þ¸ì +*Byelorussian.label:Çò¥í¥·¥¢¸ì +*Cambodian.label:¥«¥ó¥Ü¥¸¥¢¸ì +*Catalan.label:¥«¥¿¥í¥Ë¥¢¸ì +*Chinese.label:Ãæ¹ñ¸ì +*Corsican.label:¥³¥ë¥·¥«¸ì +*Croatian.label:¥¯¥í¥¢¥Á¥¢¸ì +*Czech.label:¥Á¥§¥³¸ì +*Danish.label:¥Ç¥ó¥Þ¡¼¥¯¸ì +*Dutch.label:¥ª¥é¥ó¥À¸ì +*English.label:±Ñ¸ì +*Esperanto.label:¥¨¥¹¥Ú¥é¥ó¥È +*Estonian.label:¥¨¥¹¥È¥Ë¥¢¸ì +*Faeroese.label:¥Õ¥§¡¼¥í¡¼¸ì +*Farsi.label:¥Ú¥ë¥·¥¢¸ì +*Fiji.label:¥Õ¥£¥¸¡¼¸ì +*Finnish.label:¥Õ¥£¥ó¥é¥ó¥É¸ì +*French.label:¥Õ¥é¥ó¥¹¸ì +*Frisian.label:¥Õ¥ê¥¸¥¢¸ì +*Galician.label:¥¬¥ê¥·¥¢¸ì +*Gaelic(Scottish).label:¥²¡¼¥ë¸ì¡Ê¥¹¥³¥Ã¥È¥é¥ó¥É¡Ë +*Gaelic(Manx).label:¥²¡¼¥ë¸ì¡Ê¥Þ¥óÅç¡Ë +*Georgian.label:¥°¥ë¥¸¥¢¸ì +*German.label:¥É¥¤¥Ä¸ì +*Greek.label:¥®¥ê¥·¥¢¸ì +*Greenlandic.label:¥°¥ê¡¼¥ó¥é¥ó¥É¸ì +*Guarani.label:¥ï¥é¥Ë¡¼¸ì +*Gujarati.label:¥°¥¸¥ã¥é¡¼¥È¸ì +*Hausa.label:¥Ï¥¦¥µ¸ì +*Hebrew.label:¥Ø¥Ö¥é¥¤¸ì +*Hindi.label:¥Ò¥ó¥Ç¥£¡¼¸ì +*Hungarian.label:¥Ï¥ó¥¬¥ê¡¼¸ì +*Icelandic.label:¥¢¥¤¥¹¥é¥ó¥É¸ì +*Indonesian.label:¥¤¥ó¥É¥Í¥·¥¢¸ì +*Inuktitut.label:¥¤¥Ì¥¯¥Æ¥£¥È¥Ã¥È¸ì +*Inupiak.label:¥¤¥Ì¥Ô¥¢¥Ã¥¯¸ì +*Irish.label:¥¢¥¤¥ë¥é¥ó¥É¸ì +*Italian.label:¥¤¥¿¥ê¥¢¸ì +*Japanese.label:ÆüËܸì +*Javanese.label:¥¸¥ã¥ï¸ì +*Kannada.label:¥«¥ó¥Ê¥À¸ì +*Kashmiri.label:¥«¥·¥å¥ß¡¼¥ë¸ì +*Kazakh.label:¥«¥¶¥Õ¸ì +*Kinyarwanda.label:¥ë¥ï¥ó¥À¸ì +*Kirghiz.label:¥­¥ë¥®¥¹¸ì +*Kirundi.label:¥ë¥ó¥Ç¥£¸ì +*Korean.label:Ä«Á¯¸ì +*Kurdish.label:¥¯¥ë¥É¸ì +*Laothian.label:¥é¥ª¸ì +*Latin.label:¥é¥Æ¥ó¸ì +*Latvian.label:¥é¥È¥ô¥£¥¢¸ì +*Lingala.label:¥ê¥ó¥¬¥é¸ì +*Lithuanian.label:¥ê¥È¥¢¥Ë¥¢¸ì +*Macedonian.label:¥Þ¥±¥É¥Ë¥¢¸ì +*Malagasy.label:¥Þ¥é¥¬¥·¸ì +*Malay.label:¥à¥é¥æ¸ì +*Malayalam.label:¥Þ¥é¥ä¡¼¥é¥à¸ì +*Maltese.label:¥Þ¥ë¥¿¸ì +*Maori.label:¥Þ¥ª¥ê¸ì +*Marathi.label:¥Þ¥é¡¼¥Æ¥£¡¼¸ì +*Moldavian.label:¥â¥ë¥À¥Ó¥¢¸ì +*Mongolian.label:¥â¥ó¥´¥ë¸ì +*Nauru.label:¥Ê¥¦¥ë¸ì +*Nepali.label:¥Í¥Ñ¡¼¥ë¸ì +*Norwegian.label:¥Î¥ë¥¦¥§¡¼¸ì +*Occitan.label:¥×¥í¥ô¥¡¥ó¥¹¸ì +*Oriya.label:¥ª¥ê¥ä¡¼¸ì +*Oromo.label:¥¬¥Ã¥é¸ì +*Pashto.label:¥Ñ¥·¥å¥È¡¼¸ì +*Polish.label:¥Ý¡¼¥é¥ó¥É¸ì +*Portuguese.label:¥Ý¥ë¥È¥¬¥ë¸ì +*Punjabi.label:¥Ñ¥ó¥¸¥ã¡¼¥Ö¸ì +*Quechua.label:¥±¥Á¥å¥¢¸ì +*Rhaeto-Romance.label:¥ì¥È¡¦¥í¥Þ¥ó¥¹¸ì +*Romanian.label:¥ë¡¼¥Þ¥Ë¥¢¸ì +*Russian.label:¥í¥·¥¢¸ì +*Samoan.label:¥µ¥â¥¢¸ì +*Sangro.label:¥µ¥ó¥´¸ì +*Sanskrit.label:¥µ¥ó¥¹¥¯¥ê¥Ã¥È +*Serbian.label:¥»¥ë¥Ó¥¢¸ì +*Serbo-Croatian.label:¥»¥ë¥Ó¥¢¡¦¥¯¥í¥¢¥Á¥¢¸ì +*Sesotho.label:¥½¥È¸ì +*Setswana.label:¥Ä¥ï¥Ê¸ì +*Shona.label:¥·¥ç¥Ê¸ì +*Sindhi.label:¥·¥ó¥É¸ì +*Sinhalese.label:¥·¥ó¥Ï¥é¸ì +*Siswati.label:¥¹¥ï¥Æ¥£¸ì +*Slovak.label:¥¹¥í¥Ð¥­¥¢¸ì +*Slovenian.label:¥¹¥í¥Ù¥Ë¥¢¸ì +*Somali.label:¥½¥Þ¥ê¸ì +*Spanish.label:¥¹¥Ú¥¤¥ó¸ì +*Sundanese.label:¥¹¥ó¥À¸ì +*Swahili.label:¥¹¥ï¥Ò¥ê¸ì +*Swedish.label:¥¹¥¦¥§¡¼¥Ç¥ó¸ì +*Tagalog.label:¥¿¥¬¥í¥°¸ì +*Tajik.label:¥¿¥¸¥¯¸ì +*Tamil.label:¥¿¥ß¡¼¥ë¸ì +*Tatar.label:¥¿¥¿¡¼¥ë¸ì +*Telugu.label:¥Æ¥ë¥°¸ì +*Thai.label:¥¿¥¤¸ì +*Tibetan.label:¥Á¥Ù¥Ã¥È¸ì +*Tigrinya.label:¥Æ¥£¥°¥ê¥Ë¥¢¸ì +*Tonga.label:¥È¥ó¥¬¸ì +*Tsonga.label:¥Ä¥©¥ó¥¬¸ì +*Turkish.label:¥È¥ë¥³¸ì +*Turkmen.label:¥È¥ë¥¯¥á¥ó¸ì +*Twi.label:¥Á¥å¥¤¸ì +*Uighur.label:¥¦¥¤¥°¥ë¸ì +*Ukrainian.label:¥¦¥¯¥é¥¤¥Ê¸ì +*Urdu.label:¥¦¥ë¥É¥¥¡¼¸ì +*Uzbek.label:¥¦¥º¥Ù¥¯¸ì +*Vietnamese.label:¥Ù¥È¥Ê¥à¸ì +*Volapuk.label:¥ô¥©¥é¥Ô¥å¥¯ +*Welsh.label:¥¦¥§¡¼¥ë¥º¸ì +*Wolof.label:¥¦¥©¥í¥Õ¸ì +*Xhosa.label:¥³¥µ¸ì +*Yiddish.label:¥¤¥Ç¥£¥Ã¥·¥å¸ì +*Yoruba.label:¥è¥ë¥Ð¸ì +*Zulu.label:¥º¡¼¥ë¡¼¸ì diff --git a/example/Makefile.am b/example/Makefile.am new file mode 100644 index 0000000..94c5629 --- /dev/null +++ b/example/Makefile.am @@ -0,0 +1,76 @@ +# Makefile.am -- example level Makefile for the m17n library. +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 + +# This file is part of the m17n library. + +# The m17n library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. + +# The m17n library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the m17n library; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. + +## Process this file with Automake to create Makefile.in + +bin_PROGRAMS = mconv mview mdate mdump medit + +common_ldflags = ${top_srcdir}/src/libm17n-core.la ${top_srcdir}/src/libm17n.la +common_ldflags_X = ${top_srcdir}/src/libm17n-X.la + +mdate_SOURCES = mdate.c +mdate_LDADD = ${common_ldflags} + +mconv_SOURCES = mconv.c +mconv_LDADD = ${common_ldflags} + +medit_SOURCES = medit.c linebreak.c +medit_LDADD = ${common_ldflags_X} +medit_LDFLAGS = -lXaw -lXmu @WORDCUT_LD_FLAGS@ + +mview_SOURCES = mview.c +mview_LDADD = ${common_ldflags_X} +mview_LDFLAGS = -lXaw -lXmu + +mdump_SOURCES = mdump.c linebreak.c +mdump_LDADD = ${common_ldflags_X} @WORDCUT_LD_FLAGS@ + +# Input method data files. + +pkgdatadir=$(datadir)/m17n + +EXTRA_DIST = \ + .gdbinit .gdb.util \ + HELLO.utf8 HELLO.xml HELLO-ja.utf8 HELLO-ja.xml MEdit.ja + +# External modules used by the above input methods. + +lib_LTLIBRARIES = libmimx-ispell.la libmimx-anthy.la + +libmimx_ispell_la_SOURCES = mimx-ispell.c +libmimx_ispell_la_LIBADD = ${common_ldflags_X} + +libmimx_anthy_la_SOURCES = mimx-anthy.c +libmimx_anthy_la_LIBADD = ${common_ldflags} +libmimx_anthy_la_LDFLAGS = @ANTHY_LD_FLAGS@ + +# Special targets to test the m17n library with Purify. They are for +# the maintainers only. + +PURIFY=/usr/local/rational/releases/purify.sol.2002.05.00/purify + +purify_medit: medit ../src/.libs/libm17n-core.so ../src/.libs/libm17n.so ../src/.libs/libm17n-X.so linebreak.c + ${PURIFY} gcc -g medit.o linebreak.o -lXaw -lXmu -L/usr/X11R6/lib -R/usr/X11R6/lib -lSM -lICE -lX11 -lXt -L../src/.libs -lm17n-core -lm17n -lm17n-X -ldl + +purify_mdate: mdate.o ../src/.libs/libm17n.so ../src/.libs/libm17n-X.so + ${PURIFY} gcc -g mdate.o -lXaw -lXmu -L/usr/X11R6/lib -R/usr/X11R6/lib -lSM -lICE -lX11 -lXt -L../src/.libs -lm17n -lm17n-X -ldl + diff --git a/example/linebreak.c b/example/linebreak.c new file mode 100644 index 0000000..e5d3efa --- /dev/null +++ b/example/linebreak.c @@ -0,0 +1,123 @@ +#include + +#include + +#ifdef HAVE_WORDCUT + +#define THAI_BEG 0x0E00 +#define THAI_END 0x0E6F + +static int wordcut_initiazlied = 0; + +#include +#include +#include + +static Wordcut wordcut; + +static MSymbol Mwordcut_wordbeg, Miso_8859_11; + +static void +init_th_wordcut () +{ + Mwordcut_wordbeg = msymbol (" wordcut-wordseg"); + Miso_8859_11 = msymbol ("iso-8859-11"); +} + +int +thai_line_break (MText *mt, int pos, int from, int to) +{ + WordcutResult result; + MTextProperty *prop; + int pos1, pos2, c, i; + unsigned char *str; + + if (wordcut_initiazlied < 0) + return pos; + if (! wordcut_initiazlied) + { + if (wordcut_init (&wordcut, WORDCUT_TDICT) != 0) + { + wordcut_initiazlied = -1; + return pos; + } + init_th_wordcut (); + wordcut_initiazlied = 1; + } + prop = mtext_get_property (mt, pos, Mwordcut_wordbeg); + if (prop) + { + pos1 = mtext_property_start (prop); + if (pos1 == from) + pos1 = pos; + return pos1; + } + str = alloca (to - from); + pos1 = pos - 1; + while (pos1 >= from + && (c = mtext_ref_char (mt, pos1)) >= THAI_BEG && c <= THAI_END) + str[pos1-- - from] = mchar_encode (Miso_8859_11, c); + pos1++; + pos2 = pos; + while (pos2 < to + && (c = mtext_ref_char (mt, pos2)) >= 0x0E00 && c <= 0x0E6F) + str[pos2++ - from] = mchar_encode (Miso_8859_11, c); + str[pos2 - from] = 0; + wordcut_cut (&wordcut, (char *) (str + pos1 - from), &result); + for (i = 0; i < result.count; i++) + { + int start = pos1 + result.start[i]; + + prop = mtext_property (Mwordcut_wordbeg, Mt, + MTEXTPROP_VOLATILE_WEAK | MTEXTPROP_NO_MERGE); + mtext_attach_property (mt, start, start + result.offset[i], prop); + m17n_object_unref (prop); + } + prop = mtext_get_property (mt, pos, Mwordcut_wordbeg); + pos1 = mtext_property_start (prop); + if (pos1 == from) + pos1 = pos; + return pos1; +} + +#define CHECK_THAI_LINE_BREAK(c, mt, pos, from, to) \ + do { \ + if ((c) >= THAI_BEG && (c) <= THAI_END) \ + return thai_line_break ((mt), (pos), (from), (to)); \ + } while (0) + +#else /* not HAVE_WORDCUT */ + +#define CHECK_THAI_LINE_BREAK(c, mt, pos, from, to) (void) 0 + +#endif /* not HAVE_WORDCUT */ + +int +line_break (MText *mt, int pos, int from, int to, int line, int y) +{ + int c = mtext_ref_char (mt, pos); + int orig_pos = pos; + + if (c == ' ' || c == '\t' || c == '\n') + { + for (pos++; pos < to; pos++) + if ((c = mtext_ref_char (mt, pos)) != ' ' && c != '\t' && c != '\n') + break; + } + else + { + while (pos > from) + { + if (c == ' ' || c == '\t') + break; + CHECK_THAI_LINE_BREAK (c, mt, pos, from, to); + pos--; + c = mtext_ref_char (mt, pos); + } + if (pos == from) + pos = orig_pos; + else + pos++; + } + return pos; +} diff --git a/example/mconv.c b/example/mconv.c new file mode 100644 index 0000000..7dca6c4 --- /dev/null +++ b/example/mconv.c @@ -0,0 +1,346 @@ +/* mconv.c -- Code converter. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mconv convert file code + + @section mconv-synopsis SYNOPSIS + + mconv [ OPTION ... ] [ INFILE [ OUTFILE ] ] + + @section mconv-description DESCRIPTION + + Convert encoding of given files from one to another. + + If INFILE is omitted, the input is taken from standard input. If + OUTFILE is omitted, the output written to standard output. + + The following OPTIONs are available. + +
    + +
  • -f FROMCODE + + FROMCODE is the encoding of INFILE (defaults to UTF-8). + +
  • -t TOCODE + + TOCODE is the encoding of OUTFILE (defaults to UTF-8). + +
  • -k + + Do not stop conversion on error. + +
  • -s + + Suppress warnings. + +
  • -v + + Print progress information. + +
  • -l + + List available encodings. + +
  • --version + + Print version number. + +
  • -h, --help + + Print this message. + +
+*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include + +#include +#include + +#define VERSION "1.0" + +/* Print all coding system names. */ + +void +list_coding () +{ + MSymbol *codings; + int i, n; + char *name; + int len, clm; + + n = mconv_list_codings (&codings); + clm = 0; + for (i = 0; i < n; i++) + { + name = msymbol_name (codings[i]); + len = strlen (name) + 1; + if (clm + len >= 80) + { + printf ("\n"); + clm = 0; + } + printf (" %s", name); + clm += len; + } + printf ("\n"); + free (codings); +} + + +/* Print the usage of this program (the name is PROG), and exit with + EXIT_CODE. */ + +void +help_exit (char *prog, int exit_code) +{ + char *p = prog; + + while (*p) + if (*p++ == '/') + prog = p; + + printf ("Usage: %s [ OPTION ... ] [ INFILE [ OUTFILE ] ]\n", prog); + printf ("Convert encoding of given files from one to another.\n"); + printf (" If INFILE is omitted, the input is taken from standard input.\n"); + printf (" If OUTFILE is omitted, the output is written to standard output.\n"); + printf ("The following OPTIONs are available.\n"); + printf (" %-13s %s", "-f FROMCODE", + "FROMCODE is the encoding of INFILE (defaults to UTF-8).\n"); + printf (" %-13s %s", "-t TOCODE", + "TOCODE is the encoding of OUTFILE (defaults to UTF-8).\n"); + printf (" %-13s %s", "-k", "Do not stop conversion on error.\n"); + printf (" %-13s %s", "-s", "Suppress warnings.\n"); + printf (" %-13s %s", "-v", "Print progress information.\n"); + printf (" %-13s %s", "-l", "List available encodings.\n"); + printf (" %-13s %s", "--version", "Print version number.\n"); + printf (" %-13s %s", "-h, --help", "Print this message.\n"); + exit (exit_code); +} + + +/* Check invalid bytes found in the last decoding. Text property + Mcharset of such a byte is Mcharset_binary. */ + +void +check_invalid_bytes (MText *mt) +{ + int from = 0, to = 0; + int len = mtext_len (mt); + int first = 1; + + while (to < len) + { + int n = mtext_prop_range (mt, Mcharset, from, NULL, &to, 1); + MSymbol charset + = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcharset) : Mnil; + + if (charset == Mcharset_binary) + { + if (first) + { + fprintf (stderr, + "Invalid bytes (at each character position);\n"); + first = 0; + } + for (; from < to; from++) + fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from); + } + else + from = to; + } + if (! first) + fprintf (stderr, "\n"); +} + + +/* Check unencoded characters in the last encoding. Text property + Mcoding of such a character is Mnil. */ + +void +check_unencoded_chars (MText *mt, int len) +{ + int from = 0, to = 0; + int first = 1; + + while (to < len) + { + int n = mtext_prop_range (mt, Mcoding, from, NULL, &to, 1); + MSymbol coding + = n > 0 ? (MSymbol) mtext_get_prop (mt, from, Mcoding) : Mnil; + + if (coding == Mnil) + { + if (first) + { + fprintf (stderr, + "Unencoded characters (at each character position):\n"); + first = 0; + } + for (; from < to; from++) + fprintf (stderr, " 0x%02X(%d)", mtext_ref_char (mt, from), from); + } + else + from = to; + } + if (! first) + fprintf (stderr, "\n"); +} + + +/* Format MSG by FMT and print the result to the stderr, and exit. */ + +#define FATAL_ERROR(fmt, arg) \ + do { \ + fprintf (stderr, fmt, arg); \ + exit (1); \ + } while (0) + + +int +main (int argc, char **argv) +{ + int suppress_warning, verbose, continue_on_error; + MSymbol incode, outcode; + FILE *in, *out; + MText *mt; + MConverter *converter; + int i; + + /* Initialize the m17n library. */ + M17N_INIT (); + if (merror_code != MERROR_NONE) + FATAL_ERROR ("%s\n", "Fail to initialize the m17n library."); + + /* Default encodings are both UTF-8. */ + incode = outcode = Mcoding_utf_8; + /* By default, read from standard input and write to standard output. */ + in = stdin, out = stdout; + /* By default, all these flags are 0. */ + suppress_warning = verbose = continue_on_error = 0; + /* Parse the command line arguments. */ + for (i = 1; i < argc; i++) + { + if (! strcmp (argv[i], "--help") + || ! strcmp (argv[i], "-h") + || ! strcmp (argv[i], "-?")) + help_exit (argv[0], 0); + else if (! strcmp (argv[i], "--version")) + { + printf ("mconv (m17n library) %s\n", VERSION); + printf ("Copyright (C) 2003 AIST, JAPAN\n"); + exit (0); + } + else if (! strcmp (argv[i], "-l")) + { + list_coding (); + M17N_FINI (); + exit (0); + } + else if (! strcmp (argv[i], "-f")) + { + incode = mconv_resolve_coding (msymbol (argv[++i])); + if (incode == Mnil) + FATAL_ERROR ("Unknown encoding: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-t")) + { + outcode = mconv_resolve_coding (msymbol (argv[++i])); + if (outcode == Mnil) + FATAL_ERROR ("Unknown encoding: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-k")) + continue_on_error = 1; + else if (! strcmp (argv[i], "-s")) + suppress_warning = 1; + else if (! strcmp (argv[i], "-v")) + verbose = 1; + else if (argv[i][0] != '-') + { + if (in == stdin) + { + in = fopen (argv[i], "r"); + if (! in) + FATAL_ERROR ("Can't read the file %s\n", argv[i]); + } + else if (out == stdout) + { + out = fopen (argv[i], "w"); + if (! out) + FATAL_ERROR ("Can't write the file %s\n", argv[i]); + } + else + help_exit (argv[0], 1); + } + else + help_exit (argv[0], 1); + } + + /* Create an M-text to store the decoded characters. */ + mt = mtext (); + + /* Create a converter for decoding. */ + converter = mconv_stream_converter (incode, in); + /* Instead of doing strict decoding, we decode all input bytes at + once, and check invalid bytes later by the fuction + check_invalid_bytes. */ + converter->lenient = 1; + + mconv_decode (converter, mt); + + if (! suppress_warning) + check_invalid_bytes (mt); + if (verbose) + fprintf (stderr, "%d bytes (%s) decoded into %d characters,\n", + converter->nbytes, msymbol_name (incode), mtext_len (mt)); + + mconv_free_converter (converter); + + /* Create a converter for encoding. */ + converter = mconv_stream_converter (outcode, out); + /* Instead of doing strict encoding, we encode all characters at + once, and check unencoded characters later by the fuction + check_unencoded_chars. */ + converter->lenient = 1; + converter->last_block = 1; + if (mconv_encode (converter, mt) < 0 + && ! suppress_warning) + fprintf (stderr, "I/O error on writing\n"); + if (! suppress_warning) + check_unencoded_chars (mt, converter->nchars); + if (verbose) + fprintf (stderr, "%d characters encoded into %d bytes (%s).\n", + converter->nchars, converter->nbytes, msymbol_name (outcode)); + + /* Clear away. */ + mconv_free_converter (converter); + m17n_object_unref (mt); + M17N_FINI (); + exit (0); +} +#endif /* not FOR_DOXYGEN */ diff --git a/example/mdate.c b/example/mdate.c new file mode 100644 index 0000000..6f46599 --- /dev/null +++ b/example/mdate.c @@ -0,0 +1,215 @@ +/* mdate.c -- Show the sysmet date and time in all locales. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mdate display date and time + + @section mdate-synopsis SYNOPSIS + + mdate [ OPTION ... ] + + @section mdate-description DESCRIPTION + + Display the system date and time in many locales on a window. + + The following OPTIONs are available. + +
    + +
  • --version + + Print version number. + +
  • -h, --help + + Print this message. +
+*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define VERSION "1.0" + +/* Return a plist of all locales currently avairable on the system. + The keys and values of the plist are locale name symbols and + pointers to MLocale respectively. */ + +MPlist * +list_system_locales () +{ + FILE *p; + char buf[1024]; + MPlist *plist; + + /* Run the command "locale -a" to get a list of locales. */ + if (! (p = popen ("locale -a", "r"))) + { + fprintf (stderr, "Can't run `locale -a'.\n"); + exit (1); + } + + plist = mplist (); + /* Read from the pipe one line by one. */ + while (fgets (buf, 1024, p)) + { + MLocale *locale; + int len = strlen (buf); + + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + locale = mlocale_set (LC_TIME, buf); + + /* If the locale is surely usable, it is not duplicated, and we + know the corresponding coding system, add it to the list. */ + if (locale + && ! mplist_get (plist, mlocale_get_prop (locale, Mname)) + && mlocale_get_prop (locale, Mcoding) != Mnil) + mplist_add (plist, mlocale_get_prop (locale, Mname), locale); + } + pclose (p); + return plist; +} + + +/* Print the usage of this program (the name is PROG), and exit with + EXIT_CODE. */ + +void +help_exit (char *prog, int exit_code) +{ + char *p = prog; + + while (*p) + if (*p++ == '/') + prog = p; + + printf ("Usage: %s [ OPTION ...]\n", prog); + printf ("Display the system date and time in many locales on a window.\n"); + printf ("The following OPTIONs are available.\n"); + printf (" %-13s %s", "--version", "Print version number.\n"); + printf (" %-13s %s", "-h, --help", "Print this message.\n"); + exit (exit_code); +} + + +/* Format MSG by FMT and print the result to the stderr, and exit. */ + +#define FATAL_ERROR(fmt, arg) \ + do { \ + fprintf (stderr, fmt, arg); \ + exit (1); \ + } while (0) + + +int +main (int argc, char **argv) +{ + time_t current_time_t = time (NULL); + struct tm *current_time_tm = localtime (¤t_time_t); + /* List of all locales. */ + MPlist *locale_list, *plist; + /* Text to be shown. */ + MText *mt; + int i; + + for (i = 1; i < argc; i++) + { + if (! strcmp (argv[i], "--help") + || ! strcmp (argv[i], "-h") + || ! strcmp (argv[i], "-?")) + help_exit (argv[0], 0); + else if (! strcmp (argv[i], "--version")) + { + printf ("mdate (m17n library) %s\n", VERSION); + printf ("Copyright (C) 2003 AIST, JAPAN\n"); + exit (0); + } + else + help_exit (argv[0], 1); + } + + + /* Initialize the m17n library. */ + M17N_INIT (); + if (merror_code != MERROR_NONE) + FATAL_ERROR ("%s\n", "Fail to initialize the m17n library."); + + /* Get a local list in LOCALE_LIST, and generate an M-text that + contains date string in each locale. */ + locale_list = list_system_locales (); + mt = mtext (); + plist = locale_list; + while (mplist_key (plist) != Mnil) + { + char *name = msymbol_name (mplist_key (plist)); + char fmtbuf[256]; + int len; + MLocale *locale = mplist_value (plist); + MSymbol coding = mlocale_get_prop (locale, Mcoding); + /* One line text for this locale. The format is: + "LOCALE-NAME: DATE-AND-TIME-STRING". */ + MText *thisline; + + sprintf (fmtbuf, "%16s: ", name); + len = strlen (fmtbuf); + thisline = mconv_decode_buffer (coding, (unsigned char *) fmtbuf, len); + if (thisline) + { + mlocale_set (LC_TIME, name); + if (mtext_ftime (thisline, "%c", current_time_tm, NULL) > 0) + { + mtext_cat_char (thisline, '\n'); + mtext_cat (mt, thisline); + } + m17n_object_unref (thisline); + } + plist = mplist_next (plist); + } + + /* Show the generated M-text by another example program "mview". */ + { + FILE *p = popen ("mview", "w"); + + if (!p) + FATAL_ERROR ("%s\n", "Can't run the program mview!"); + mconv_encode_stream (Mcoding_utf_8, mt, p); + fclose (p); + } + + /* Clear away. */ + m17n_object_unref (locale_list); + m17n_object_unref (mt); + M17N_FINI (); + + exit (0); +} +#endif /* not FOR_DOXYGEN */ diff --git a/example/mdump.c b/example/mdump.c new file mode 100644 index 0000000..c402338 --- /dev/null +++ b/example/mdump.c @@ -0,0 +1,605 @@ +/* mdump.c -- Dump text image + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mdump dump text image + + @section mdump-synopsis SYNOPSIS + + mdump [ OPTION ... ] [ FILE ] + + @section mdump-description DESCRIPTION + + Dump a text as a Netpbm image. + + The Netpbm image is written to a file created in the current + directory with the name "BASE.pbm" where BASE is the basename of + FILE. If FILE is omitted, text is read from standard input, and + the image is dumped into the file "output.pbm". + + The following OPTIONs are available. + +
    + +
  • -s SIZE + + SIZE is the font size in point. The default font size is 12 point. + +
  • -d DPI + + DPI is the resolution in dots per inch. The default resolution is + 300 dpi. + +
  • -p PAPER + + PAPER is the paper size: a4, a4r, a5, a5r, b5, b5r, letter, or + WxH. In the last case, W and H are the width and height in + millimeter. If this option is specified, PAPER limits the image + size. If FILE is too large for a single page, multiple files with + the names "BASE.01.pbm", "BASE.02.pbm", etc. are created. + +
  • -m MARGIN + + MARGIN is the horizontal and vertical margin in millimeter. The + default margin is 20 mm. It is ignored when PAPER is not + specified. + +
  • -c POS + + POS is the character position of cursor to draw. By default, + cursor is not drawn. + +
  • -x + + FILE is assumed to be an XML file generated by the serialize + facility of the m17n library, and FILE is deserialized before the + image is created. + +
  • -w + + Each line is broken at word boundary. + +
  • -f FILTER + + FILTER is a string containing a shell command line. If this + option is specified, the Netpbm image is not written info a + file but is given to FILTER as standard input. If FILTER + contains "%s", that part is replaced by a basename of FILE. + So, the default behaviour is the same as specifying "cat > + %s.pbm" as FILTER. + +
  • -q + + Quiet mode. Don't print any messages. + +
  • --version + + Print the version number. + +
  • -h, --help + + Print this message. + +
+*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include +#include + +#include + +#include +#include + +#define VERSION "1.0" + +/* Enumuration of the supported paper types. */ +enum paper_type + { + PAPER_A4, + PAPER_A4R, + PAPER_A5, + PAPER_A5R, + PAPER_B5, + PAPER_B5R, + PAPER_LETTER, + PAPER_USER, + PAPER_NOLIMIT + }; + +/* Array of paper sizes for the supported paper types. */ +struct +{ + int width, height; /* in millimeter */ +} paper_size[PAPER_NOLIMIT] = { + { 210, 297 }, /* a4 */ + { 297, 210 }, /* a4r */ + { 148, 210 }, /* a5 */ + { 210, 148 }, /* a5r */ + { 250, 176 }, /* b5 */ + { 176, 250 }, /* b5r */ + { 216, 279 }, /* letter */ +}; + + +/* Print the usage of this program (the name is PROG), and exit with + EXIT_CODE. */ + +void +help_exit (char *prog, int exit_code) +{ + char *p = prog; + + while (*p) + if (*p++ == '/') + prog = p; + + printf ("Usage: %s [ OPTION ...] [ FILE ]\n", prog); + printf ("Dump a text as a Netpbm image into a PBM file.\n"); + printf (" The PBM file is created in the current directory\n"); + printf (" with the name \"BASE.pbm\" where BASE is the basename of FILE.\n"); + printf (" If FILE is omitted, text is read from standard input, and\n"); + printf (" dumped into the file \"output.pbm\".\n"); + printf ("The following OPTIONs are available.\n"); + printf (" %-13s %s", "-s SIZE", + "Font size in point (default 12).\n"); + printf (" %-13s %s", "-d DPI", + "Resolution in dots per inch (defualt 300).\n"); + printf (" %-13s %s", "-p PAPER", + "Paper size; a4, a4r, a5, a5r, b5, b5r, letter, or WxH.\n"); + printf (" %-13s %s", "-m MARGIN", + "Marginal space in millimeter (default 20).\n"); + printf (" %-13s %s", "-c POS", + "Character position of cursor to draw (default no cursor)\n"); + printf (" %-13s %s", "-x", + "FILE is assumed to be an XML file.\n"); + printf (" %-13s %s", "-f FILTER", + "String containing a shell command line to be used as a filter.\n"); + printf (" %-13s %s", "-w", "Each line is broken at word boundary.\n"); + printf (" %-13s %s", "-q", "Quiet mode. Don't print any messages.\n"); + printf (" %-13s %s", "--version", "Print the version number.\n"); + printf (" %-13s %s", "-h, --help", "Print this message.\n"); + exit (exit_code); +} + + +/* Format MSG by FMT and print the result to the stderr, and exit. */ + +#define FATAL_ERROR(fmt, arg) \ + do { \ + fprintf (stderr, fmt, arg); \ + exit (1); \ + } while (0) + + +/* Move POS to the next line head in M-text MT whose length is LEN. + If POS is already on the last line, set POS to LEN. */ + +#define NEXTLINE(pos, len) \ + do { \ + pos = mtext_character (mt, pos, len, '\n'); \ + if (pos < 0) \ + pos = len; \ + else \ + pos++; \ + } while (0) + + +/* Find the range of M-text MT that fits in one page of height HEIGHT + when drawn from the character position POS. Set RECT->y to the + Y-offset of the first baseline. */ + +int +find_page_end (MFrame *frame, int height, MText *mt, int pos, + MDrawControl *control, MDrawMetric *rect) +{ + int len = mtext_len (mt); + int to = pos; + int y = 0, yoff; + + while (to < len) + { + int next = to; + + NEXTLINE (next, len); + mdraw_text_extents (frame, mt, to, next, control, NULL, NULL, rect); + if (to == pos) + yoff = rect->y; + if (y + rect->height > height) + { + MDrawGlyphInfo info; + + while (to < next) + { + mdraw_glyph_info (frame, mt, to, to, control, &info); + if (y + info.this.height > height) + break; + y += info.this.height; + to = info.line_to; + } + break; + } + y += rect->height; + to = next; + } + + rect->y = yoff; + return to; +} + + +/* Table to convert a byte of LSBFirst to MSBFirst. */ +char reverse_bit_order[256]; + +/* Initialize the above table. */ + +void +init_reverse_bit_order () +{ + int i; + + for (i = 0; i < 256; i++) + reverse_bit_order[i] + = (((i & 1) << 7) | ((i & 2) << 5) | ((i & 4) << 3) | ((i & 8) << 1) + | ((i & 16) >> 1) | ((i & 32) >> 3) + | ((i & 64) >> 5) | ((i & 128) >> 7)); +} + + +/* Dump the image in IMAGE into a file whose name is generated from + FILENAME and PAGE_INDEX (if it is not zero). */ + +void +dump_image (XImage *image, char *filename, char *filter, + int white_is_zero, int page_index, int quiet_mode) +{ + FILE *fp; + int pbm_bytes_per_line; + char *data = image->data; + int x, y; + + if (page_index) + { + char *name = alloca (strlen (filename) + 8); + + sprintf (name, "%s.%02d", filename, page_index); + filename = name; + } + + if (filter) + { + char *command = alloca (strlen (filename) + strlen (filter) + 1); + + sprintf (command, filter, filename); + fp = popen (command, "w"); + if (! fp) + FATAL_ERROR ("Can't run the command \"%s\"\n", command); + if (! quiet_mode) + printf ("Running \"%s\" ... ", command); + } + else + { + char *fullname = alloca (strlen (filename) + 5); + + sprintf (fullname, "%s.pbm", filename); + fp = fopen (fullname, "w"); + if (! fp) + FATAL_ERROR ("Can't write to \"%s\"\n", fullname); + if (! quiet_mode) + printf ("Writing %s ... ", fullname); + } + + if (image->bitmap_bit_order != MSBFirst + || (image->byte_order != MSBFirst + && image->bitmap_unit != 8)) + { + /* We must adjust image->data for PBM. */ + int bytes_per_unit = image->bitmap_unit / 8; + + for (y = 0; y < image->height; y++, data += image->bytes_per_line) + { + char b; + + if (image->byte_order != MSBFirst) + { + if (reverse_bit_order[0] == 0) + init_reverse_bit_order (); + if (bytes_per_unit == 2) + for (x = 0; x < image->bytes_per_line; x += 2) + b = data[x], data[x] = data[x + 1], data[x + 1] = b; + else if (bytes_per_unit == 6) + for (x = 0; x < image->bytes_per_line; x += 3) + { + b = data[x], data[x] = data[x + 3], data[x + 3] = b; + x++; + b = data[x], data[x] = data[x + 1], data[x + 1] = b; + } + } + + if (image->bitmap_bit_order != MSBFirst) + for (x = 0; x < image->bytes_per_line; x++) + data[x] = reverse_bit_order[(unsigned char) data[x]]; + if (! white_is_zero) + for (x = 0; x < image->bytes_per_line; x++) + data[x] = ~data[x]; + } + /* Reset DATA. */ + data = image->data; + } + + /* Generate PBM (Portable Bitmap File Format) of P4 format. */ + fprintf (fp, "P4\n%d %d\n", image->width, image->height); + pbm_bytes_per_line = (image->width + 7) / 8; + for (y = 0; y < image->height; y++, data += image->bytes_per_line) + fwrite (data, 1, pbm_bytes_per_line, fp); + + fclose (fp); + if (! quiet_mode) + printf (" done (%dx%d)\n", image->width, image->height); +} + +extern int line_break (MText *mt, int pos, int from, int to, int line, int y); + +int +main (int argc, char **argv) +{ + Display *display; + int screen; + GC gc; + Pixmap pixmap; + XImage *image; + int fontsize = 120; + int paper = PAPER_NOLIMIT; + int dpi = 300; + int margin = 20; + int xml = 0; + FILE *fp = stdin; + int cursor_pos = -1; + int quiet_mode = 0; + int break_by_word = 0; + char *filter = NULL; + int i; + int paper_width, paper_height; + int page_index; + + MFrame *frame; + MText *mt; + MDrawControl control; + MDrawMetric rect; + char *filename = "output"; + int len, from; + + /* Parse the command line arguments. */ + for (i = 1; i < argc; i++) + { + if (! strcmp (argv[i], "--help") + || ! strcmp (argv[i], "-h") + || ! strcmp (argv[i], "-?")) + help_exit (argv[0], 0); + else if (! strcmp (argv[i], "--version")) + { + printf ("mdump (m17n library) %s\n", VERSION); + printf ("Copyright (C) 2003, 2004 AIST, JAPAN\n"); + exit (0); + } + else if (! strcmp (argv[i], "-s") && i + 1< argc) + { + fontsize = atoi (argv[++i]); + if (! fontsize) + FATAL_ERROR ("Invalid font size: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-p") && i + 1< argc) + { + int w, h; + + i++; + if (! strcmp (argv[i], "a4")) + paper = PAPER_A4; + else if (! strcmp (argv[i], "a4r")) + paper = PAPER_A4R; + else if (! strcmp (argv[i], "a5")) + paper = PAPER_A5; + else if (! strcmp (argv[i], "a5r")) + paper = PAPER_A5R; + else if (! strcmp (argv[i], "b5")) + paper = PAPER_B5; + else if (! strcmp (argv[i], "b5r")) + paper = PAPER_B5R; + else if (! strcmp (argv[i], "letter")) + paper = PAPER_LETTER; + else if (sscanf (argv[i], "%dx%d", &w, &h) == 2 + && w > 0 && h > 0) + { + paper = PAPER_USER; + paper_size[paper].width = w; + paper_size[paper].height = h; + } + else + FATAL_ERROR ("Invalid paper type: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-d") && i + 1< argc) + { + dpi = atoi (argv[++i]); + if (! dpi) + FATAL_ERROR ("Invalid resolution: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-m") && i + 1< argc) + { + margin = atoi (argv[++i]); + if (margin < 0) + FATAL_ERROR ("Invalid margin: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-c") && i + 1< argc) + { + cursor_pos = atoi (argv[++i]); + if (cursor_pos < 0) + FATAL_ERROR ("Invalid cursor position: %s\n", argv[i]); + } + else if (! strcmp (argv[i], "-f") && i + 1< argc) + { + filter = argv[++i]; + } + else if (! strcmp (argv[i], "-x")) + { + xml = 1; + } + else if (! strcmp (argv[i], "-w")) + { + break_by_word = 1; + } + else if (! strcmp (argv[i], "-q")) + { + quiet_mode = 1; + } + else if (argv[i][0] != '-') + { + fp = fopen (argv[i], "r"); + if (! fp) + FATAL_ERROR ("Fail to open the file %s!\n", argv[i]); + filename = basename (argv[i]); + } + else + { + fprintf (stderr, "Unknown or invalid option: %s\n", argv[i]); + help_exit (argv[0], 1); + } + } + + /* Initialize the m17n library. */ + M17N_INIT (); + if (merror_code != MERROR_NONE) + FATAL_ERROR ("%s\n", "Fail to initialize the m17n library."); + + mt = mconv_decode_stream (Mcoding_utf_8, fp); + fclose (fp); + if (xml) + mt = mtext_deserialize (mt); + if (! mt) + FATAL_ERROR ("%s\n", "Fail to decode the input file or stream!"); + + len = mtext_len (mt); + + if (paper == PAPER_NOLIMIT) + paper_width = paper_height = margin = 0; + else + { + paper_width = paper_size[paper].width * dpi / 25.4; + paper_height = paper_size[paper].height * dpi / 25.4; + margin = margin * dpi / 25.4; + } + + display = XOpenDisplay (NULL); + screen = DefaultScreen (display); + + { + MPlist *plist = mplist (), *p; + MFontset *fontset = mfontset ("truetype"); + MFace *face = mface (); + + mface_put_prop (face, Mfontset, fontset); + mface_put_prop (face, Msize, (void *) (fontsize * dpi / 100)); + p = mplist_add (plist, msymbol ("display"), display); + p = mplist_add (p, msymbol ("depth"), (void *) 1); + p = mplist_add (p, Mface, face); + m17n_object_unref (face); + frame = mframe (plist); + m17n_object_unref (plist); + if (! frame) + FATAL_ERROR ("%s\n", "Can't open a frame (perhaps no font avairable)!"); + } + + memset (&control, 0, sizeof control); + control.as_image = 1; + control.two_dimensional = 1; + control.enable_bidi = 1; + if (cursor_pos >= 0) + { + control.with_cursor = 1; + if (cursor_pos > len) + cursor_pos = len; + control.cursor_pos = cursor_pos; + control.cursor_width = -1; + } + else + control.ignore_formatting_char = 1; + if (break_by_word) + control.line_break = line_break; + + if (paper == PAPER_NOLIMIT) + { + control.max_line_width = 0; + mdraw_text_extents (frame, mt, 0, len, &control, NULL, NULL, &rect); + paper_width = rect.width; + paper_height = rect.height; + } + else + control.max_line_width = paper_width - margin * 2; + + pixmap = XCreatePixmap (display, RootWindow (display, screen), + paper_width, paper_height, 1); + gc = XCreateGC (display, pixmap, 0L, NULL); + + from = 0; + page_index = 1; + while (from < len) + { + int to; + + if (paper == PAPER_NOLIMIT) + to = len; + else + to = find_page_end (frame, paper_height - margin * 2, mt, from, + &control, &rect); + + XSetForeground (display, gc, WhitePixel (display, screen)); + XFillRectangle (display, pixmap, gc, 0, 0, paper_width, paper_height); + mdraw_text_with_control (frame, (MDrawWindow) pixmap, + margin, margin - rect.y, mt, from, to, + &control); + XSetForeground (display, gc, BlackPixel (display, screen)); +#if 0 + XDrawRectangle (display, pixmap, gc, margin, margin, + paper_width - margin * 2 - 1, + paper_height - margin * 2 - 1); +#endif + image = XGetImage (display, pixmap, 0, 0, paper_width, paper_height, + AllPlanes, XYPixmap); + XInitImage (image); + dump_image (image, filename, filter, !WhitePixel (display, screen), + ((from > 0 || to < len) ? page_index : 0), + quiet_mode); + + from = to; + page_index++; + } + + m17n_object_unref (frame); + m17n_object_unref (mt); + M17N_FINI (); + XCloseDisplay (display); + exit (0); +} +#endif /* not FOR_DOXYGEN */ diff --git a/example/medit.c b/example/medit.c new file mode 100644 index 0000000..eb30560 --- /dev/null +++ b/example/medit.c @@ -0,0 +1,2700 @@ +/* medit.c -- simple multilingual editor. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page medit edit multilingual text + + @section medit-synopsis SYNOPSIS + + medit [ XT-OPTION ...] [ OPTION ... ] FILE + + @section medit-description DESCRIPTION + + Display FILE on a window and allow users to edit it. + + XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg). + + The following OPTIONs are available. + +
    + +
  • --version + + Print version number. + +
  • -h, --help + + Print this message. + +
+ + This program is to demonstrate how to use the m17n GUI API. + Although medit directly uses the GUI API, the API is mainly for + toolkit libraries or to implement XOM (X Outout Method), not for + direct use from application programs. +*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VERSION "1.0" + +/* Global variables. */ + +char *filename; +int serialized; + +/* For the X Window System. */ +Display *display; +int screen; +/* GCs for normal drawing, filling by background color, normal drawing + on bitmap (i.e. pixmap of depth 1), filling bitmap by background + color. */ +GC gc, gc_inv, mono_gc, mono_gc_inv; +Window win; +Atom XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING; /* X Selection types. */ +XtAppContext context; +int default_font_size; + +/* Widget hierarchy + +Shell - Form -+- Head -- File, Cursor, Bidi, LineBreak, InputMethod, CurIM; + +- Face -- Size, Family, Style, Color, Misc, Pop, CurFace + +- Lang -- A-B, C-D, ..., U-Z, Pop, CurLang + +- Body -- Sbar, Text + +- Tail -- Message +*/ + +Widget ShellWidget, HeadWidget, TailWidget, MessageWidget; +Widget CursorMenus[5], BidiMenus[3], LineBreakMenus[3], *InputMethodMenus; +Widget SbarWidget, TextWidget; +Widget FileShellWidget, FileDialogWidget; +Widget FaceWidget, CurFaceWidget, LangWidget, CurLangWidget; +Widget CurIMLang, CurIMStatus; + +int win_width, win_height; /* Size of TextWidget. */ +Arg arg[10]; + +Pixmap input_status_pixmap; +int input_status_width, input_status_height; + +/* Bitmap for "check" glyph. */ +#define check_width 9 +#define check_height 8 +static unsigned char check_bits[] = { + 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00, + 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00 }; +Pixmap CheckPixmap; + +/* For the m17n library. */ +MFrame *frame; +MText *mt; +int nchars; /* == mtext_len (mt) */ +MDrawControl control; +MTextProperty *selection; + +MFace *face_default; +MFace *face_xxx_large; +MFace *face_box; +MFace *face_courier, *face_helvetica, *face_times; +MFace *face_dv_ttyogesh, *face_freesans, *face_freemono; +MFace *face_default_fontset, *face_no_ctl_fontset; +MFace *face_input_status; + +int logical_move = 1; /* If 0, move cursor visually. */ + +MInputMethod **input_method_table; +int num_input_methods; +int current_input_method = -1; /* i.e. none */ +int auto_input_method = 0; +MInputContext *current_input_context; + +struct FaceRec +{ + char *name; + MFace **face; +} face_table[] = + { {"Menu Size", NULL}, + {"xx-small", &mface_xx_small}, + {"x-small", &mface_x_small}, + {"small", &mface_small}, + {"normalsize", &mface_normalsize}, + {"large", &mface_large}, + {"x-large", &mface_x_large}, + {"xx-large", &mface_xx_large}, + {"xxx-large", &face_xxx_large}, + + {"Menu Family", NULL}, + {"courier", &face_courier}, + {"helvetica", &face_helvetica}, + {"times", &face_times}, + {"dv-ttyogesh", &face_dv_ttyogesh}, + {"freesans", &face_freesans}, + {"freemono", &face_freemono}, + + {"Menu Style", NULL}, + {"medium", &mface_medium}, + {"bold", &mface_bold}, + {"italic", &mface_italic}, + + {"Menu Color", NULL}, + {"black", &mface_black}, + {"white", &mface_white}, + {"red", &mface_red}, + {"green", &mface_green}, + {"blue", &mface_blue}, + {"cyan", &mface_cyan}, + {"yello", &mface_yellow}, + {"magenta", &mface_magenta}, + + {"Menu Misc", NULL}, + {"normal", &mface_normal_video}, + {"reverse", &mface_reverse_video}, + {"underline", &mface_underline}, + {"box", &face_box}, + {"No CTL", &face_no_ctl_fontset} }; + + +int num_faces = sizeof (face_table) / sizeof (struct FaceRec); + +/* Information about a physical line metric. */ +struct LineInfo +{ + int from; /* BOL position of the line. */ + int to; /* BOL position of the next line. */ + int y0, y1; /* Top and bottom Y position of the line. */ + int ascent; /* Height of the top Y position. */ +}; + +struct LineInfo top; /* Topmost line. */ +struct LineInfo cur; /* Line containing cursor. */ +struct LineInfo sel_start; /* Line containing selection start. */ +struct LineInfo sel_end; /* Line containing selection end. */ + +MDrawGlyphInfo cursor; /* Information about the cursor glyph. */ + +/* X position to keep on vertical (up and down) cursor motion. */ +int target_x_position; + +/* Interface macros for m17n-lib drawing routines. */ + +/* Draw a text in the range $FROM to $TO of the M-text #MT at the + coordinate ($X, $Y) */ +#define DRAW_TEXT(x, y, from, to) \ + mdraw_text_with_control \ + (frame, (MDrawWindow) win, \ + control.orientation_reversed ? x + win_width : x, y, \ + mt, from, to, &control) + +/* Store the extents of a text in the range $FROM to $TO in the + structure $RECT (type MDrawMetric). */ +#define TEXT_EXTENTS(from, to, rect) \ + mdraw_text_extents (frame, mt, from, (to), &control, NULL, NULL, &(rect)) + +/* Store the glyph information of a character at the position $POS in + the struct $INFO (type MDrawGlyphInfo) assuming that the text from + $FROM is written at the coordinate (0, 0). */ +#define GLYPH_INFO(from, pos, info) \ + mdraw_glyph_info (frame, mt, from, (pos), &control, &(info)) + +/* Set $X and $Y to the coordinate of character at position $POS + assuming that the text from $FROM is written at the coordinate (0, + 0). */ +#define COORDINATES_POSITION(from, pos, x, y) \ + mdraw_coordinates_position (frame, mt, (from), (pos), (x), (y), &control) + +/* Interface macros for X library. */ +#define COPY_AREA(y0, y1, to) \ + XCopyArea (display, win, win, gc, 0, (y0), win_width, (y1) - (y0), 0, (to)) + +#define CLEAR_AREA(x, y, w, h) \ + XClearArea (display, win, (x), (y), (w), (h), False) + +#define SELECTEDP() \ + mtext_property_mtext (selection) + +/* Format MSG by FMT and print the result to the stderr, and exit. */ +#define FATAL_ERROR(fmt, arg) \ + do { \ + fprintf (stderr, fmt, arg); \ + exit (1); \ + } while (0) + + +/* If POS is greater than zero, move POS back to the beginning of line + (BOL) position. If FORWARD is nonzero, move POS forward instead. + Return the new position. */ +int +bol (int pos, int forward) +{ + int limit = forward ? nchars : 0; + + pos = mtext_character (mt, pos, limit, '\n'); + return (pos < 0 ? limit : pos + 1); +} + +/* Update the structure #TOP (struct LineInfo) to make $POS the first + character position of the screen. */ +void +update_top (int pos) +{ + int from = bol (pos, 0); + MDrawGlyphInfo info; + + GLYPH_INFO (from, pos, info); + top.from = info.line_from; + top.to = info.line_to; + top.y0 = 0; + top.y1 = info.this.height; + top.ascent = - info.this.y; +} + + +/* Update the scroll bar so that the text of the range $FROM to $TO + are shown on the window. */ +void +update_scroll_bar (int from, int to) +{ + float top = (float) from / nchars; + float shown = (float) (to - from) / nchars; + XtArgVal *l_top = (XtArgVal *) ⊤ + XtArgVal *l_shown = (XtArgVal *) &shown; + + XtSetArg (arg[0], XtNtopOfThumb, *l_top); + XtSetArg (arg[1], XtNshown, *l_shown); + XtSetValues (SbarWidget, arg, 2); +} + + +/* Redraw the window area between $Y0 and $Y1 (both Y-codinates). If + $CLEAR is nonzero, clear the area before drawing. If $SCROLL_BAR + is nonzero, update the scoll bar. */ +void +redraw (int y0, int y1, int clear, int scroll_bar) +{ + int from, to; + int y; + MDrawGlyphInfo info; + int sel_y0 = SELECTEDP () ? sel_start.y0 : 0; + struct LineInfo *line; + + if (clear) + CLEAR_AREA (0, y0, win_width, y1 - y0); + + /* Find a line closest to y0. The lihe is a cursor line if the + cursor is at the position above Y0, otherwise the top line. */ + if (y0 >= cur.y0) + line = &cur; + else + line = ⊤ + /* If there exists a selected region, check it too. */ + if (sel_y0 > line->y0 && y0 >= sel_y0) + line = &sel_start; + + from = line->from; + y = line->y0; + info.this.height = line->y1 - y; + info.this.y = - line->ascent; + info.line_to = line->to; + while (from < nchars && y + info.this.height <= y0) + { + y += info.this.height; + from = info.line_to; + GLYPH_INFO (from, from, info); + } + y0 = y - info.this.y; + to = from; + while (to < nchars && y < y1) + { + GLYPH_INFO (to, to, info); + y += info.this.height; + to = info.line_to; + } + if (to == nchars) + to++; + if (from < to) + DRAW_TEXT (0, y0, from, to); + if (scroll_bar) + { + while (to < nchars) + { + GLYPH_INFO (to, to, info); + if (y + info.this.height >= win_height) + break; + to = info.line_to; + y += info.this.height; + } + update_scroll_bar (top.from, to); + } +} + + +/* Set the current input method spot to the correct position. */ +void +set_input_method_spot () +{ + int x = cursor.x + (control.orientation_reversed ? win_width : 0); + int pos = cursor.from > 0 ? cursor.from - 1 : 0; + MFace *faces[256]; + int n = mtext_get_prop_values (mt, pos, Mface, (void **) faces, 256); + int size = 0, ratio = 0, i; + + for (i = n - 1; i >= 0; i--) + { + if (! size) + size = (int) mface_get_prop (faces[i], Msize); + if (! ratio) + ratio = (int) mface_get_prop (faces[i], Mratio); + } + if (! size) + size = default_font_size; + if (ratio) + size = size * ratio / 100; + minput_set_spot (current_input_context, x, cur.y0 + cur.ascent, + cur.ascent, cur.y1 - (cur.y0 + cur.ascent), size, + mt, cursor.from); +} + + +/* Redraw the cursor. If $CLEAR is nonzero, clear the cursor area + before drawing. */ +void +redraw_cursor (int clear) +{ + if (control.cursor_bidi) + { + /* We must update the whole line of the cursor. */ + int beg = bol (cur.from, 0); + int end = bol (cur.to - 1, 1); + MDrawMetric rect; + int y0 = cur.y0, y1 = cur.y1; + + if (beg != cur.from) + { + TEXT_EXTENTS (beg, cur.from, rect); + y0 -= rect.height; + } + if (end != cur.to) + { + TEXT_EXTENTS (cur.to, end, rect); + y1 += rect.height; + } + redraw (y0, y1, clear, 0); + } + else + { + if (clear) + { + int x = cursor.x; + + if (control.orientation_reversed) + x += win_width - cursor.this.width; + CLEAR_AREA (x, cur.y0, cursor.this.width, cursor.this.height); + } + DRAW_TEXT (cursor.x, cur.y0 + cur.ascent, cursor.from, cursor.to); + } +} + + +/* Update the information about the location of cursor to the position + $POS. If $FULL is nonzero, update the information fully only from + the information about the top line. Otherwise, truct the current + information in the structure $CUR. */ +void +update_cursor (int pos, int full) +{ + MDrawMetric rect; + + if (full) + { + /* CUR is inaccurate. We can trust only TOP. */ + GLYPH_INFO (top.from, pos, cursor); + cur.y0 = top.ascent + cursor.y + cursor.this.y; + } + else if (pos < cur.from) + { + int from = bol (pos, 0); + + TEXT_EXTENTS (from, cur.from, rect); + GLYPH_INFO (from, pos, cursor); + cur.y0 -= (rect.height + rect.y) - (cursor.y + cursor.this.y); + } + else if (pos < cur.to) + { + GLYPH_INFO (cur.from, pos, cursor); + } + else + { + GLYPH_INFO (cur.from, pos, cursor); + cur.y0 += cur.ascent + cursor.y + cursor.this.y; + } + + cur.from = cursor.line_from; + cur.to = cursor.line_to; + cur.y1 = cur.y0 + cursor.this.height; + cur.ascent = - cursor.this.y; +} + + +/* Update the information about the selected region. */ +void +update_selection () +{ + int from, to; + MDrawMetric rect; + MDrawGlyphInfo info; + + if (! SELECTEDP ()) + return; + from = mtext_property_start (selection); + to = mtext_property_end (selection); + + if (from < top.from) + { + GLYPH_INFO (bol (from, 0), from, info); + sel_start.ascent = -info.this.y; + sel_start.from = info.line_from; + sel_start.to = info.line_to; + TEXT_EXTENTS (from, top.from, rect); + sel_start.y0 = - rect.height; + sel_start.y1 = sel_start.y0 + info.this.height; + } + else + { + GLYPH_INFO (top.from, from, info); + sel_start.y0 = top.ascent + info.y + info.this.y; + sel_start.y1 = sel_start.y0 + info.this.height; + sel_start.ascent = -info.this.y; + sel_start.from = info.line_from; + sel_start.to = info.line_to; + } + + if (to <= sel_start.to) + { + sel_end = sel_start; + to = bol (to - 1, 1) - 1; + if (to >= sel_end.to) + { + GLYPH_INFO (sel_start.from, to, info); + sel_end.y1 = sel_end.y0 + info.y + info.this.height; + sel_end.to = info.line_to; + } + } + else + { + to = bol (to - 1, 1) - 1; + GLYPH_INFO (sel_start.from, to, info); + sel_end.y0 = sel_start.y0 + sel_start.ascent + info.y + info.this.y; + sel_end.y1 = sel_end.y0 + info.this.height; + sel_end.ascent = - info.this.y; + sel_end.from = info.line_from; + sel_end.to = info.line_to; + } +} + + +/* Select the text in the region from $FROM to $TO. */ +void +select_region (int from, int to) +{ + int pos; + + if (from > to) + pos = from, from = to, to = pos; + mtext_push_property (mt, from, to, selection); + update_selection (); +} + + +/* Setup the window to display the character of $POS at the top left + of the window. */ +void +reseat (int pos) +{ + MDrawMetric rect; + /* Top and bottom Y positions to redraw. */ + int y0, y1; + + if (pos + 1000 < top.from) + y0 = 0, y1 = win_height; + else if (pos < top.from) + { + y0 = 0; + TEXT_EXTENTS (pos, top.from, rect); + if (rect.height >= win_height * 0.9) + y1 = win_height; + else + { + y1 = rect.height; + COPY_AREA (0, win_height - y1, y1); + } + } + else if (pos < top.to) + { + /* No need of redrawing. */ + y0 = y1 = 0; + } + else if (pos < top.from + 1000) + { + TEXT_EXTENTS (top.from, pos, rect); + if (rect.height >= win_height * 0.9) + y0 = 0; + else + { + y0 = win_height - rect.height; + COPY_AREA (rect.height, win_height, 0); + } + y1 = win_height; + } + else + y0 = 0, y1 = win_height; + + if (y0 < y1) + { + update_top (pos); + if (cur.to <= pos) + update_cursor (pos, 1); + else + update_cursor (cursor.from, 1); + update_selection (); + redraw (y0, y1, 1, 1); + } +} + +static void MenuHelpProc (Widget, XEvent *, String *, Cardinal *); + + +/* Select an input method accoding to $IDX. If $IDX is negative, turn + off the current input method, otherwide turn on the input method + input_method_table[$IDX]. */ +void +select_input_method (idx) +{ + if (idx == current_input_method) + return; + if (current_input_context) + { + minput_destroy_ic (current_input_context); + current_input_context = NULL; + current_input_method = -1; + } + if (idx >= 0) + { + MInputMethod *im = input_method_table[idx]; + + if (im->language == Mnil) + { + MInputXIMArgIC arg_xic; + Window win = XtWindow (TextWidget); + + arg_xic.input_style = 0; + arg_xic.client_win = arg_xic.focus_win = win; + arg_xic.preedit_attrs = arg_xic.status_attrs = NULL; + current_input_context = minput_create_ic (im, &arg_xic); + } + else + { + MInputGUIArgIC arg_ic; + + arg_ic.frame = frame; + arg_ic.client = (MDrawWindow) XtWindow (ShellWidget); + arg_ic.focus = (MDrawWindow) XtWindow (TextWidget); + current_input_context = minput_create_ic (im, &arg_ic); + } + + if (current_input_context) + { + set_input_method_spot (); + current_input_method = idx; + } + } + if (current_input_method >= 0) + { + char *label; + XtSetArg (arg[0], XtNlabel, &label); + XtGetValues (InputMethodMenus[current_input_method + 2], arg, 1); + XtSetArg (arg[0], XtNlabel, label); + } + else + XtSetArg (arg[0], XtNlabel, ""); + XtSetValues (CurIMLang, arg, 1); +} + +static void MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num); + + +/* Display cursor according to the current information of #CUR. + $CLIENT_DATA is ignore. Most callback functions add this function + as a background processing procedure the current application (by + XtAppAddWorkProc) via the function hide_cursor. */ +Boolean +show_cursor (XtPointer client_data) +{ + if (cur.y0 < 0) + { + reseat (cur.from); + update_cursor (cursor.from, 1); + } + while (cur.y1 > win_height) + { + reseat (top.to); + update_cursor (cursor.from, 1); + } + + control.cursor_pos = cursor.from; + if (! SELECTEDP ()) + { + control.with_cursor = 1; + redraw_cursor (0); + } + if (current_input_context) + set_input_method_spot (); + + { + int pos = (SELECTEDP () ? mtext_property_start (selection) + : cursor.from > 0 ? cursor.from - 1 + : cursor.from); + MFace *face = mface (); + MTextProperty *props[256]; + int n = mtext_get_properties (mt, pos, Mface, props, 256); + int i; + char buf[256], *p = buf; + MSymbol sym; + + buf[0] = '\0'; + if (cursor.font) + { + int size = (int) mfont_get_prop (cursor.font, Msize); + MSymbol family = mfont_get_prop (cursor.font, Mfamily); + MSymbol weight = mfont_get_prop (cursor.font, Mweight); + MSymbol style = mfont_get_prop (cursor.font, Mstyle); + MSymbol registry = mfont_get_prop (cursor.font, Mregistry); + + sprintf (p, "%dpt", size / 10), p += strlen (p); + if (family) + strcat (p, ","), strcat (p, msymbol_name (family)), p += strlen (p); + if (weight) + strcat (p, ","), strcat (p, msymbol_name (weight)), p += strlen (p); + if (style) + strcat (p, ","), strcat (p, msymbol_name (style)), p += strlen (p); + if (registry) + strcat (p, ","), strcat (p, msymbol_name (registry)), p += strlen (p); + p += strlen (p); + } + + mface_merge (face, face_default); + for (i = 0; i < n; i++) + if (props[i] != selection) + mface_merge (face, (MFace *) mtext_property_value (props[i])); + sym = (MSymbol) mface_get_prop (face, Mforeground); + if (sym != Mnil) + strcat (p, ","), strcat (p, msymbol_name (sym)), p += strlen (p); + if ((MSymbol) mface_get_prop (face, Mvideomode) == Mreverse) + strcat (p, ",rev"), p += strlen (p); + if (mface_get_prop (face, Mhline)) + strcat (p, ",ul"), p += strlen (p); + if (mface_get_prop (face, Mbox)) + strcat (p, ",box"), p += strlen (p); + m17n_object_unref (face); + + XtSetArg (arg[0], XtNborderWidth, 1); + XtSetArg (arg[1], XtNlabel, buf); + XtSetValues (CurFaceWidget, arg, 2); + } + + if (control.cursor_pos < nchars) + { + MSymbol sym = Mnil; + + if (control.cursor_pos > 0 + && mtext_ref_char (mt, control.cursor_pos - 1) != '\n') + sym = mtext_get_prop (mt, control.cursor_pos - 1, Mlanguage); + if (sym == Mnil) + sym = mtext_get_prop (mt, control.cursor_pos, Mlanguage); + + if (sym == Mnil) + { + XtSetArg (arg[0], XtNborderWidth, 0); + XtSetArg (arg[1], XtNlabel, ""); + } + else + { + XtSetArg (arg[0], XtNborderWidth, 1); + XtSetArg (arg[1], XtNlabel, + msymbol_name (msymbol_get (sym, Mlanguage))); + XtSetValues (CurLangWidget, arg, 2); + } + XtSetValues (CurLangWidget, arg, 2); + + if (auto_input_method) + { + if (sym == Mnil) + select_input_method (-1); + else + { + int i; + + for (i = 0; i < num_input_methods; i++) + if (input_method_table[i]->language == sym) + break; + if (i < num_input_methods) + select_input_method (i); + else + select_input_method (-1); + } + } + } + + MenuHelpProc (MessageWidget, NULL, NULL, NULL); + + return True; +} + + +/* Hide the cursor. */ +void +hide_cursor () +{ + control.with_cursor = 0; + redraw_cursor (1); + XtAppAddWorkProc (context, show_cursor, NULL); +} + + +/* Update the window area between the Y-positions $Y0 and $OLD_Y1 to + $Y1 and $NEW_Y1 assuming that the text in the other area is not + changed. */ +void +update_region (int y0, int old_y1, int new_y1) +{ + if (y0 < 0) + y0 = 0; + if (new_y1 < old_y1) + { + if (old_y1 < win_height) + { + COPY_AREA (old_y1, win_height, new_y1); + redraw (win_height - (old_y1 - new_y1), win_height, 1, 0); + } + else + redraw (new_y1, win_height, 1, 0); + } + else if (new_y1 > old_y1) + { + if (new_y1 < win_height) + COPY_AREA (old_y1, win_height, new_y1); + } + if (new_y1 > win_height) + new_y1 = win_height; + redraw (y0, new_y1, 1, 1); +} + + +/* Delete the next $N characters. If $N is negative delete the + precious (- $N) characters. */ +void +delete_char (int n) +{ + MDrawMetric rect; + MDrawGlyphInfo info; + int old_y1, new_y1; + int from, to; + + if (n > 0) + from = cursor.from, to = from + n; + else + { + if (cursor.from == cur.from) + { + /* We are at the beginning of line. */ + int pos = cursor.prev_from; + + if (cursor.from == top.from) + { + /* We are at the beginning of screen. We must scroll + down. */ + GLYPH_INFO (bol (top.from - 1, 0), top.from - 1, info); + reseat (info.line_from); + } + update_cursor (pos, 1); + from = cursor.from; + to = cursor.to; + } + else + { + from = cursor.from - 1; + to = cursor.from; + } + } + + TEXT_EXTENTS (cur.from, bol (to + 1, 1), rect); + old_y1 = cur.y0 + rect.height; + + /* Now delete a character. */ + mtext_del (mt, from, to); + nchars--; + if (from >= top.from && from < top.to) + update_top (top.from); + update_cursor (from, 1); + + TEXT_EXTENTS (cur.from, bol (to, 1), rect); + new_y1 = cur.y0 + rect.height; + + update_region (cur.y0, old_y1, new_y1); +} + + +/* Insert M-text $NEWTEXT at the current cursor position. */ +void +insert_chars (MText *newtext) +{ + int n = mtext_len (newtext); + MDrawMetric rect; + int y0, old_y1, new_y1; + + if (SELECTEDP ()) + { + int n = (mtext_property_end (selection) + - mtext_property_start (selection)); + mtext_detach_property (selection); + delete_char (n); + } + + y0 = cur.y0; + TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect); + old_y1 = y0 + rect.height; + + /* Now insert chars. */ + mtext_ins (mt, cursor.from, newtext); + nchars += n; + if (cur.from == top.from) + update_top (top.from); + update_cursor (cursor.from + n, 1); + + TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect); + new_y1 = cur.y0 + rect.height; + + update_region (y0, old_y1, new_y1); + update_selection (); +} + + +/* Convert the currently selected text to COMPOUND-TEXT. It is called + when someone requests the current value of the selection. */ +Boolean +covert_selection (Widget w, Atom *selection_atom, + Atom *target, Atom *return_type, + XtPointer *value, unsigned long *length, int *format) +{ + unsigned char *buf = (unsigned char *) XtMalloc (4096); + MText *this_mt = mtext (); + int from = mtext_property_start (selection); + int to = mtext_property_end (selection); + + mtext_copy (this_mt, 0, mt, from, to); + *length = mconv_encode_buffer (msymbol ("compound-text"), + this_mt, buf, 4096); + *return_type = XA_COMPOUND_TEXT; + *value = (XtPointer) buf; + *format = 8; + m17n_object_unref (this_mt); + return True; +} + + +/* Unselect the text. It is called when we loose the selection. */ +void +lose_selection (Widget w, Atom *selection_atom) +{ + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0); + } +} + +void +get_selection (Widget w, XtPointer cliend_data, Atom *selection, Atom *type, + XtPointer value, unsigned long *length, int *format) +{ + MText *this_mt; + MSymbol coding; + + if (*type == XT_CONVERT_FAIL || ! value) + goto err; + if (*type == XA_STRING) + coding = Mnil; + else if (*type == XA_COMPOUND_TEXT) + coding = msymbol ("compound-text"); + else if (*type == XA_UTF8_STRING) + coding = msymbol ("utf-8"); + else + goto err; + + this_mt = mconv_decode_buffer (coding, (unsigned char *) value, *length); + if (this_mt) + { + hide_cursor (); + insert_chars (this_mt); + m17n_object_unref (this_mt); + } + + err: + if (value) + XtFree (value); +} + +static void +ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + XExposeEvent *expose = (XExposeEvent *) event; + + if (top.from < 0) + { + Dimension width_max, width; + + XtSetArg (arg[0], XtNwidth, &width); + XtGetValues (XtParent (w), arg, 1); + width_max = width; + XtGetValues (HeadWidget, arg, 1); + if (width_max < width) + width_max = width; + XtGetValues (FaceWidget, arg, 1); + if (width_max < width) + width_max = width; + XtGetValues (LangWidget, arg, 1); + if (width_max < width) + width_max = width; + XtSetArg (arg[0], XtNwidth, width_max); + XtSetValues (HeadWidget, arg, 1); + XtSetValues (FaceWidget, arg, 1); + XtSetValues (LangWidget, arg, 1); + XtSetValues (XtParent (w), arg, 1); + XtSetValues (TailWidget, arg, 1); + + update_top (0); + update_cursor (0, 1); + redraw (0, win_height, 0, 1); + show_cursor (NULL); + } + else + { + redraw (expose->y, expose->y + expose->height, 0, 0); + if (current_input_context + && expose->y < cur.y0 && expose->y + expose->height < cur.y1) + set_input_method_spot (); + } +} + +static void +ConfigureProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + XConfigureEvent *configure = (XConfigureEvent *) event; + + hide_cursor (); + control.max_line_width = win_width = configure->width; + win_height = configure->height; + mdraw_clear_cache (mt); + update_top (0); + update_cursor (0, 1); + redraw (0, win_height, 1, 1); + if (current_input_context) + set_input_method_spot (); +} + +static void +ButtonProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + int pos; + int x = event->xbutton.x; + int y = event->xbutton.y - top.ascent; + + if (control.orientation_reversed) + x -= win_width; + pos = COORDINATES_POSITION (top.from, nchars + 1, x, y); + if (SELECTEDP ()) + { + XtDisownSelection (w, XA_PRIMARY, CurrentTime); + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0); + } + hide_cursor (); + update_cursor (pos, 0); +} + + +static void +ButtonReleaseProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + if (! SELECTEDP ()) + return; + + XtOwnSelection (w, XA_PRIMARY, CurrentTime, + covert_selection, lose_selection, NULL); + update_cursor (mtext_property_start (selection), 0); +} + +static +void +Button2Proc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + if (! SELECTEDP ()) + { + /* We don't have a local selection. */ + XtGetSelectionValue (w, XA_PRIMARY, XA_TEXT, get_selection, NULL, + CurrentTime); + } + else + { + int from = mtext_property_start (selection); + int to = mtext_property_end (selection); + MText *this_mt; + int pos; + int x = event->xbutton.x; + int y = event->xbutton.y - top.ascent; + + if (control.orientation_reversed) + x -= win_width; + pos = COORDINATES_POSITION (top.from, nchars + 1, x, y); + + XtDisownSelection (w, XA_PRIMARY, CurrentTime); + mtext_detach_property (selection); + hide_cursor (); + this_mt = mtext_copy (mtext (), 0, mt, from, to); + update_cursor (pos, 0); + insert_chars (this_mt); + m17n_object_unref (this_mt); + } +} + +static void +ButtonMoveProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + int pos; + int x = event->xbutton.x; + int y = event->xbutton.y; + + if (control.orientation_reversed) + x -= win_width; + if (y < cur.y0) + pos = top.from, y -= top.ascent; + else + pos = cur.from, y -= cur.y0 + cur.ascent; + pos = COORDINATES_POSITION (pos, nchars + 1, x, y); + + if (pos == cursor.from) + return; + + hide_cursor (); + if (SELECTEDP ()) + { + /* Selection range changed. */ + int from = mtext_property_start (selection); + int to = mtext_property_end (selection); + int start_y0 = sel_start.y0, start_y1 = sel_start.y1; + int end_y0 = sel_end.y0, end_y1 = sel_end.y1; + + if (cursor.from == from) + { + /* Start position of selection changed. */ + select_region (pos, to); + if (pos > from) + /* Shrunken. Previous selection face must be cleared. */ + redraw (start_y0, sel_start.y1, 1, 0); + else + /* Enlarged. We can simply overdraw. */ + redraw (sel_start.y0, start_y1, 0, 0); + } + else + { + /* End position of selection changed. */ + select_region (from, pos); + if (pos < to) + /* Shrunken. Previous selection face must be cleared. */ + redraw (sel_end.y0, end_y1, 1, 0); + else + /* Enlarged. We can simply overdraw. */ + redraw (end_y0, sel_end.y1, 0, 0); + } + } + else + { + /* Newly selected. */ + select_region (pos, cursor.from); + redraw (sel_start.y0, sel_end.y1, 0, 0); + } + update_cursor (pos, 1); +} + +void +ScrollProc (Widget w, XtPointer client_data, XtPointer position) +{ + int from; + MDrawGlyphInfo info; + int height; + int cursor_pos = cursor.from; + + if (((int) position) < 0) + { + /* Scroll down. */ + int pos; + + from = top.from; + height = top.y1 - top.y0; + while (from > 0) + { + pos = bol (from - 1, 0); + GLYPH_INFO (pos, from - 1, info); + if (height + info.this.height > win_height) + break; + height += info.this.height; + from = info.line_from; + } + if (cursor_pos >= top.to) + { + cursor_pos = top.from; + pos = top.to; + while (cursor_pos < nchars) + { + GLYPH_INFO (pos, pos, info); + if (height + info.this.height > win_height) + break; + height += info.this.height; + cursor_pos = pos; + pos = info.line_to; + } + } + } + else if (cur.to < nchars) + { + /* Scroll up, but leave at least one line. */ + from = cur.to; + height = cur.y1; + while (from < nchars) + { + GLYPH_INFO (from, from, info); + if (height + info.this.height > win_height + || info.line_to >= nchars) + break; + height += info.this.height; + from = info.line_to; + } + if (from == nchars) + from = info.line_from; + if (cursor_pos < from) + cursor_pos = from; + } + else + /* Scroll up to make the cursor line top. */ + from = cur.from; + hide_cursor (); + reseat (from); + update_cursor (cursor_pos, 1); +} + +void +JumpProc (Widget w, XtPointer client_data, XtPointer persent_ptr) +{ + float persent = *(float *) persent_ptr; + int pos1, pos2 = nchars * persent; + MDrawGlyphInfo info; + + hide_cursor (); + pos1 = bol (pos2, 0); + GLYPH_INFO (pos1, pos2, info); + pos1 = info.line_from; + reseat (pos1); + update_cursor (pos1, 1); +} + + +static void +KeyProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + XKeyEvent *key_event = (XKeyEvent *) event; + char buf[512]; + KeySym keysym = NoSymbol; + int ret; + /* If set to 1, do not update target_x_position. */ + int keep_target_x_position = 0; + MText *produced; + + if (current_input_context + && minput_filter (current_input_context, Mnil, event)) + return; + if (event->type == KeyRelease) + return; + + hide_cursor (); + + produced = mtext (); + ret = minput_lookup (current_input_context, Mnil, event, produced); + if (mtext_len (produced) > 0) + insert_chars (produced); + if (ret) + ret = XLookupString (key_event, buf, sizeof (buf), &keysym, NULL); + m17n_object_unref (produced); + + switch (keysym) + { + case XK_Delete: + { + int n = 0; + + if (SELECTEDP ()) + { + n = (mtext_property_end (selection) + - mtext_property_start (selection)); + mtext_detach_property (selection); + } + else if (cursor.from < nchars) + { + /* Delete the following grapheme cluster. */ + n = cursor.to - cursor.from; + } + if (n != 0) + delete_char (n); + } + break; + + case XK_BackSpace: + { + int n = 0; + + if (SELECTEDP ()) + { + /* Delete selected region. */ + n = (mtext_property_end (selection) + - mtext_property_start (selection)); + mtext_detach_property (selection); + } + else if (cursor.from > 0) + { + /* Delete the preceding character. */ + n = -1; + } + if (n != 0) + delete_char (n); + } + break; + + case XK_Left: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (logical_move) + { + if (cursor.prev_from >= 0) + update_cursor (cursor.prev_from, 0); + } + else + { + if (cursor.left_from >= 0) + update_cursor (cursor.left_from, 0); + } + break; + + case XK_Right: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (logical_move) + { + if (cursor.next_to >= 0) + update_cursor (cursor.to, 0); + } + else + { + if (cursor.right_from >= 0) + update_cursor (cursor.right_from, 0); + } + break; + + case XK_Down: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (cur.to <= nchars) + { + MDrawGlyphInfo info; + int pos; + + GLYPH_INFO (cur.from, cur.to, info); + pos = COORDINATES_POSITION (cur.from, nchars + 1, + target_x_position, info.y); + keep_target_x_position = 1; + update_cursor (pos, 0); + } + break; + + case XK_Up: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (cur.from > 0) + { + MDrawMetric rect; + int y; + int pos = bol (cur.from - 1, 0); + + TEXT_EXTENTS (pos, cur.from - 1, rect); + y = rect.height + rect.y - 1; + pos = COORDINATES_POSITION (pos, nchars, + target_x_position, y); + keep_target_x_position = 1; + update_cursor (pos, 0); + } + break; + + case XK_Page_Down: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (top.from < nchars) + ScrollProc (w, NULL, (XtPointer) 1); + break; + + case XK_Page_Up: + if (SELECTEDP ()) + { + mtext_detach_property (selection); + redraw (sel_start.y0, sel_end.y1, 1, 0);; + } + if (top.from > 0) + ScrollProc (w, NULL, (XtPointer) -1); + break; + + default: + if (ret > 0) + { + if (buf[0] == 17) /* C-q */ + { + XtAppSetExitFlag (context); + return; + } + else if (buf[0] == 12) /* C-l */ + { + redraw (0, win_height, 1, 1); + return; + } + else + { + MText *temp = mtext (); + + mtext_cat_char (temp, buf[0] == '\r' ? '\n' : buf[0]); + if (current_input_context) + mtext_put_prop (temp, 0, 1, Mlanguage, + current_input_context->im->language); + insert_chars (temp); + m17n_object_unref (temp); + } + } + } + + if (! keep_target_x_position) + target_x_position = cursor.x; +} + +void +SaveProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + char *name = (char *) client_data; + FILE *fp; + int from = -1, to = 0; + + if (name) + { + free (filename); + filename = strdup (name); + } + + fp = fopen (filename, "w"); + if (! fp) + { + fprintf (stderr, "Open for write fail: %s", filename); + return; + } + + if (SELECTEDP ()) + { + from = mtext_property_start (selection); + to = mtext_property_end (selection); + mtext_detach_property (selection); + } + + mconv_encode_stream (Mcoding_utf_8, mt, fp); + fclose (fp); + if (from >= 0) + select_region (from, to); +} + +void +SerializeProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + MText *new; + + hide_cursor (); + if (SELECTEDP ()) + mtext_detach_property (selection); + serialized = (int) client_data; + if (! serialized) + new = mtext_deserialize (mt); + else + { + MPlist *plist = mplist (); + + mplist_push (plist, Mt, Mface); + mplist_push (plist, Mt, Mlanguage); + new = mtext_serialize (mt, 0, mtext_len (mt), plist); + m17n_object_unref (plist); + } + if (new) + { + m17n_object_unref (mt); + mt = new; + serialized = ! serialized; + nchars = mtext_len (mt); + update_top (0); + } + update_cursor (0, 1); + redraw (0, win_height, 1, 1); +} + +void +QuitProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + XtAppSetExitFlag (context); +} + +MText * +read_file () +{ + FILE *fp = fopen (filename, "r"); + + if (! fp) + FATAL_ERROR ("Can't read \"%s\"!\n", filename); + mt = mconv_decode_stream (Mcoding_utf_8, fp); + fclose (fp); + if (! mt) + FATAL_ERROR ("Can't decode \"%s\" by UTF-8!\n", filename); + return mt; +} + +void +BidiProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int data = (int) client_data; + int i; + + if (data == 0) + { + control.enable_bidi = 0; + control.orientation_reversed = 0; + } + else + { + control.enable_bidi = 1; + control.orientation_reversed = data == 2; + } + for (i = 0; i < 3; i++) + { + if (i == data) + XtSetArg (arg[0], XtNleftBitmap, CheckPixmap); + else + XtSetArg (arg[0], XtNleftBitmap, None); + XtSetValues (BidiMenus[i], arg, 1); + } + + update_cursor (cursor.from, 1); + redraw (0, win_height, 1, 0); +} + +extern int line_break (MText *mt, int pos, int from, int to, int line, int y); + +void +LineBreakProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int data = (int) client_data; + int i; + + if (data == 0) + control.max_line_width = 0; + else + { + control.max_line_width = win_width; + control.line_break = (data == 1 ? NULL : line_break); + } + for (i = 0; i < 3; i++) + { + if (i == data) + XtSetArg (arg[0], XtNleftBitmap, CheckPixmap); + else + XtSetArg (arg[0], XtNleftBitmap, None); + XtSetValues (LineBreakMenus[i], arg, 1); + } + + update_cursor (cursor.from, 1); + redraw (0, win_height, 1, 0); +} + +void +CursorProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int data = (int) client_data; + int i, from, to; + + switch (data) + { + case 0: + logical_move = 1; + from = 0, to = 2; + break; + case 1: + logical_move = 0; + from = 0, to = 2; + break; + case 2: + control.cursor_bidi = 0, control.cursor_width = -1; + from = 2, to = 5; + break; + case 3: + control.cursor_bidi = 0, control.cursor_width = 2; + from = 2, to = 5; + break; + default: + control.cursor_bidi = 1; + from = 2, to = 5; + break; + } + + for (i = from; i < to; i++) + { + if (i == data) + XtSetArg (arg[0], XtNleftBitmap, CheckPixmap); + else + XtSetArg (arg[0], XtNleftBitmap, None); + XtSetValues (CursorMenus[i], arg, 1); + } + + redraw (0, win_height, 1, 0); +} + +static void +InputMethodProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int idx = (int) client_data; + + if (idx == -2 ? current_input_method < 0 + : idx == -1 ? auto_input_method + : idx == current_input_method) + return; + + XtSetArg (arg[0], XtNleftBitmap, None); + if (auto_input_method) + { + XtSetValues (InputMethodMenus[1], arg, 1); + auto_input_method = 0; + } + else if (current_input_method < 0) + XtSetValues (InputMethodMenus[0], arg, 1); + else + XtSetValues (InputMethodMenus[current_input_method + 2], arg, 1); + + if (idx == -1) + { + auto_input_method = 1; + hide_cursor (); + } + else + select_input_method (idx); + XtSetArg (arg[0], XtNleftBitmap, CheckPixmap); + XtSetValues (InputMethodMenus[idx + 2], arg, 1); +} + +void +FaceProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int idx = (int) client_data; + int from, to; + int old_y1; + + if (! SELECTEDP ()) + return; + + XtAppAddWorkProc (context, show_cursor, NULL); + from = mtext_property_start (selection); + to = mtext_property_end (selection); + old_y1 = sel_end.y1; + + mtext_detach_property (selection); + if (idx >= 0) + { + MTextProperty *prop = mtext_property (Mface, *face_table[idx].face, + MTEXTPROP_REAR_STICKY); + mtext_push_property (mt, from, to, prop); + m17n_object_unref (prop); + } + else + mtext_pop_prop (mt, from, to, Mface); + if (from < top.to) + update_top (top.from); + update_cursor (cursor.from, 1); + select_region (from, to); + update_region (sel_start.y0, old_y1, sel_end.y1); + if (cur.y1 > win_height) + { + while (cur.y1 > win_height) + { + reseat (top.to); + update_cursor (cursor.from, 1); + } + } +} + +void +LangProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + MSymbol sym = (MSymbol) client_data; + int from, to; + int old_y1; + + if (! SELECTEDP ()) + return; + + XtAppAddWorkProc (context, show_cursor, NULL); + from = mtext_property_start (selection); + to = mtext_property_end (selection); + old_y1 = sel_end.y1; + + mtext_detach_property (selection); + if (sym != Mnil) + mtext_put_prop (mt, from, to, Mlanguage, sym); + else + mtext_pop_prop (mt, from, to, Mlanguage); + + if (from < top.to) + update_top (top.from); + update_cursor (cursor.from, 1); + select_region (from, to); + update_region (sel_start.y0, old_y1, sel_end.y1); + if (cur.y1 > win_height) + { + while (cur.y1 > win_height) + { + reseat (top.to); + update_cursor (cursor.from, 1); + } + } +} + +void +DumpImageProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + int narrowed = (int) client_data; + FILE *mdump; + int from, to; + MConverter *converter; + + if (narrowed) + { + if (! SELECTEDP ()) + return; + from = mtext_property_start (selection); + to = mtext_property_end (selection); + } + else + { + from = 0; + to = nchars; + } + + if (! narrowed) + mdump = popen ("mdump -q -p a4", "w"); + else + mdump = popen ("mdump -q", "w"); + if (! mdump) + return; + converter = mconv_stream_converter (Mcoding_utf_8, mdump); + mconv_encode_range (converter, mt, from, to); + mconv_free_converter (converter); + fclose (mdump); +} + +void +input_status (MInputContext *ic, MSymbol command) +{ + XFillRectangle (display, input_status_pixmap, gc_inv, + 0, 0, input_status_width, input_status_height); + if (command == Minput_status_draw) + { + MDrawMetric rect; + + mtext_put_prop (ic->status, 0, mtext_len (ic->status), + Mface, face_input_status); + if (ic->im->language != Mnil) + mtext_put_prop (ic->status, 0, mtext_len (ic->status), + Mlanguage, ic->im->language); + mdraw_text_extents (frame, ic->status, 0, mtext_len (ic->status), + NULL, NULL, NULL, &rect); + mdraw_text (frame, (MDrawWindow) input_status_pixmap, + input_status_width - rect.width - 2, - rect.y, + ic->status, 0, mtext_len (ic->status)); + } + XtSetArg (arg[0], XtNbitmap, input_status_pixmap); + XtSetValues (CurIMStatus, arg, 1); +} + +int +compare_input_method (const void *elt1, const void *elt2) +{ + const MInputMethod *im1 = *(MInputMethod **) elt1; + const MInputMethod *im2 = *(MInputMethod **) elt2; + MSymbol lang1, lang2; + + if (im1->language == Mnil) + return 1; + if (im1->language == im2->language) + return strcmp (msymbol_name (im1->name), msymbol_name (im2->name)); + if (im1->language == Mt) + return 1; + if (im2->language == Mt) + return -1; + lang1 = msymbol_get (im1->language, Mlanguage); + lang2 = msymbol_get (im2->language, Mlanguage); + return strcmp (msymbol_name (lang1), msymbol_name (lang2)); +} + +void +setup_input_methods (int with_xim) +{ + MInputMethod *im = NULL; + MInputXIMArgIM arg_xim; + MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil); + MPlist *pl; + int i = 0; + + num_input_methods = mplist_length (plist); + + if (with_xim) + { + arg_xim.display = display; + arg_xim.db = NULL; + arg_xim.res_name = arg_xim.res_class = NULL; + arg_xim.locale = NULL; + arg_xim.modifier_list = NULL; + im = minput_open_im (Mnil, msymbol ("xim"), &arg_xim); + if (im) + num_input_methods++; + } + input_method_table = calloc (num_input_methods, sizeof (MInputMethod *)); + if (im) + input_method_table[i++] = im; + for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl)) + { + MDatabase *mdb = mplist_value (pl); + MSymbol *tag = mdatabase_tag (mdb); + + if (tag[1] != Mnil) + { + im = minput_open_im (tag[1], tag[2], NULL); + if (im) + input_method_table[i++] = im; + } + } + + m17n_object_unref (plist); + num_input_methods = i; + qsort (input_method_table, num_input_methods, sizeof input_method_table[0], + compare_input_method); + current_input_context = NULL; + + mplist_put (minput_driver->callback_list, Minput_status_start, + (void *) input_status); + mplist_put (minput_driver->callback_list, Minput_status_draw, + (void *) input_status); + mplist_put (minput_driver->callback_list, Minput_status_done, + (void *) input_status); +} + + +static void +MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + char *msg; + + if (num && *num > 0) + { + int bytes = 0, i; + + for (i = 0; i < *num; i++) + bytes += strlen (str[i]) + 1; + msg = alloca (bytes); + strcpy (msg, str[0]); + for (i = 1; i < *num; i++) + strcat (msg, " "), strcat (msg, str[i]); + } + else if (cursor.from < nchars) + { + int c = mtext_ref_char (mt, cursor.from); + char *name = mchar_get_prop (c, Mname); + + if (! name) + name = ""; + msg = alloca (10 + strlen (name)); + sprintf (msg, "U+%04X %s", c, name); + } + else + { + msg = ""; + } + XtSetArg (arg[0], XtNlabel, msg); + XtSetValues (MessageWidget, arg, 1); +} + +typedef struct +{ + int type; + char *name1, *name2; + XtCallbackProc proc; + XtPointer client_data; + int status; + Widget w; +} MenuRec; + +void PopupProc (Widget w, XtPointer client_data, XtPointer call_data); + +void SaveProc (Widget w, XtPointer client_data, XtPointer call_data); + +MenuRec FileMenu[] = + { { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 }, + { 0, "Save", NULL, SaveProc, NULL, -1 }, + { 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 }, + { 1 }, + { 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 }, + { 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 }, + { 1 }, + { 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 }, + { 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 }, + { 1 }, + { 0, "Quit", NULL, QuitProc, NULL, -1 } }; + +void +PopupProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + MenuRec *rec = (MenuRec *) client_data; + Position x, y; + + XtSetArg (arg[0], XtNvalue, ""); + XtSetArg (arg[1], XtNlabel, rec->name1); + XtSetValues (FileDialogWidget, arg, 2); + XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y); + XtSetArg (arg[0], XtNx, x + 20); + XtSetArg (arg[1], XtNy, y + 10); + XtSetValues (FileShellWidget, arg, 2); + XtPopup (FileShellWidget, XtGrabExclusive); +} + +void +FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + FILE *fp; + char *label; + + XtPopdown (FileShellWidget); + if ((int) client_data == 1) + return; + XtSetArg (arg[0], XtNlabel, &label); + XtGetValues (FileDialogWidget, arg, 1); + if (strcmp (label, FileMenu[0].name1) == 0) + { + /* Open a file */ + free (filename); + filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget)); + fp = fopen (filename, "r"); + hide_cursor (); + m17n_object_unref (mt); + if (fp) + { + mt = mconv_decode_stream (Mcoding_utf_8, fp); + fclose (fp); + if (! mt) + mt = mtext (); + } + else + mt = mtext (); + serialized = 0; + nchars = mtext_len (mt); + update_top (0); + update_cursor (0, 1); + redraw (0, win_height, 1, 1); + } + else if (strcmp (label, FileMenu[2].name1) == 0) + SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL); + else + fprintf (stderr, "Invalid calling sequence: FileDialogProc\n"); +} + +#define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS) \ + ((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \ + (MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA), \ + (MENU).status = (STATUS)) + + +Widget +create_menu_button (Widget top, Widget parent, Widget left, char *button_name, + char *menu_name, MenuRec *menus, int num_menus, char *help) +{ + Widget button, menu; + char *fmt = ": highlight() MenuHelp(%s)\n\ + : reset() MenuHelp()\n\ + : reset() PopupMenu()\n\ + : highlight()"; + int i; + MenuRec *m; + char *trans; + int max_width = 0; + + menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0); + for (i = 0; i < num_menus; i++) + { + m = menus + i; + if (m->type == 0) + { + if (m->proc) + { + int n = 0; + + if (m->status >= 0) + { + XtSetArg (arg[n], XtNleftMargin, 20), n++; + if (m->status > 0) + XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++; + } + m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass, + menu, arg, n); + XtAddCallback (m->w, XtNcallback, m->proc, m->client_data); + } + else + { + XtSetArg (arg[0], XtNsensitive, False); + m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass, + menu, arg, 2); + } + } + else + { + XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0); + } + if (m->name2) + max_width = 1; + } + trans = alloca (strlen (fmt) + strlen (help)); + sprintf (trans, fmt, help); + XtSetArg (arg[0], XtNmenuName, menu_name); + XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans)); + XtSetArg (arg[2], XtNinternalWidth, 2); + XtSetArg (arg[3], XtNhighlightThickness, 1); + XtSetArg (arg[4], XtNleft, XawChainLeft); + XtSetArg (arg[5], XtNright, XawChainLeft); + i = 6; + if (left) + XtSetArg (arg[i], XtNfromHoriz, left), i++; + button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent, + arg, i); + + if (max_width) + { + int height, ascent, *width = alloca (sizeof (int) * num_menus); + int *len = alloca (sizeof (int) * num_menus); + + XFontSet font_set; + XFontSetExtents *fontset_extents; + + XtSetArg (arg[0], XtNfontSet, &font_set); + XtGetValues (button, arg, 1); + + fontset_extents = XExtentsOfFontSet (font_set); + height = fontset_extents->max_logical_extent.height; + ascent = - fontset_extents->max_logical_extent.y; + + for (i = 0; i < num_menus; i++) + if (menus[i].name2) + { + len[i] = strlen (menus[i].name2); + width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]); + if (max_width < width[i]) + max_width = width[i]; + } + for (i = 0; i < num_menus; i++) + if (menus[i].name2) + { + Pixmap pixmap = XCreatePixmap (display, + RootWindow (display, screen), + max_width, height, 1); + XFillRectangle (display, pixmap, mono_gc_inv, + 0, 0, max_width, height); + XmbDrawString (display, pixmap, font_set, mono_gc, + max_width - width[i], ascent, + menus[i].name2, len[i]); + XtSetArg (arg[0], XtNrightBitmap, pixmap); + XtSetArg (arg[1], XtNrightMargin, max_width + 20); + XtSetValues (menus[i].w, arg, 2); + } + } + + return button; +} + + +XtActionsRec actions[] = { + {"Expose", ExposeProc}, + {"Configure", ConfigureProc}, + {"Key", KeyProc}, + {"ButtonPress", ButtonProc}, + {"ButtonRelease", ButtonReleaseProc}, + {"ButtonMotion", ButtonMoveProc}, + {"Button2Press", Button2Proc}, + {"MenuHelp", MenuHelpProc} +}; + + +/* Print the usage of this program (the name is PROG), and exit with + EXIT_CODE. */ + +void +help_exit (char *prog, int exit_code) +{ + char *p = prog; + + while (*p) + if (*p++ == '/') + prog = p; + + printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog); + printf ("Display FILE on a window and allow users to edit it.\n"); + printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n"); + printf ("The following OPTIONs are available.\n"); + printf (" %-13s %s", "--version", "print version number\n"); + printf (" %-13s %s", "-h, --help", "print this message\n"); + exit (exit_code); +} + +int +main (int argc, char **argv) +{ + Widget form, BodyWidget, w; + char *fontset_name = NULL; + int col = 80, row = 32; + /* Translation table for TextWidget. */ + String trans = ": Expose()\n\ + : Configure()\n\ + : Key()\n\ + : Key()\n\ + : ButtonPress()\n\ + : ButtonRelease()\n\ + : ButtonMotion()\n\ + : Button2Press()"; + /* Translation table for the top form widget. */ + String trans2 = ": Key()\n\ + : Key()"; + String pop_face_trans + = ": MenuHelp(Pop face property) highlight()\n\ + : MenuHelp() reset()\n\ + : set()\n\ + : notify() unset()"; + String pop_lang_trans + = ": MenuHelp(Pop language property) highlight()\n\ + : MenuHelp() reset()\n\ + : set()\n\ + : notify() unset()"; + int font_width, font_ascent, font_descent; + int with_xim = 0; + int i, j; + + setlocale (LC_ALL, ""); + /* Create the top shell. */ + XtSetLanguageProc (NULL, NULL, NULL); + ShellWidget = XtOpenApplication (&context, "MEdit", NULL, 0, &argc, argv, + NULL, sessionShellWidgetClass, NULL, 0); + display = XtDisplay (ShellWidget); + screen = XScreenNumberOfScreen (XtScreen (ShellWidget)); + + /* Parse the remaining command line arguments. */ + for (i = 1; i < argc; i++) + { + if (! strcmp (argv[i], "--help") + || ! strcmp (argv[i], "-h")) + help_exit (argv[0], 0); + else if (! strcmp (argv[i], "--version")) + { + printf ("medit (m17n library) %s\n", VERSION); + printf ("Copyright (C) 2003 AIST, JAPAN\n"); + exit (0); + } + else if (! strcmp (argv[i], "--geometry")) + { + i++; + if (sscanf (argv[i], "%dx%d", &col, &row) != 2) + help_exit (argv[0], 1); + } + else if (! strcmp (argv[i], "--fontset")) + { + i++; + fontset_name = strdup (argv[i]); + } + else if (! strcmp (argv[i], "--with-xim")) + { + with_xim = 1; + } + else if (argv[i][0] != '-') + { + filename = strdup (argv[i]); + } + else + { + fprintf (stderr, "Unknown option: %s", argv[i]); + help_exit (argv[0], 1); + } + } + if (! filename) + help_exit (argv[0], 1); + + mdatabase_dir = "."; + /* Initialize the m17n library. */ + M17N_INIT (); + if (merror_code != MERROR_NONE) + FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!"); + + mt = read_file (filename); + serialized = 0; + + nchars = mtext_len (mt); + + { + MFace *face = mface (); + + mface_put_prop (face, Mbackground, msymbol ("blue")); + mface_put_prop (face, Mforeground, msymbol ("yellow")); + selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE); + m17n_object_unref (face); + } + + /* This tells ExposeProc to initialize everything. */ + top.from = -1; + + XA_TEXT = XInternAtom (display, "TEXT", False); + XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False); + XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False); + { + MPlist *plist = mplist (); + MFace *face; + MFont *font; + + mplist_put (plist, msymbol ("widget"), ShellWidget); + if (fontset_name) + { + MFontset *fontset = mfontset (fontset_name); + + face = mface (); + mface_put_prop (face, Mfontset, fontset); + m17n_object_unref (fontset); + mplist_add (plist, Mface, face); + m17n_object_unref (face); + } + frame = mframe (plist); + m17n_object_unref (plist); + face_default = (MFace *) mframe_get_prop (frame, Mface); + face_default_fontset = mface (); + mface_put_prop (face_default_fontset, Mfontset, + mface_get_prop (face_default, Mfontset)); + + font = (MFont *) mframe_get_prop (frame, Mfont); + default_font_size = (int) mfont_get_prop (font, Msize); + } + + font_width = (int) mframe_get_prop (frame, Mfont_width); + font_ascent = (int) mframe_get_prop (frame, Mfont_ascent); + font_descent = (int) mframe_get_prop (frame, Mfont_descent); + win_width = font_width * col; + win_height = (font_ascent + font_descent) * row; + + { + MFaceBoxProp prop; + + prop.width = 4; + prop.color_top = prop.color_left = msymbol ("magenta"); + prop.color_bottom = prop.color_right = msymbol ("red"); + prop.inner_hmargin = prop.inner_vmargin = 1; + prop.outer_hmargin = prop.outer_vmargin = 2; + + face_box = mface (); + mface_put_prop (face_box, Mbox, &prop); + } + + face_courier = mface (); + mface_put_prop (face_courier, Mfamily, msymbol ("courier")); + face_helvetica = mface (); + mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica")); + face_times = mface (); + mface_put_prop (face_times, Mfamily, msymbol ("times")); + face_dv_ttyogesh = mface (); + mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh")); + face_freesans = mface (); + mface_put_prop (face_freesans, Mfamily, msymbol ("freesans")); + face_freemono = mface (); + mface_put_prop (face_freemono, Mfamily, msymbol ("freemono")); + + face_xxx_large = mface (); + mface_put_prop (face_xxx_large, Mratio, (void *) 300); + { + MFont *latin_font = mframe_get_prop (frame, Mfont); + MFont *dev_font = mfont (); + MFont *thai_font = mfont (); + MFont *tib_font = mfont (); + MFontset *fontset; + MSymbol unicode_bmp = msymbol ("unicode-bmp"); + MSymbol no_ctl = msymbol ("no-ctl"); + + mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi")); + mfont_put_prop (dev_font, Mregistry, unicode_bmp); + mfont_put_prop (thai_font, Mfamily, msymbol ("norasi")); + mfont_put_prop (thai_font, Mregistry, unicode_bmp); + mfont_put_prop (tib_font, Mfamily, msymbol ("mtib")); + mfont_put_prop (tib_font, Mregistry, unicode_bmp); + + fontset = mfontset_copy (mfontset (fontset_name), "no-ctl"); + mfontset_modify_entry (fontset, msymbol ("latin"), Mnil, Mnil, + latin_font, Mnil, 0); + mfontset_modify_entry (fontset, msymbol ("devanagari"), Mnil, Mnil, + dev_font, no_ctl, 0); + mfontset_modify_entry (fontset, msymbol ("thai"), Mnil, Mnil, + thai_font, no_ctl, 0); + mfontset_modify_entry (fontset, msymbol ("tibetan"), Mnil, Mnil, + tib_font, no_ctl, 0); + face_no_ctl_fontset = mface (); + mface_put_prop (face_no_ctl_fontset, Mfontset, fontset); + m17n_object_unref (fontset); + + free (dev_font); + free (thai_font); + free (tib_font); + } + + setup_input_methods (with_xim); + + gc = DefaultGC (display, screen); + + XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2)); + XtSetArg (arg[1], XtNdefaultDistance, 2); + form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2); + + XtSetArg (arg[0], XtNborderWidth, 0); + XtSetArg (arg[1], XtNdefaultDistance, 2); + XtSetArg (arg[2], XtNtop, XawChainTop); + XtSetArg (arg[3], XtNbottom, XawChainTop); + XtSetArg (arg[4], XtNleft, XawChainLeft); + XtSetArg (arg[5], XtNright, XawChainRight); + XtSetArg (arg[6], XtNresizable, True); + HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7); + XtSetArg (arg[7], XtNfromVert, HeadWidget); + FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8); + XtSetArg (arg[7], XtNfromVert, FaceWidget); + LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8); + XtSetArg (arg[3], XtNbottom, XawChainBottom); + XtSetArg (arg[7], XtNfromVert, LangWidget); + BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8); + XtSetArg (arg[2], XtNtop, XawChainBottom); + XtSetArg (arg[7], XtNfromVert, BodyWidget); + TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8); + + FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass, + HeadWidget, NULL, 0); + XtSetArg (arg[0], XtNvalue, ""); + FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass, + FileShellWidget, arg, 1); + XawDialogAddButton (FileDialogWidget, "OK", + FileDialogProc, (XtPointer) 0); + XawDialogAddButton (FileDialogWidget, "CANCEL", + FileDialogProc, (XtPointer) 1); + + CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen), + (char *) check_bits, + check_width, check_height); + { + unsigned long valuemask = GCForeground; + XGCValues values; + + values.foreground = 1; + mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values); + values.foreground = 0; + mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values); + } + + { + MenuRec *menus; + int num_menus = 10; + + if (num_menus < num_input_methods + 2) + num_menus = num_input_methods + 2; + if (num_menus < num_faces + 1) + num_menus = num_faces + 1; + menus = alloca (sizeof (MenuRec) * num_menus); + + w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu", + FileMenu, sizeof FileMenu / sizeof (MenuRec), + "File I/O, Serialization, Image, Quit"); + + SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1); + SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0); + SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0); + SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0); + SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1); + SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0); + w = create_menu_button (ShellWidget, HeadWidget, w, + "Cursor", "Cursor Menu", + menus, 6, "Cursor Movement Mode, Cursor Shape"); + CursorMenus[0] = menus[0].w; + CursorMenus[1] = menus[1].w; + CursorMenus[2] = menus[3].w; + CursorMenus[3] = menus[4].w; + CursorMenus[4] = menus[5].w; + + SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0); + SetMenu (menus[1], 0, "Left (|--> |)", NULL, BidiProc, 1, 1); + SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0); + w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu", + menus, 3, "BIDI Processing Mode"); + for (i = 0; i < 3; i++) + BidiMenus[i] = menus[i].w; + + SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0); + SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1); + SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0); + w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak", + "LineBreak Menu", + menus, 3, "How to break lines"); + for (i = 0; i < 3; i++) + LineBreakMenus[i] = menus[i].w; + + SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1); + SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0); + for (i = 0; i < num_input_methods; i++) + { + MInputMethod *im = input_method_table[i]; + char *name1, *name2; + + if (im->language != Mnil && im->language != Mt) + { + MSymbol sym = msymbol_get (im->language, Mlanguage); + if (sym == Mnil) + name1 = msymbol_name (im->language); + else + name1 = msymbol_name (sym); + name2 = msymbol_name (im->name); + } + else + name1 = msymbol_name (im->name), name2 = NULL; + + SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0); + } + w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod", + "Input Method Menu", menus, i + 2, + "Select input method"); + + { + unsigned long valuemask = GCForeground; + XGCValues values; + + XtSetArg (arg[0], XtNbackground, &values.foreground); + XtGetValues (w, arg, 1); + gc_inv = XCreateGC (display, RootWindow (display, screen), + valuemask, &values); + } + + InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2)); + for (i = 0; i < num_input_methods + 2; i++) + InputMethodMenus[i] = menus[i].w; + + input_status_width = font_width * 8; + input_status_height = (font_ascent + font_descent) * 2.4; + input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen), + input_status_width, + input_status_height, + DefaultDepth (display, screen)); + { + MFaceBoxProp prop; + + prop.width = 1; + prop.color_top = prop.color_bottom + = prop.color_left = prop.color_right = Mnil; + prop.inner_hmargin = prop.inner_vmargin = 1; + prop.outer_hmargin = prop.outer_vmargin = 0; + face_input_status = mface (); + mface_put_prop (face_input_status, Mbox, &prop); + } + + XFillRectangle (display, input_status_pixmap, gc_inv, + 0, 0, input_status_width, input_status_height); + XtSetArg (arg[0], XtNfromHoriz, w); + XtSetArg (arg[1], XtNleft, XawRubber); + XtSetArg (arg[2], XtNright, XawChainRight); + XtSetArg (arg[3], XtNborderWidth, 0); + XtSetArg (arg[4], XtNlabel, " "); + XtSetArg (arg[5], XtNjustify, XtJustifyRight); + CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass, + HeadWidget, arg, 6); + XtSetArg (arg[0], XtNfromHoriz, CurIMLang); + XtSetArg (arg[1], XtNleft, XawChainRight); + XtSetArg (arg[4], XtNbitmap, input_status_pixmap); + CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass, + HeadWidget, arg, 5); + + XtSetArg (arg[0], XtNborderWidth, 0); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainLeft); + w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3); + for (i = 0; i < num_faces;) + { + char *label_menu = face_table[i++].name; /* "Menu Xxxx" */ + char *label = label_menu + 5; /* "Xxxx" */ + + for (j = i; j < num_faces && face_table[j].face; j++) + SetMenu (menus[j - i], 0, face_table[j].name, NULL, + FaceProc, j, -1); + w = create_menu_button (ShellWidget, FaceWidget, w, + label, label_menu, + menus, j - i, "Push face property"); + i = j; + } + + XtSetArg (arg[0], XtNfromHoriz, w); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainLeft); + XtSetArg (arg[3], XtNhorizDistance, 10); + XtSetArg (arg[4], XtNlabel, "Pop"); + XtSetArg (arg[5], XtNtranslations, + XtParseTranslationTable (pop_face_trans)); + w = XtCreateManagedWidget ("Pop Face", commandWidgetClass, + FaceWidget, arg, 6); + XtAddCallback (w, XtNcallback, FaceProc, (void *) -1); + + XtSetArg (arg[0], XtNfromHoriz, w); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainRight); + XtSetArg (arg[3], XtNlabel, ""); + XtSetArg (arg[4], XtNborderWidth, 0); + XtSetArg (arg[5], XtNjustify, XtJustifyRight); + CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass, + FaceWidget, arg, 6); + + XtSetArg (arg[0], XtNborderWidth, 0); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainLeft); + w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3); + { + MPlist *plist[11], *pl; + char langname[3]; + + for (i = 0; i < 11; i++) plist[i] = NULL; + langname[2] = '\0'; + for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++) + for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++) + { + MSymbol sym = msymbol_exist (langname); + MSymbol fullname; + + if (sym != Mnil + && ((fullname = msymbol_get (sym, Mlanguage)) != Mnil)) + { + char *name = msymbol_name (fullname); + char c = name[0]; + + if (c >= 'A' && c <= 'Z') + { + int idx = (c < 'U') ? (c - 'A') / 2 : 10; + + pl = plist[idx]; + if (! pl) + pl = plist[idx] = mplist (); + for (; mplist_next (pl); pl = mplist_next (pl)) + if (strcmp (name, (char *) mplist_value (pl)) < 0) + break; + mplist_push (pl, sym, fullname); + } + } + } + + for (i = 0; i < 11; i++) + if (plist[i]) + { + char *name = malloc (9); + + sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1); + if (i == 10) + name[7] = 'Z'; + for (j = 0, pl = plist[i]; mplist_next (pl); + j++, pl = mplist_next (pl)) + SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)), + msymbol_name (mplist_key (pl)), + LangProc, mplist_key (pl), -1); + w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name, + menus, j, "Push language property"); + } + for (i = 0; i < 11; i++) + if (plist[i]) + m17n_object_unref (plist[i]); + } + XtSetArg (arg[0], XtNfromHoriz, w); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainLeft); + XtSetArg (arg[3], XtNhorizDistance, 10); + XtSetArg (arg[4], XtNlabel, "Pop"); + XtSetArg (arg[5], XtNtranslations, + XtParseTranslationTable (pop_lang_trans)); + w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass, + LangWidget, arg, 6); + XtAddCallback (w, XtNcallback, LangProc, Mnil); + + XtSetArg (arg[0], XtNfromHoriz, w); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainRight); + XtSetArg (arg[3], XtNlabel, ""); + XtSetArg (arg[4], XtNborderWidth, 0); + XtSetArg (arg[5], XtNjustify, XtJustifyRight); + CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass, + LangWidget, arg, 6); + } + + XtSetArg (arg[0], XtNheight, win_height); + XtSetArg (arg[1], XtNwidth, 10); + XtSetArg (arg[2], XtNleft, XawChainLeft); + XtSetArg (arg[3], XtNright, XawChainLeft); + SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget, + arg, 4); + XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL); + XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL); + + XtSetArg (arg[0], XtNheight, win_height); + XtSetArg (arg[1], XtNwidth, win_width); + XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans)); + XtSetArg (arg[3], XtNfromHoriz, SbarWidget); + XtSetArg (arg[4], XtNleft, XawChainLeft); + XtSetArg (arg[5], XtNright, XawChainRight); + TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget, + arg, 5); + + XtSetArg (arg[0], XtNborderWidth, 0); + XtSetArg (arg[1], XtNleft, XawChainLeft); + XtSetArg (arg[2], XtNright, XawChainRight); + XtSetArg (arg[3], XtNresizable, True); + XtSetArg (arg[4], XtNjustify, XtJustifyLeft); + MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass, + TailWidget, arg, 5); + + memset (&control, 0, sizeof control); + control.two_dimensional = 1; + control.enable_bidi = 1; + control.min_line_ascent = font_ascent; + control.min_line_descent = font_descent; + control.max_line_width = win_width; + control.with_cursor = 1; + control.cursor_width = 2; + control.partial_update = 1; + control.ignore_formatting_char = 1; + + XtAppAddActions (context, actions, XtNumber (actions)); + XtRealizeWidget (ShellWidget); + + win = XtWindow (TextWidget); + + XtAppMainLoop (context); + + if (current_input_context) + minput_destroy_ic (current_input_context); + for (i = 0; i < num_input_methods; i++) + minput_close_im (input_method_table[i]); + m17n_object_unref (frame); + + m17n_object_unref (mt); + m17n_object_unref (face_xxx_large); + m17n_object_unref (face_box); + m17n_object_unref (face_courier); + m17n_object_unref (face_helvetica); + m17n_object_unref (face_times); + m17n_object_unref (face_dv_ttyogesh); + m17n_object_unref (face_freesans); + m17n_object_unref (face_freemono); + m17n_object_unref (face_default_fontset); + m17n_object_unref (face_no_ctl_fontset); + m17n_object_unref (face_input_status); + m17n_object_unref (selection); + + M17N_FINI (); + + free (fontset_name); + + XtUninstallTranslations (form); + XtUninstallTranslations (TextWidget); + XtDestroyWidget (ShellWidget); + XtDestroyApplicationContext (context); + + exit (0); +} +#endif /* not FOR_DOXYGEN */ diff --git a/example/mimx-anthy.c b/example/mimx-anthy.c new file mode 100644 index 0000000..518abb8 --- /dev/null +++ b/example/mimx-anthy.c @@ -0,0 +1,330 @@ +/* mimx-anthy.c -- Input method external module for Anthy. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mimx-anthy external module for the input method + + @section mimx-anthy-description DESCRIPTION + + The shared library mimx-anthy.so is an external module used by the + input method . It exports these functions. + +
    +
  • init + + Initialize this module. + +
  • fini + + Finalize this module. + +
  • convert + + Convert the current preedit text (Hiragana sequence) into + Kana-Kanji mixed text. + +
  • change + + Record the change of candidate of the current segment. + +
  • resize + + Enlarge or shorten the length of the current segment. + +
  • commit + + Commit the lastly selected candidates of all the segments. + +
+ + @section mimx-anthy-seealso See also + @ref mdbIM +*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include +#include + +#ifdef HAVE_ANTHY + +#include + +static int initialized; +static MSymbol Manthy, Msegment; + +/* A structure to record in MInputContext->plist with key Manthy. */ + +typedef struct { + anthy_context_t ac; + /* Which candidate is selected in each segment. */ + int *candidate_numbers; + /* Size of the above array. */ + int num_segments; + /* Converter for this context. */ + MConverter *converter; +} AnthyContext; + +static AnthyContext * +new_context (MInputContext *ic) +{ + AnthyContext *context = NULL; + MSymbol euc_jp = msymbol ("euc-jp"); + /* Rebound to an actual buffer just before being used. */ + MConverter *converter = mconv_buffer_converter (euc_jp, NULL, 0); + + if (converter) + { + context = calloc (1, sizeof (AnthyContext)); + context->ac = anthy_create_context (); + context->num_segments = 0; + context->candidate_numbers = NULL; + context->converter = converter; + } + return context; +} + +static void +free_context (AnthyContext *context) +{ + anthy_release_context (context->ac); + if (context->candidate_numbers) + free (context->candidate_numbers); + mconv_free_converter (context->converter); + free (context); +} + +static void +allocate_candidate_numbers (AnthyContext *context, int num) +{ + if (context->num_segments < num) + { + if (context->num_segments == 0) + context->candidate_numbers = malloc (sizeof (int) * num); + else + context->candidate_numbers = realloc (context->candidate_numbers, + sizeof (int) * num); + context->num_segments = num; + } +} + +static void +add_action (MPlist *actions, MSymbol name, MSymbol key, void *val) +{ + MPlist *action = mplist (); + + mplist_add (action, Msymbol, name); + mplist_add (action, key, val); + mplist_add (actions, Mplist, action); + m17n_object_unref (action); +} + +/* Return a list of all candidates of the Nth segment. The return + value is a plist whose elements are plists who contains at most 5 + candidates. */ + +static MPlist * +make_candidate_list (AnthyContext *context, int n) +{ + MPlist *plist = mplist (), *pl; + int i; + char buf[1024]; + struct anthy_segment_stat ss; + MText *mt; + + anthy_get_segment_stat (context->ac, n, &ss); + for (i = 0, pl = mplist (); i < ss.nr_candidate; i++) + { + anthy_get_segment (context->ac, n, i, buf, sizeof (buf)); + mconv_rebind_buffer (context->converter, + (unsigned char *) buf, strlen (buf)); + mt = mconv_decode (context->converter, mtext ()); + mtext_put_prop (mt, 0, mtext_len (mt), Msegment, (void *) (n + 1)); + mplist_add (pl, Mtext, mt); + m17n_object_unref (mt); + if (i % 5 == 4) + { + mplist_add (plist, Mplist, pl); + m17n_object_unref (pl); + pl = mplist (); + } + } + if (mplist_key (pl) != Mnil) + mplist_add (plist, Mplist, pl); + m17n_object_unref (pl); + return plist; +} + +MPlist * +init (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + + if (! initialized) + { + anthy_init (); + Manthy = msymbol (" anthy"); + Msegment = msymbol (" segment"); + initialized = 1; + } + mplist_push (ic->plist, Manthy, new_context (ic)); + return NULL; +} + +MPlist * +fini (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + AnthyContext *context = mplist_get (ic->plist, Manthy); + + if (context) + free_context (context); + return NULL; +} + +MPlist * +convert (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + AnthyContext *context = mplist_get (ic->plist, Manthy); + struct anthy_conv_stat cs; + MPlist *action, *actions; + int i; + unsigned char buf[1024]; + + if (! context) + return NULL; + + mconv_rebind_buffer (context->converter, buf, sizeof (buf)); + mconv_encode (context->converter, ic->preedit); + buf[context->converter->nbytes] = '\0'; + anthy_set_string (context->ac, (char *) buf); + anthy_get_stat (context->ac, &cs); + allocate_candidate_numbers (context, cs.nr_segment); + + actions = mplist (); + add_action (actions, msymbol ("move"), Msymbol, msymbol ("@<")); + add_action (actions, msymbol ("delete"), Msymbol, msymbol ("@>")); + for (i = 0; i < cs.nr_segment; i++) + { + context->candidate_numbers[i] = 0; + if (i == 1) + add_action (actions, msymbol ("mark"), Msymbol, msymbol ("@anthy")); + action = make_candidate_list (context, i); + mplist_add (actions, Mplist, action); + m17n_object_unref (action); + } + if (cs.nr_segment > 1) + add_action (actions, msymbol ("move"), Msymbol, msymbol ("@anthy")); + + return actions; +} + +MPlist * +change (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + AnthyContext *context = mplist_get (ic->plist, Manthy); + int segment; + + if (! context) + return NULL; + if (! ic->candidate_list || ic->cursor_pos == 0) + return NULL; + segment = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1, Msegment); + if (segment == 0) + return NULL; + segment--; + context->candidate_numbers[segment] = ic->candidate_index; + return NULL; +} + +MPlist * +resize (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + AnthyContext *context = mplist_get (ic->plist, Manthy); + struct anthy_conv_stat cs; + MSymbol shorten; + int segment; + MPlist *actions, *action; + int i; + + if (! context) + return NULL; + if (! ic->candidate_list || ic->cursor_pos == 0) + return NULL; + segment = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1, Msegment); + if (segment == 0) + return NULL; + segment--; + args = mplist_next (args); + shorten = mplist_value (args); + anthy_resize_segment (context->ac, segment, shorten == Mt ? -1 : 1); + anthy_get_stat (context->ac, &cs); + allocate_candidate_numbers (context, cs.nr_segment); + + actions = mplist (); + if (segment == 0) + add_action (actions, msymbol ("move"), Msymbol, msymbol ("@<")); + else + add_action (actions, msymbol ("move"), Msymbol, msymbol ("@[")); + add_action (actions, msymbol ("delete"), Msymbol, msymbol ("@>")); + for (i = segment; i < cs.nr_segment; i++) + { + context->candidate_numbers[i] = 0; + if (i == segment + 1) + add_action (actions, msymbol ("mark"), Msymbol, msymbol ("@anthy")); + action = make_candidate_list (context, i); + mplist_add (actions, Mplist, action); + m17n_object_unref (action); + } + if (segment + 1 < cs.nr_segment) + add_action (actions, msymbol ("move"), Msymbol, msymbol ("@anthy")); + return actions; +} + +MPlist * +commit (MPlist *args) +{ + MInputContext *ic = mplist_value (args); + AnthyContext *context = mplist_get (ic->plist, Manthy); + struct anthy_conv_stat cs; + int i; + + anthy_get_stat (context->ac, &cs); + for (i = 0; i < cs.nr_segment; i++) + anthy_commit_segment (context->ac, i, context->candidate_numbers[i]); + return NULL; +} + +#else /* not HAVE_ANTHY */ + +MPlist *convert (MPlist *args) { return NULL; } +MPlist *change (MPlist *args) { return NULL; } +MPlist *resize (MPlist *args) { return NULL; } +MPlist *commit (MPlist *args) { return NULL; } + +#endif /* not HAVE_ANTHY */ +#endif /* not FOR_DOXYGEN */ diff --git a/example/mimx-ispell.c b/example/mimx-ispell.c new file mode 100644 index 0000000..c81b5fc --- /dev/null +++ b/example/mimx-ispell.c @@ -0,0 +1,202 @@ +/* imx-ispell.c -- Input method external module for Ispell. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mimx-ispell external module for the input method + + @section mimx-ispell-description DESCRIPTION + + The shared library mimx-ispell.so is an external module used by the + input method . It exports these functions. + +
    +
  • init + + Initialize this library. + +
  • fini + + Finalize this library. + +
  • ispell_word + + Check the spell of the current preedit text (English) and, if the + spell is incorrect, return a list of candidates. + +
+ + This program is just for demonstrating how to write an external + module for an m17n input method, not for an actual use. + + @section mimx-ispell-seealso See also + @ref mdbIM +*/ + +#ifndef FOR_DOXYGEN + +#include +#include +#include +#include + +#ifdef HAVE_ISPELL + +static int initialized = 0; +static MFace *mface_overstrike = NULL; + +static MPlist * +add_action (MPlist *actions, MSymbol name, MSymbol key, void *val) +{ + MPlist *action = mplist (); + + mplist_add (action, Msymbol, name); + if (key != Mnil) + mplist_add (action, key, val); + mplist_add (actions, Mplist, action); + m17n_object_unref (action); + return actions; +} + +MPlist * +init (MPlist *args) +{ + if (! initialized++) + { + MFaceHLineProp hline; + + hline.type = MFACE_HLINE_STRIKE_THROUGH; + hline.width = 1; + hline.color = msymbol ("black"); + mface_overstrike = mface (); + mface_put_prop (mface_overstrike, Mhline, &hline); + } + return NULL; +} + +MPlist * +fini (MPlist *args) +{ + if (! --initialized) + m17n_object_unref (mface_overstrike); + return NULL; +} + +MPlist * +ispell_word (MPlist *args) +{ + MInputContext *ic; + unsigned char buf[256]; + int nbytes; + MPlist *actions, *candidates, *plist; + char command[256]; + char **words; + FILE *ispell; + char *p = (char *) buf; + int i, n; + MSymbol init_state; + MSymbol select_state; + MText *mt; + + ic = mplist_value (args); + args = mplist_next (args); + init_state = (MSymbol) mplist_value (args); + args = mplist_next (args); + select_state = (MSymbol) mplist_value (args); + nbytes = mconv_encode_buffer (Mcoding_us_ascii, ic->preedit, buf, 256); + + actions = mplist (); + + if (nbytes < 3) + return add_action (actions, msymbol ("shift"), Msymbol, init_state); + + buf[nbytes] = '\0'; + sprintf (command, "echo %s | ispell -a -m", (char *) buf); + ispell = popen (command, "r"); + if (! ispell) + return add_action (actions, msymbol ("shift"), Msymbol, init_state); + + /* Skip the heading line. */ + fgets (p, 256, ispell); + /* Read just 256 bytes, thus candidates listed after the first 256 + bytes are just ignored. */ + fgets (p, 256, ispell); + pclose (ispell); + p[strlen (p) - 1] = '\0'; + if (*p != '&' && *p != '#') + return add_action (actions, msymbol ("shift"), Msymbol, init_state); + + add_action (actions, msymbol ("delete"), Msymbol, msymbol ("@<")); + if (*p == '#') + { + mt = mtext_dup (ic->preedit); + mtext_push_prop (mt, 0, mtext_len (mt), Mface, mface_overstrike); + mplist_add (actions, Mtext, mt); + add_action (actions, msymbol ("shift"), Msymbol, init_state); + m17n_object_unref (mt); + return actions; + } + + p = strchr (p + 2, ' '); + if (sscanf (p, "%d", &n) != 1) + return add_action (actions, msymbol ("shift"), Msymbol, init_state); + words = alloca (sizeof (char *) * n); + p = strchr (p + 1, ' '); + p = strchr (p + 1, ' '); + for (i = 0; i < n - 1; i++) + { + words[i] = ++p; + p = strchr (p, ','); + if (! p) + { + n = i - 1; + break; + } + *p++ = '\0'; + } + words[i] = ++p; + candidates = mplist (); + for (i = 0; i < n; i++) + { + mt = mconv_decode_buffer (Mcoding_us_ascii, (unsigned char *) words[i], + strlen (words[i])); + mplist_add (candidates, Mtext, mt); + m17n_object_unref (mt); + } + mt = mtext_dup (ic->preedit); + mtext_push_prop (mt, 0, mtext_len (mt), Mface, mface_overstrike); + mplist_add (candidates, Mtext, mt); + m17n_object_unref (mt); + plist = mplist_add (mplist (), Mplist, candidates); + m17n_object_unref (candidates); + mplist_add (actions, Mplist, plist); + m17n_object_unref (plist); + add_action (actions, msymbol ("show"), Mnil, NULL); + add_action (actions, msymbol ("shift"), Msymbol, select_state); + return actions; +} + +#else /* not HAVE_ISPELL */ + +MPlist *ispell_word (MPlist *args) { return NULL; } + +#endif /* not HAVE_ISPELL */ +#endif /* not FOR_DOXYGEN */ diff --git a/example/mview.c b/example/mview.c new file mode 100644 index 0000000..c5b02e0 --- /dev/null +++ b/example/mview.c @@ -0,0 +1,384 @@ +/* mview.c -- File viewer + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @page mview view file + + @section mview-synopsis SYNOPSIS + + mview [ XT-OPTION ...] [ OPTION ... ] [ FILE ] + + @section mview-description DESCRIPTION + + Display FILE on a window. + + If FILE is omitted, the input is taken from standard input. + + XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg). + + The following OPTIONs are available. + +
    + +
  • -e ENCODING + + ENCODING is the encoding of FILE (defaults to UTF-8). + +
  • -s FONTSIZE + + FONTSIZE is the fontsize in point. If ommited, it defaults to the + size of the default font defined in X resource. + +
  • --version + + Print version number. + +
  • -h, --help + + Print this message. + +
+*/ +#ifndef FOR_DOXYGEN + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VERSION "1.0" + +/* Global m17n variables. */ +MFrame *frame; +MText *mt; +MDrawMetric metric; +MDrawControl control; + + +/* Callback procedure for "quit". */ + +void +QuitProc (Widget w, XtPointer client_data, XtPointer call_data) +{ + XtAppSetExitFlag (XtWidgetToApplicationContext (w)); +} + + +/* Move POS to the next line head in M-text MT whose length is LEN. + If POS is already on the last line, set POS to LEN. */ + +#define NEXTLINE(pos, len) \ + do { \ + pos = mtext_character (mt, pos, len, '\n'); \ + if (pos < 0) \ + pos = len; \ + else \ + pos++; \ + } while (0) + + +/* Action procedure for expose event. Redraw partial area of the + widget W. The area is specified in EVENT. */ + +static void +ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num) +{ + XExposeEvent *expose = (XExposeEvent *) event; + int len = mtext_len (mt); + int pos, from, to; + int y = 0, yoff = 0; + MDrawMetric rect; + + /* We must update the area between the Y-positions expose->y and + (expose->y + expose->height). We ignore X-positions. */ + + /* At first, find the line that occupies the Y-position expose->y. + That is the first line to draw. */ + to = 0; + while (1) + { + from = to; + NEXTLINE (to, len); + mdraw_text_extents (frame, mt, from, to, &control, NULL, NULL, &rect); + if (to == len || y + rect.height > expose->y) + break; + y += rect.height; + } + /* The first character to draw is at position FROM. Remeber the + Y-position to start drawing. */ + yoff = y - rect.y; + + /* Next, find the line that occupies the Y-position (expose->y + + expose->height). That is the last line to draw. This time, we + enable caching to utilize it in the later drawing. */ + y += rect.height; + control.disable_caching = 0; + while (to < len && y < expose->y + expose->height) + { + pos = to; + NEXTLINE (to, len); + mdraw_text_extents (frame, mt, pos, to, &control, NULL, NULL, &rect); + y += rect.height; + } + + /* It is decided that we must draw from FROM to the previous + character of TO. */ + mdraw_text_with_control (frame, (MDrawWindow) XtWindow (w), + 0, yoff, mt, from, to, &control); + + /* Disable caching again. */ + control.disable_caching = 1; + + /* If the widget was vertically enlarged too much, shrink it. */ + if (metric.height < expose->y + expose->height) + { + Arg arg; + + XtSetArg (arg, XtNheight, metric.height); + XtSetValues (w, &arg, 0); + } +} + + +/* Print the usage of this program (the name is PROG), and exit with + EXIT_CODE. */ + +void +help_exit (char *prog, int exit_code) +{ + char *p = prog; + + while (*p) + if (*p++ == '/') + prog = p; + + printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] [ FILE ]\n", prog); + printf ("Display FILE on a window.\n"); + printf (" If FILE is omitted, the input is taken from standard input.\n"); + printf (" XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n"); + printf ("The following OPTIONs are available.\n"); + printf (" %-13s %s", "-e ENCODING", + "ENCODING is the encoding of FILE (defaults to UTF-8).\n"); + printf (" %-13s %s", "-s FONTSIZE", + "FONTSIZE is the fontsize in point.\n"); + printf ("\t\tIf ommited, it defaults to the size\n"); + printf ("\t\tof the default font defined in X resource.\n"); + printf (" %-13s %s", "--version", "print version number\n"); + printf (" %-13s %s", "-h, --help", "print this message\n"); + exit (exit_code); +} + + +/* Format MSG by FMT and print the result to the stderr, and exit. */ + +#define FATAL_ERROR(fmt, arg) \ + do { \ + fprintf (stderr, fmt, arg); \ + exit (1); \ + } while (0) + + +/* Adjust FONTSIZE for the resolution of the screen of widget W. */ + +int +adjust_fontsize (Widget w, int fontsize) +{ + Display *display = XtDisplay (w); + int screen_number = XScreenNumberOfScreen (XtScreen (w)); + double pixels = DisplayHeight (display, screen_number); + double mm = DisplayHeightMM (display, screen_number); + + return (fontsize * pixels * 25.4 / mm / 100); +} + + +int +main (int argc, char **argv) +{ + XtAppContext context; + Widget shell, form, quit, viewport, text; + String quit_action = "q: set() notify() unset()"; + XtActionsRec actions[] = { {"Expose", ExposeProc} }; + Arg arg[10]; + int i; + int viewport_width, viewport_height; + char *coding_name = NULL; + FILE *fp = stdin; + MSymbol coding; + int fontsize = 0; + + /* Open an application context. */ + XtSetLanguageProc (NULL, NULL, NULL); + shell = XtOpenApplication (&context, "MView", NULL, 0, &argc, argv, NULL, + sessionShellWidgetClass, NULL, 0); + XtAppAddActions (context, actions, XtNumber (actions)); + + /* Parse the remaining command line arguments. */ + for (i = 1; i < argc; i++) + { + if (! strcmp (argv[i], "--help") + || ! strcmp (argv[i], "-h")) + help_exit (argv[0], 0); + else if (! strcmp (argv[i], "--version")) + { + printf ("mview (m17n library) %s\n", VERSION); + printf ("Copyright (C) 2003 AIST, JAPAN\n"); + exit (0); + } + else if (! strcmp (argv[i], "-e")) + { + i++; + coding_name = argv[i]; + } + else if (! strcmp (argv[i], "-s")) + { + double n; + i++; + n = atof (argv[i]); + if (n <= 0) + FATAL_ERROR ("Invalid fontsize %s!\n", argv[i]); + fontsize = adjust_fontsize (shell, (int) (n * 10)); + } + else if (argv[i][0] != '-') + { + fp = fopen (argv[i], "r"); + if (! fp) + FATAL_ERROR ("Fail to open the file %s!\n", argv[i]); + } + else + { + printf ("Unknown option: %s", argv[i]); + help_exit (argv[0], 1); + } + } + + /* Initialize the m17n library. */ + M17N_INIT (); + if (merror_code != MERROR_NONE) + FATAL_ERROR ("%s\n", "Fail to initialize the m17n library."); + + /* Decide how to decode the input stream. */ + if (coding_name) + { + coding = mconv_resolve_coding (msymbol (coding_name)); + if (coding == Mnil) + FATAL_ERROR ("Invalid coding: %s\n", coding_name); + } + else + coding = Mcoding_utf_8; + + mt = mconv_decode_stream (coding, fp); + fclose (fp); + if (! mt) + FATAL_ERROR ("%s\n", "Fail to decode the input file or stream!"); + + { + MPlist *param = mplist (); + MFace *face = mface (); + + if (fontsize) + mface_put_prop (face, Msize, (void *) fontsize); + mplist_put (param, Mwidget, shell); + mplist_put (param, Mface, face); + frame = mframe (param); + m17n_object_unref (param); + m17n_object_unref (face); + } + + /* Create this widget hierarchy. + Shell - form -+- quit + | + +- viewport - text */ + + form = XtCreateManagedWidget ("form", formWidgetClass, shell, NULL, 0); + XtSetArg (arg[0], XtNleft, XawChainLeft); + XtSetArg (arg[1], XtNright, XawChainLeft); + XtSetArg (arg[2], XtNtop, XawChainTop); + XtSetArg (arg[3], XtNbottom, XawChainTop); + XtSetArg (arg[4], XtNaccelerators, XtParseAcceleratorTable (quit_action)); + quit = XtCreateManagedWidget ("quit", commandWidgetClass, form, arg, 5); + XtAddCallback (quit, XtNcallback, QuitProc, NULL); + + viewport_width = (int) mframe_get_prop (frame, Mfont_width) * 80; + viewport_height + = ((int) mframe_get_prop (frame, Mfont_ascent) + + (int) mframe_get_prop (frame, Mfont_descent)) * 24; + XtSetArg (arg[0], XtNallowVert, True); + XtSetArg (arg[1], XtNforceBars, False); + XtSetArg (arg[2], XtNfromVert, quit); + XtSetArg (arg[3], XtNtop, XawChainTop); + XtSetArg (arg[4], XtNbottom, XawChainBottom); + XtSetArg (arg[5], XtNright, XawChainRight); + XtSetArg (arg[6], XtNwidth, viewport_width); + XtSetArg (arg[7], XtNheight, viewport_height); + viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, form, + arg, 8); + + /* Before creating the text widget, we must calculate the height of + the M-text to draw. */ + control.two_dimensional = 1; + control.enable_bidi = 1; + control.disable_caching = 1; + control.max_line_width = viewport_width; + mdraw_text_extents (frame, mt, 0, mtext_len (mt), &control, + NULL, NULL, &metric); + + { + /* Decide the size of the text widget. */ + XtSetArg (arg[0], XtNwidth, viewport_width); + if (viewport_height > metric.height) + /* The outer viewport is tall enough. */ + XtSetArg (arg[1], XtNheight, viewport_height); + else if (metric.height < 0x8000) + /* The M-text height is within the limit of X. */ + XtSetArg (arg[1], XtNheight, metric.height); + else + /* We can't make such a tall widget. Truncate it. */ + XtSetArg (arg[1], XtNheight, 0x7FFF); + + /* We must provide our own expose event handler. */ + XtSetArg (arg[2], XtNtranslations, + XtParseTranslationTable ((String) ": Expose()")); + text = XtCreateManagedWidget ("text", simpleWidgetClass, viewport, arg, 3); + } + + /* Realize the top widget, and dive into an even loop. */ + XtInstallAllAccelerators (form, form); + XtRealizeWidget (shell); + XtAppMainLoop (context); + + /* Clear away. */ + m17n_object_unref (mt); + m17n_object_unref (frame); + M17N_FINI (); + + exit (0); +} +#endif /* not FOR_DOXYGEN */ diff --git a/m17n-config.in b/m17n-config.in new file mode 100644 index 0000000..a04d3eb --- /dev/null +++ b/m17n-config.in @@ -0,0 +1,105 @@ +#!/bin/sh +# m17n-config -- helper script for the m17n library. -*- coding: euc-jp; -*- +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 +# See the end for copying conditions. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +help () +{ + echo "Usage: $0 [CORE | X] [--version | --cflags | --libs | --libtool]" +} + +if test $# -eq 0; then + help 1>&2 + exit 0 +fi + +LIBNAME=m17n + +case $1 in +CORE) LIBNAME=m17n-core; shift;; +X) LIBNAME=m17n-X; shift;; +esac + +case $1 in +--version) + echo "@PACKAGE_VERSION@";; + +--libs) + if test "@libdir@" != "/usr/lib"; then + echo "-L@libdir@ -l${LIBNAME}" + else + echo "-lm17n-X" + fi;; + +--cflags) + if test "@includedir@" != "/usr/include"; then + echo "-I@includedir@" + fi;; + +--libtool) + echo "@libdir@/lib${LIBNAME}.la" + ;; + +*) + help + exit 1;; +esac +exit 0 + +cat > /dev/null < + + * Version 1.0 released. + + +Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation; either version 2.1 of +the License, or (at your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with the m17n library; if not, write to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307, USA. diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..e87399f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,89 @@ +# Copyright (C) 2003, 2004 +# National Institute of Advanced Industrial Science and Technology (AIST) +# Registration Number H15PRO112 + +# This file is part of the m17n library. + +# The m17n library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. + +# The m17n library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with the m17n library; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. + +lib_LTLIBRARIES = libm17n-core.la libm17n.la libm17n-X.la + +libm17n_core_la_SOURCES = \ + character.h character.c \ + chartab.h chartab.c \ + internal.h \ + plist.h plist.c \ + m17n-core.h m17n-core.c \ + m17n-misc.h \ + mtext.h mtext.c \ + symbol.h symbol.c \ + textprop.h textprop.c + +libm17n_core_la_LDFLAGS = @XML2_LD_FLAGS@ + +libm17n_la_SOURCES = \ + charset.h charset.c \ + coding.h coding.c \ + database.h database.c \ + input.h input.c \ + language.h language.c \ + mlocale.h locale.c \ + m17n.h m17n.c +libm17n_la_LIBADD = libm17n-core.la +libm17n_la_LDFLAGS = -ldl + +GUI_SOURCES = \ + face.h face.c \ + font.h font.c font-ft.c font-flt.c \ + fontset.h fontset.c \ + draw.c \ + input-gui.c \ + internal-gui.h \ + m17n-gui.h m17n-gui.c + +X_LD_FLAGS = ${X_PRE_LIBS} ${X_LIBS} -lX11 -lXt ${X_EXTRA_LIBS} +OPTIONAL_LD_FLAGS = @FRIBIDI_LD_FLAGS@ @FREETYPE_LD_FLAGS@ @OTF_LD_FLAGS@ + +libm17n_X_la_SOURCES = ${GUI_SOURCES} m17n-X.h m17n-X.c +libm17n_X_la_LIBADD = libm17n.la +libm17n_X_la_LDFLAGS = ${X_LD_FLAGS} ${OPTIONAL_LD_FLAGS} + +include_HEADERS = m17n-core.h m17n.h m17n-misc.h m17n-gui.h m17n-X.h + +AM_CPPFLAGS = -DM17NDIR="\"$(datadir)/m17n\"" + +bin_PROGRAMS = linkcore linkshell linkgui + +linkcore_SOURCES = linkcore.c +linkcore_LDADD = libm17n-core.la +linkcore_LDFLAGS = -static + +linkshell_SOURCES = linkshell.c +linkshell_LDADD = libm17n.la +linkshell_LDFLAGS = -static + +linkgui_SOURCES = linkgui.c +linkgui_LDADD = libm17n-X.la +#linkgui_LDFLAGS = -static + +# We should not install the above test programs. +install-binPROGRAMS: +uninstall-binPROGRAMS: + +SRC = ${libm17n_core_la_SOURCES} ${libm17n_la_SOURCES} ${libm17n_X_la_SOURCES} + +TAGS: ${SRC} + etags ${SRC} diff --git a/src/README b/src/README new file mode 100644 index 0000000..afc3a7c --- /dev/null +++ b/src/README @@ -0,0 +1,23 @@ +Naming convention. + +name-space internal external +========== ======== ======== +function mobject__xxx mobject_xxx +symbol variable Mobject__xxx Mobject_xxx +other variable mobject__xxx mobject_xxx +constant macro MOBJECT_XXX +other macro MOBJECT_XXX +type MObject[Xxx] + +A structure is named as MXxxxx and is `typedef'ed with the same name. +Ex: typedef struct MFace MFace + +In comments, a structure member is denoted simply as `', +but a member of a specific structure is denoted as +`MStruct->member_name'. If the member itself is a structure (or a +pointer to a structure) and has a sub-member, the sub-member is +denoted as: + `' + or `sub_name>' + or `MStruct->member_name.subname' + or `MStruct->member_name->subname' diff --git a/src/character.c b/src/character.c new file mode 100644 index 0000000..c46cfa4 --- /dev/null +++ b/src/character.c @@ -0,0 +1,517 @@ +/* character.c -- character module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nCharacter + @brief Character objects and API for them. + + The m17n library represents a @e character by a character code (an + integer). The minimum character code is @c 0. The maximum + character code is defined by the macro #MCHAR_MAX. It is + assured that #MCHAR_MAX is not smaller than @c 0x3FFFFF (22 + bits). + + Characters @c 0 to @c 0x10FFFF are equivalent to the Unicode + characters of the same code values. + + A character can have zero or more properties called @e character + @e properties. A character property consists of a @e key and a + @e value, where key is a symbol and value is anything that can be + cast to (void *). "The character property that belongs + to character C and whose key is K" may be shortened to "the K + property of C". */ + +/***ja + @addtogroup m17nCharacter + @brief ʸ»ú¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï @e ʸ»ú ¤òʸ»ú¥³¡¼¥É¡ÊÀ°¿ô¡Ë¤Çɽ¸½¤¹¤ë¡£ºÇ¾®¤Îʸ + »ú¥³¡¼¥É¤Ï @c 0 ¤Ç¡¢ºÇÂç¤Îʸ»ú¥³¡¼¥É¤Ï¥Þ¥¯¥í #MCHAR_MAX ¤Ë¤è¤Ã¤Æ + ÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£#MCHAR_MAX ¤Ï @c 0x3FFFFF¡Ê22¥Ó¥Ã¥È¡Ë°Ê¾å¤Ç¤¢¤ë + ¤³¤È¤¬Êݾڤµ¤ì¤Æ¤¤¤ë¡£ + + @c 0 ¤«¤é @c 0x10FFFF ¤Þ¤Ç¤Îʸ»ú¤Ï¡¢¤½¤ì¤ÈƱ¤¸Ãͤò»ý¤Ä Unicode ¤Î + ʸ»ú¤Ë³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¡£ + + ³Æʸ»ú¤Ï @e ʸ»ú¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ö¥×¥í¥Ñ¥Æ¥£¤ò0¸Ä°Ê¾å»ý¤Ä¤³¤È¤¬¤Ç¤­ + ¤ë¡£Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥­¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢ + ¤ê¡¢ÃÍ¤Ï (void *) ·¿¤Ë¥­¥ã¥¹¥È¤Ç¤­¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£ + ¡Öʸ»ú C ¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¥­¡¼¤¬ @c K ¤Ç¤¢¤ë¤â¤Î¡×¤ò´Êñ¤Ë + ¡Öʸ»ú C ¤Î K ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */ +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include +#include + +#include "m17n-core.h" +#include "m17n-misc.h" +#include "internal.h" + +typedef struct +{ + MSymbol type; + void *mdb; + MCharTable *table; +} MCharPropRecord; + +static MPlist *char_prop_list; + +static void +free_string (int from, int to, void *str, void *arg) +{ + free (str); +} + + +/* Internal API */ + +int +mchar__init () +{ + char_prop_list = mplist (); + + Mname + = mchar_define_property ("name", Mstring); + Mcategory + = mchar_define_property ("category", Msymbol); + Mcombining_class + = mchar_define_property ("combining-class", Minteger); + Mbidi_category + = mchar_define_property ("bidirectional-category", Msymbol); + Msimple_case_folding + = mchar_define_property ("simple-case-folding", Minteger); + Mcomplicated_case_folding + = mchar_define_property ("complicated-case-folding", Mtext); + Mscript + = mchar_define_property ("script", Msymbol); + + return 0; +} + +void +mchar__fini (void) +{ + MPlist *p; + + for (p = char_prop_list; mplist_key (p) != Mnil; p = mplist_next (p)) + { + MCharPropRecord *record = mplist_value (p); + + if (record->table) + { + if (record->type == Mstring) + mchartable_map (record->table, NULL, free_string, NULL); + M17N_OBJECT_UNREF (record->table); + } + free (record); + } + M17N_OBJECT_UNREF (char_prop_list); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + +/* External API */ + +/*** @addtogroup m17nCharacter */ +/*** @{ */ +/*=*/ + +#ifdef FOR_DOXYGEN +/***en + @brief Maximum character code. + + The macro #MCHAR_MAX gives the maximum character code. */ + +/***ja + @brief ʸ»ú¥³¡¼¥É¤ÎºÇÂçÃÍ + + ¥Þ¥¯¥í #MCHAR_MAX ¤Ïʸ»ú¥³¡¼¥É¤ÎºÇÂçÃͤòÍ¿¤¨¤ë¡£ */ + +#define MCHAR_MAX +/*=*/ +#endif /* FOR_DOXYGEN */ + +/***en + @ingroup m17nCharacter + @name Variables: Keys of character properties + + These symbols are used as keys of character properties. */ + +/***ja + @name ÊÑ¿ô: ʸ»ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼ + + ¤³¤ì¤é¤Î¥·¥ó¥Ü¥ë¤Ïʸ»ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£*/ +/*=*/ +/*** @{ */ + +/***en + @brief Key for script. + + The symbol #Mscript has the name "script" and is used as the key + of a character property. The value of such a property is a symbol + representing the script to which the character belongs. + + Each symbol that represents a script has one of the names listed in + the Unicode Technical Report #24. */ + +/***ja + @brief ¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mscript ¤Ï "script" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥× + ¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¤³¤Îʸ»ú¤Î°¤¹ + ¤ë¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£ + + ¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢Unicode Technical Report + #24 ¤Ë¥ê¥¹¥È¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£ */ + +MSymbol Mscript; + +/*=*/ + +/***en + @brief Key for character name. + + The symbol #Mname has the name "name" and is used as + the key of a character property. The value of such a property is a + C-string representing the name of the character. */ + +/***ja + @brief ̾Á°¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mname ¤Ï "name" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ + ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃÍ¤Ï C-string ¤Ç¤¢¤ê¡¢¤½ + ¤Îʸ»ú¤Î̾Á°¤òɽ¤ï¤¹¡£ */ + +MSymbol Mname; + +/*=*/ + +/***en + @brief Key for general category. + + The symbol #Mcategory has the name "category" and is + used as the key of a character property. The value of such a + property is a symbol representing the general category of + the character. + + Each symbol that represents a general category has one of the + names listed as abbreviations for General Category in + Unicode. */ + +/***ja + @brief °ìÈÌ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mcategory ¤Ï "category" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸ + »ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Âбþ¤¹¤ë + °ìÈÌ¥«¥Æ¥´¥ê ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£ + + °ìÈÌ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢General Category¤Î + ¾Êά·Á¤È¤·¤Æ Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¢¤ë¡£ */ + +MSymbol Mcategory; + +/*=*/ + +/***en + @brief Key for canonical combining class. + + The symbol #Mcombining_class has the name + "combining-class" and is used as the key of a character + property. The value of such a property is an integer that + represents the canonical combining class of the character. + + The meaning of each integer that represents a canonical combining + class is identical to the one defined in Unicode. */ + +/***ja + @brief ɸ½à·ë¹ç¥¯¥é¥¹¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mcombining_class ¤Ï "combining-class" ¤È¤¤¤¦ + ̾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃÍ + ¤Ï¡¢Âбþ¤¹¤ë @e ɸ½à·ë¹ç¥¯¥é¥¹ ¤òɽ¤ï¤¹ÈóÉéÀ°¿ô¤Ç¤¢¤ë¡£ + + ɸ½à·ë¹ç¥¯¥é¥¹¤òɽ¤ï¤¹ÈóÉéÀ°¿ô¤Î°ÕÌ£¤Ï¡¢Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î + ¤ÈƱ¤¸¤Ç¤¢¤ë¡£ */ + +MSymbol Mcombining_class; +/*=*/ + +/***en + @brief Key for bidi category. + + The symbol #Mbidi_category has the name "bidi-category" + and is used as the key of a character property. The value of such + a property is a symbol that represents the bidirectional + category of the character. + + Each symbol that represents a bidirectional category has one of + the names listed as types of Bidirectional Category in + Unicode. */ + +/***ja + @brief ÁÐÊý¸þ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mbidi_category ¤Ï "bidi-category" ¤È¤¤¤¦Ì¾Á° + ¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢ + Âбþ¤¹¤ë @e ÁÐÊý¸þ¥«¥Æ¥´¥ê ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£ + + ÁÐÊý¸þ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢Bidirectional + Category ¤Î·¿¤È¤·¤Æ Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¢¤ë¡£ */ + +MSymbol Mbidi_category; +/*=*/ + +/***en + @brief Key for corresponding single lowercase character. + + The symbol #Msimple_case_folding has the name + "simple-case-folding" and is used as the key of a + character property. The value of such a property is the + corresponding single lowercase character that is used when + comparing M-texts ignoring cases. + + If a character requires a complicated comparison (i.e. cannot be + compared by simply mapping to another single character), the value + of such a property is @c 0xFFFF. In this case, the character has + another property whose key is #Mcomplicated_case_folding. */ + +/***ja + @brief Âбþ¤¹¤ë¾®Ê¸»ú°ìʸ»ú¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Msimple_case_folding ¤Ï "simple-case-folding" + ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤϡ¢Âбþ¤¹¤ë¾®Ê¸»ú°ìʸ»ú¤Ç¤¢¤ê¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤¿ + ʸ»úÎóÈæ³Ó¤ÎºÝ¤Ë»È¤ï¤ì¤ë¡£ + + ¤â¤·¤½¤Î¤è¤¦¤ÊÈæ³Ó¤Ë»ÈÍѤ·ÆÀ¤ëñ°ì¤Î¾®Ê¸»ú¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¡¢¤³¤Î¥× + ¥í¥Ñ¥Æ¥£¤ÎÃÍ¤Ï 0xFFFF ¤Ë¤Ê¤ë¡£¤³¤Î¾ì¹ç¤½¤Îʸ»ú¤Ï¡¢ + #Mcomplicated_case_folding ¤È¤¤¤¦¥­¡¼¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£ */ + +MSymbol Msimple_case_folding; +/***en + @brief Key for corresponding multiple lowercase characters + + The symbol #Mcomplicated_case_folding has the name + "complicated-case-folding" and is used as the key of a + character property. The value of such a property is the + corresponding M-text that contains a sequence of lowercase + characters to be used for comparing M-texts ignoring case. */ + +/***ja + @brief Âбþ¤¹¤ë¾®Ê¸»ú¤ÎÎó¤òɽ¤ï¤¹¥­¡¼ + + ¥·¥ó¥Ü¥ë #Mcomplicated_case_folding ¤Ï + "complicated-case-folding" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£ + ¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Âбþ¤¹¤ë¾®Ê¸»úÎ󤫤é¤Ê + ¤ë M-text ¤Ç¤¢¤ê¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤¿Ê¸»úÎóÈæ³Ó¤ÎºÝ¤Ë»È + ¤ï¤ì¤ë¡£ + */ + +MSymbol Mcomplicated_case_folding; +/*=*/ +/*** @} */ +/*=*/ + +/***en + @brief Define a character property. + + The mchar_define_property () function searches the m17n database + for a data whose tags are \<#Mchar_table, $TYPE, $SYM \>. + Here, $SYM is a symbol whose name is $NAME. $TYPE must be + #Mstring, #Mtext, #Msymbol, #Minteger, or #Mplist. + + @return + If the operation was successful, mchar_define_property () returns + $SYM. Otherwise it returns #Mnil. */ + +/***ja + @brief ʸ»ú¥×¥í¥Ñ¥Æ¥£¤òÄêµÁ¤¹¤ë + + ´Ø¿ô mchar_define_property () ¤Ï¡¢ \<#Mchar_table, $TYPE, $SYM \> + ¤È¤¤¤¦¥¿¥°¤ò»ý¤Ã¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤«¤éõ¤¹¡£ ¤³ + ¤³¤Ç $SYM ¤Ï $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£$TYPE ¤Ï#Mstring, + #Mtext, #Msymbol, #Minteger, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + @return + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð $SYM ¤òÊÖ¤¹¡£ + ¼ºÇÔ¤·¤¿¾ì¹ç¤Ï #Mnil ¤òÊÖ¤¹¡£ */ + +/*** + @errors + @c MERROR_DB + + @seealso + mchar_get_prop (), mchar_put_prop () */ + +MSymbol +mchar_define_property (char *name, MSymbol type) +{ + MSymbol key = msymbol (name); + MCharPropRecord *record; + + record = mplist_get (char_prop_list, key); + if (record) + { + if (record->table) + M17N_OBJECT_UNREF (record->table); + } + else + { + MSTRUCT_CALLOC (record, MERROR_CHAR); + mplist_put (char_prop_list, key, record); + } + + record->type = type; + if (mdatabase__finder + && (record->mdb = (*mdatabase__finder) (Mchar_table, type, key, Mnil))) + { + record->table = NULL; + } + else + { + void *default_value = NULL; + + record->mdb = NULL; + if (type == Minteger) + default_value = (void *) -1; + record->table = mchartable (type, default_value); + } + + return key; +} + +/*=*/ + +/***en + @brief Get the value of a character property. + + The mchar_get_prop () function searches character $C for the + character property whose key is $KEY. + + @return + If the operation was successful, mchar_get_prop () returns the + value of the character property. Otherwise it returns @c + NULL. */ + +/***ja + @brief ʸ»ú¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë + + ´Ø¿ô mchar_get_prop () ¤Ï¡¢Ê¸»ú $C ¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£Ãæ¡¢¥­¡¼¤¬ $KEY + ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mchar_get_prop () ¤Ï¸«¤Ä¤«¤Ã¤¿¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ + ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï #Mnil ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mchar_get_prop} @endlatexonly +*/ + +/*** + @errors + @c MERROR_SYMBOL, @c MERROR_DB + + @seealso + mchar_define_property (), mchar_put_prop () */ + +void * +mchar_get_prop (int c, MSymbol key) +{ + MCharPropRecord *record; + + record = mplist_get (char_prop_list, key); + if (! record) + return NULL; + if (record->mdb) + { + record->table = (*mdatabase__loader) (record->mdb); + if (! record->table) + MERROR (MERROR_DB, NULL); + record->mdb = NULL; + } + return mchartable_lookup (record->table, c); +} + +/*=*/ + +/***en + @brief Set the value of a character property. + + The mchar_put_prop () function searches character $C for the + character property whose key is $KEY and assigns $VAL to the value + of the found property. + + @return + If the operation was successful, mchar_put_prop () returns 0. + Otherwise, it returns -1. */ + +/***ja + @brief ʸ»ú¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÀßÄꤹ¤ë + + ´Ø¿ô mchar_put_prop () ¤Ïʸ»ú $C ¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£Ãæ¡¢¥­¡¼¤¬ $KEY ¤Ç + ¤¢¤ë¤â¤Î¤òõ¤·¡¢¤½¤ÎÃͤȤ·¤Æ $VAL ¤òÀßÄꤹ¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mchar_put_prop () ¤Ï0¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï-1¤òÊÖ + ¤¹¡£ */ + +/*** + @errors + @c MERROR_SYMBOL, @c MERROR_DB + + @seealso + mchar_define_property (), mchar_get_prop () */ + +int +mchar_put_prop (int c, MSymbol key, void *val) +{ + MCharPropRecord *record; + + record = mplist_get (char_prop_list, key); + if (! record) + return -1; + if (record->mdb) + { + record->table = (*mdatabase__loader) (record->mdb); + if (! record->table) + MERROR (MERROR_DB, -1); + M17N_OBJECT_REF (record->table); + record->mdb = NULL; + } + return mchartable_set (record->table, c, val); +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/character.h b/src/character.h new file mode 100644 index 0000000..5a4638a --- /dev/null +++ b/src/character.h @@ -0,0 +1,283 @@ +/* character.h -- header file for the character module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_CHARACTER_H_ +#define _M17N_CHARACTER_H_ + +/* UTF-8 format + + 0-7F 0xxxxxxx + 80-7FF 110xxxxx 10xxxxxx + 800-FFFF 1110xxxx 10xxxxxx 10xxxxxx + 10000-1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + 200000-3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + 4000000-7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + + Unicode range: + 0-10FFFF 0 - 11110uuu 10uuxxxx 10xxxxxx 10xxxxxx (uuuuu <= 0x10) + +*/ + +#define MAX_UTF8_CHAR_BYTES 6 +#define MAX_UNICODE_CHAR_BYTES 4 + +/* Return how many units (char, short, or int) C will occupy in + MText->data. */ + +#define CHAR_UNITS_ASCII(c) ((c) < 0x80) + +#define CHAR_UNITS_UTF8(c) \ + ((c) < 0x80 ? 1 \ + : (c) < 0x800 ? 2 \ + : (c) < 0x10000 ? 3 \ + : (c) < 0x200000 ? 4 \ + : (c) < 0x4000000 ? 5 \ + : 6) + +#define CHAR_UNITS_UTF16(c) \ + ((c) < 0x10000 ? 1 \ + : (c) < 0x110000 ? 2 \ + : 0) + + +#define CHAR_UNITS_UTF32(c) 1 + +#define CHAR_UNITS(c, format) \ + ((c) < 0x80 ? 1 \ + : (format) == MTEXT_FORMAT_UTF8 ? CHAR_UNITS_UTF8 (c) \ + : (format) == MTEXT_FORMAT_UTF16 ? CHAR_UNITS_UTF16 (c) \ + : (format) == MTEXT_FORMAT_ASCII ? 0 \ + : CHAR_UNITS_UTF32 (c)) + +#define CHAR_BYTES CHAR_UNITS_UTF8 + +#define CHAR_UNITS_AT_UTF8(p) \ + (!(*(p) & 0x80) ? 1 \ + : !(*(p) & 0x20) ? 2 \ + : !(*(p) & 0x10) ? 3 \ + : !(*(p) & 0x08) ? 4 \ + : !(*(p) & 0x04) ? 5 \ + : !(*(p) & 0x02) ? 6 \ + : 0) + +#define CHAR_UNITS_AT_UTF16(p) \ + (2 - (*(unsigned short *) (p) < 0xD800 \ + || *(unsigned short *) (p) >= 0xDC00)) + +#define CHAR_UNITS_AT(c, format) \ + ((format) == MTEXT_FORMAT_UTF16 ? CHAR_UNITS_AT_UTF16 (c) \ + : (format) == MTEXT_FORMAT_UTF8 ? CHAR_UNITS_AT_UTF8 (c) \ + : 1) + +#define CHAR_BYTES_AT CHAR_UNITS_AT_UTF8 + +#define CHAR_UNITS_BY_HEAD_UTF8(c) \ + (!((c) & 0x80) ? 1 \ + : !((c) & 0x20) ? 2 \ + : !((c) & 0x10) ? 3 \ + : !((c) & 0x08) ? 4 \ + : !((c) & 0x04) ? 5 \ + : !((c) & 0x02) ? 6 \ + : 0) + +#define CHAR_UNITS_BY_HEAD_UTF16(c) \ + (2 - ((unsigned short) (c) < 0xD800 || (unsigned short) (c) >= 0xDC00)) + +#define CHAR_UNITS_BY_HEAD(c, format) \ + ((format) == MTEXT_FORMAT_UTF16 ? CHAR_UNITS_BY_HEAD_UTF16 (c) \ + : (format) == MTEXT_FORMAT_UTF8 ? CHAR_UNITS_BY_HEAD_UTF8 (c) \ + : 1) + +#define CHAR_BYTES_BY_HEAD CHAR_UNITS_BY_HEAD_UTF8 + +#define STRING_CHAR_UTF8(p) \ + (!((p)[0] & 0x80) ? (p)[0] \ + : !((p)[0] & 0x20) ? ((((p)[0] & 0x1F) << 6) \ + | ((p)[1] & 0x3F)) \ + : !((p)[0] & 0x10) ? ((((p)[0] & 0x0F) << 12) \ + | (((p)[1] & 0x3F) << 6) \ + | ((p)[2] & 0x3F)) \ + : !((p)[0] & 0x08) ? ((((p)[0] & 0x07) << 18) \ + | (((p)[1] & 0x3F) << 12) \ + | (((p)[2] & 0x3F) << 6) \ + | ((p)[3] & 0x3F)) \ + : !((p)[0] & 0x04) ? ((((p)[0] & 0x03) << 24) \ + | (((p)[1] & 0x3F) << 18) \ + | (((p)[2] & 0x3F) << 12) \ + | (((p)[3] & 0x3F) << 6) \ + | ((p)[4] & 0x3F)) \ + : ((((p)[0] & 0x01) << 30) \ + | (((p)[1] & 0x3F) << 24) \ + | (((p)[2] & 0x3F) << 18) \ + | (((p)[3] & 0x3F) << 12) \ + | (((p)[4] & 0x3F) << 6) \ + | ((p)[5] & 0x3F))) + +#define STRING_CHAR_UTF16(p) \ + (((unsigned short) (p)[0] < 0xD800 || (unsigned short) (p)[0] >= 0xDC00) \ + ? (p)[0] \ + : ((((p)[0] - 0xD800) << 10) + ((p)[1] - 0xDC00) + 0x10000)) + + +#define STRING_CHAR STRING_CHAR_UTF8 + + +#define STRING_CHAR_ADVANCE_UTF8(p) \ + (!(*(p) & 0x80) ? *(p)++ \ + : !(*(p) & 0x20) ? (((*(p)++ & 0x1F) << 6) \ + | (*(p)++ & 0x3F)) \ + : !(*(p) & 0x10) ? (((*(p)++ & 0x0F) << 12) \ + | ((*(p)++ & 0x3F) << 6) \ + | (*(p)++ & 0x3F)) \ + : !(*(p) & 0x08) ? (((*(p)++ & 0x07) << 18) \ + | ((*(p)++ & 0x3F) << 12) \ + | ((*(p)++ & 0x3F) << 6) \ + | (*(p)++ & 0x3F)) \ + : !(*(p) & 0x04) ? (((*(p)++ & 0x03) << 24) \ + | ((*(p)++ & 0x3F) << 18) \ + | ((*(p)++ & 0x3F) << 12) \ + | ((*(p)++ & 0x3F) << 6) \ + | (*(p)++ & 0x3F)) \ + : (((*(p)++ & 0x01) << 30) \ + | ((*(p)++ & 0x3F) << 24) \ + | ((*(p)++ & 0x3F) << 18) \ + | ((*(p)++ & 0x3F) << 12) \ + | ((*(p)++ & 0x3F) << 6) \ + | (*(p)++ & 0x3F))) + +#define STRING_CHAR_ADVANCE_UTF16(p) \ + (((unsigned short) (p)[0] < 0xD800 || (unsigned short) (p)[0] >= 0xDC00) \ + ? *(p)++ \ + : (((*(p)++ - 0xD800) << 10) + (*(p)++ - 0xDC00) + 0x10000)) + +#define STRING_CHAR_ADVANCE STRING_CHAR_ADVANCE_UTF8 + +#define STRING_CHAR_AND_UNITS_UTF8(p, bytes) \ + (!((p)[0] & 0x80) ? ((bytes) = 1, (p)[0]) \ + : !((p)[0] & 0x20) ? ((bytes) = 2, \ + ((((p)[0] & 0x1F) << 6) \ + | ((p)[1] & 0x3F))) \ + : !((p)[0] & 0x10) ? ((bytes) = 3, \ + ((((p)[0] & 0x0F) << 12) \ + | (((p)[1] & 0x3F) << 6) \ + | ((p)[2] & 0x3F))) \ + : !((p)[0] & 0x08) ? ((bytes) = 4, \ + ((((p)[0] & 0x07) << 18) \ + | (((p)[1] & 0x3F) << 12) \ + | (((p)[2] & 0x3F) << 6) \ + | ((p)[3] & 0x3F))) \ + : !((p)[0] & 0x04) ? ((bytes) = 5, \ + ((((p)[0] & 0x03) << 24) \ + | (((p)[1] & 0x3F) << 18) \ + | (((p)[2] & 0x3F) << 12) \ + | (((p)[3] & 0x3F) << 6) \ + | ((p)[4] & 0x3F))) \ + : ((bytes) = 6, \ + ((((p)[0] & 0x01) << 30) \ + | (((p)[1] & 0x3F) << 24) \ + | (((p)[2] & 0x3F) << 18) \ + | (((p)[3] & 0x3F) << 12) \ + | (((p)[4] & 0x3F) << 6) \ + | ((p)[5] & 0x3F)))) + +#define STRING_CHAR_AND_UNITS_UTF16(p, units) \ + (((unsigned short) (p)[0] < 0xD800 || (unsigned short) (p)[0] >= 0xDC00) \ + ? ((units) = 1, (p)[0]) \ + : ((units) = 2, \ + (((p)[0] - 0xD800) << 10) + ((p)[1] - 0xDC00) + 0x10000)) + +#define STRING_CHAR_AND_UNITS(p, units, format) \ + ((format) == MTEXT_FORMAT_UTF16 \ + ? STRING_CHAR_AND_UNITS_UTF16 (p, units) \ + : (format) == MTEXT_FORMAT_UTF8 \ + ? STRING_CHAR_AND_UNITS_UTF8 (p, units) \ + : ((units) = 1, (p)[0])) + + +#define STRING_CHAR_AND_BYTES STRING_CHAR_AND_UNITS_UTF8 + +#define CHAR_STRING_UTF8(c, p) \ + ((c) < 0x80 \ + ? ((p)[0] = (c), 1) \ + : (c) < 0x800 ? ((p)[0] = (0xC0 | ((c) >> 6)), \ + (p)[1] = (0x80 | ((c) & 0x3F)), \ + 2) \ + : (c) < 0x10000 ? ((p)[0] = (0xE0 | ((c) >> 12)), \ + (p)[1] = (0x80 | (((c) >> 6) & 0x3F)), \ + (p)[2] = (0x80 | ((c) & 0x3F)), \ + 3) \ + : (c) < 0x200000 ? ((p)[0] = (0xF0 | ((c) >> 18)), \ + (p)[1] = (0x80 | (((c) >> 12) & 0x3F)), \ + (p)[2] = (0x80 | (((c) >> 6) & 0x3F)), \ + (p)[3] = (0x80 | ((c) & 0x3F)), \ + 4) \ + : (c) < 0x4000000 ? ((p)[0] = 0xF8, \ + (p)[1] = (0x80 | ((c) >> 18)), \ + (p)[2] = (0x80 | (((c) >> 12) & 0x3F)), \ + (p)[3] = (0x80 | (((c) >> 6) & 0x3F)), \ + (p)[4] = (0x80 | ((c) & 0x3F)), \ + 5) \ + : ((p)[0] = (0xFC | ((c) >> 30)), \ + (p)[1] = (0x80 | (((c) >> 24) & 0x3F)), \ + (p)[2] = (0x80 | (((c) >> 18) & 0x3F)), \ + (p)[3] = (0x80 | (((c) >> 12) & 0x3F)), \ + (p)[4] = (0x80 | (((c) >> 6) & 0x3F)), \ + (p)[5] = (0x80 | ((c) & 0x3F)), \ + 6)) + +#define CHAR_STRING_UTF16(c, p) \ + ((c) < 0x10000 ? (p)[0] = (c), 1 \ + ? (p[0] = (((c) - 0x10000) >> 10) + 0xD800, \ + p[1] = (((c) - 0x10000) & 0x3FF) + 0xDC00, \ + 2)) + +#define CHAR_STRING CHAR_STRING_UTF8 + +#define CHAR_HEAD_P_UTF8(p) \ + ((*(p) & 0xC0) != 0x80) + +#define CHAR_HEAD_P_UTF16(p) \ + (*(unsigned short *) (p) < 0xDC00 \ + || *(unsigned short *) (p) >= 0xE000) + +#define CHAR_HEAD_P CHAR_HEAD_P_UTF8 + +/** Locale-safe version of tolower (). It works only for an ASCII + character. */ +#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + 32 : (c)) + +/** Locale-safe version of toupper (). It works only for an ASCII + character. */ +#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 32 : (c)) + +/** Locale-safe version of isupper (). It works only for an ASCII + character. */ +#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z') + +/** Locale-safe version of isalnum (). It works only for an ASCII + character. */ +#define ISALNUM(c) \ + (((c) >= 'A' && (c) <= 'Z') \ + || ((c) >= 'a' && (c) <= 'z') \ + || ((c) >= '0' && (c) <= '9')) + +#endif /* not _M17N_CHARACTER_H_ */ diff --git a/src/charset.c b/src/charset.c new file mode 100644 index 0000000..a2d5755 --- /dev/null +++ b/src/charset.c @@ -0,0 +1,1455 @@ +/* charset.c -- charset module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nCharset + @brief Charset objects and API for them. + + The m17n library uses @e charset objects to represent a coded + character sets (CCS). The m17n library supports many predefined + coded character sets. Moreover, application programs can add + other charsets. A character can belong to multiple charsets. + + The m17n library distinguishes the following three concepts: + + @li A @e code-point is a number assigned by the CCS to each + character. Code-points may or may not be continuous. The type + @c unsigned is used to represent a code-point. An invalid + code-point is represented by the macro @c MCHAR_INVALID_CODE. + + @li A @e character @e index is the canonical index of a character + in a CCS. The character that has the character index N occupies + the Nth position when all the characters in the current CCS are + sorted by their code-points. Character indices in a CCS are + continuous and start with 0. + + @li A @e character @e code is the internal representation in the + m17n library of a character. A character code is a signed integer + of 21 bits or longer. + + Each charset object defines how characters are converted between + code-points and character codes. To @e encode means converting + code-points to character codes and to @e decode means converting + character codes to code-points. */ + +/***ja + @addtogroup m17nCharset + @brief ʸ»ú¥»¥Ã¥È¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¡¢Éä¹æ²½Ê¸»ú½¸¹ç (CCS) ¤ò @e ʸ»ú¥»¥Ã¥È ¤È¸Æ¤Ö¥ª + ¥Ö¥¸¥§¥¯¥È¤Çɽ¸½¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤Ï¿¤¯¤ÎÉä¹æ²½Ê¸»ú½¸¹ç¤òͽ¤á + ¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤¬¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Ëʸ»ú¥»¥Ã¥È¤ò + Äɲ乤뤳¤È¤â²Äǽ¤Ç¤¢¤ë¡£°ì¤Ä¤Îʸ»ú¤ÏÊ£¿ô¤Îʸ»ú¥»¥Ã¥È¤Ë°¤·¤Æ¤â¤è + ¤¤¡£ + + m17n ¥é¥¤¥Ö¥é¥ê¤Ë¤Ï¡¢°Ê²¼¤Î°Û¤Ê¤ë³µÇ°¤¬¤¢¤ë: + + @li @e ¥³¡¼¥É¥Ý¥¤¥ó¥È ¤È¤Ï¡¢CCS ¤¬¤½¤ÎÃæ¤Î¸Ä¡¹¤Îʸ»ú¤ËÂФ·¤ÆÄêµÁ¤¹ + ¤ë¿ôÃͤǤ¢¤ë¡£¥³¡¼¥É¥Ý¥¤¥ó¥È¤ÏϢ³¤·¤Æ¤¤¤ë¤È¤Ï¸Â¤é¤Ê¤¤¡£ + + @li @e ʸ»ú¥¤¥ó¥Ç¥Ã¥¯¥¹ ¤È¤Ï¡¢CCS Æâ¤Ç³Æʸ»ú¤Ë³ä¤êÅö¤Æ¤é¤ì¤ëÀµµ¬²½¤µ + ¤ì¤¿¥¤¥ó¥Ç¥Ã¥¯¥¹¤Ç¤¢¤ë¡£Ê¸»ú¥¤¥ó¥Ç¥Ã¥¯¥¹¤¬N¤Îʸ»ú¤Ï¡¢CCS Ãæ¤ÎÁ´Ê¸»ú¤ò + ¥³¡¼¥É¥Ý¥¤¥ó¥È¤Ç¥½¡¼¥È¤·¤¿¤È¤­¤ËNÈÖÌܤËÍè¤ë¡£ + + @li @e ʸ»ú¥³¡¼¥É¤È¤Ï¡¢m17n ¥é¥¤¥Ö¥é¥êÆâ¤Ë¤ª¤±¤ëʸ»ú¤ÎÆâÉôɽ¸½¤Ç¤¢ + ¤ê¡¢21 ¥Ó¥Ã¥È°Ê¾å¤ÎŤµ¤ò»ý¤ÄÉä¹çÉÕ¤­À°¿ô¤Ç¤¢¤ë¡£ + + ³Æʸ»ú¥»¥Ã¥È¥ª¥Ö¥¸¥§¥¯¥È¤Ï¡¢¤½¤ì¤Ë°¤¹¤ëʸ»ú¤Î¥³¡¼¥É¥Ý¥¤¥ó¥È¤Èʸ»ú + ¥³¡¼¥É¤È¤ÎÁê¸ßÊÑ´¹¤òµ¬Äꤹ¤ë¡£¥³¡¼¥É¥Ý¥¤¥ó¥È¤«¤éʸ»ú¥³¡¼¥É¤Ø¤ÎÊÑ´¹ + ¤ò @e ¥Ç¥³¡¼¥É ¤È¸Æ¤Ó¡¢Ê¸»ú¥³¡¼¥É¤«¤é¥³¡¼¥É¥Ý¥¤¥ó¥È¤Ø¤ÎÊÑ´¹¤ò @e + ¥¨¥ó¥³¡¼¥É ¤È¸Æ¤Ö¡£ */ + +/*=*/ +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "charset.h" +#include "coding.h" +#include "chartab.h" +#include "plist.h" + +static int unified_max = MCHAR_MAX; + +/** List of all charsets ever defined. */ + +struct MCharsetList +{ + int size, inc, used; + MCharset **charsets; +}; + +static struct MCharsetList charset_list; + +static MPlist *charset_definition_list; + +/** Make a charset object from the template of MCharset structure + CHARSET, and return a pointer to the new charset object. + CHARSET->code_range[4N + 2] and TMPL->code_range[4N + 3] are not + yet set. */ + +static MCharset * +make_charset (MCharset *charset) +{ + unsigned min_code, max_code; + int i, n; + int *range = charset->code_range; + + if (charset->dimension < 1 || charset->dimension > 4) + MERROR (MERROR_CHARSET, NULL); + if ((charset->final_byte > 0 && charset->final_byte < '0') + || charset->final_byte > 127) + MERROR (MERROR_CHARSET, NULL); + + for (i = 0, n = 1; i < 4; i++) + { + if (range[i * 4] > range[i * 4 + 1]) + MERROR (MERROR_CHARSET, NULL); + range[i * 4 + 2] = range[i * 4 + 1] - range[i * 4] + 1; + n *= range[i * 4 + 2]; + range[i * 4 + 3] = n; + } + + min_code = range[0] | (range[4] << 8) | (range[8] << 16) | (range[12] << 24); + if (charset->min_code == 0) + charset->min_code = min_code; + else if (charset->min_code < min_code) + MERROR (MERROR_CHARSET, NULL); + max_code = range[1] | (range[5] << 8) | (range[9] << 16) | (range[13] << 24); + if (charset->max_code == 0) + charset->max_code = max_code; + else if (charset->max_code > max_code) + MERROR (MERROR_CHARSET, NULL); + + charset->code_range_min_code = min_code; + + if (charset->method == Msubset) + { + MCharset *parent; + + if (charset->nparents != 1) + MERROR (MERROR_CHARSET, NULL); + parent = charset->parents[0]; + if (parent->method == Msuperset + || charset->min_code - charset->subset_offset < parent->min_code + || charset->max_code - charset->subset_offset > parent->max_code) + MERROR (MERROR_CHARSET, NULL); + if (parent->method == Moffset) + { + unsigned code; + + code = charset->min_code - charset->subset_offset; + charset->min_char = DECODE_CHAR (parent, code); + code = charset->max_code - charset->subset_offset; + charset->max_char = DECODE_CHAR (parent, code); + } + else + { + unsigned min_code = charset->min_code - charset->subset_offset; + unsigned max_code = charset->max_code - charset->subset_offset; + int min_char = DECODE_CHAR (parent, min_code); + int max_char = min_char; + + for (++min_code; min_code <= max_code; min_code++) + { + int c = DECODE_CHAR (parent, min_code); + + if (c >= 0) + { + if (c < min_char) + min_char = c; + else if (c > max_char) + max_char = c; + } + } + charset->min_char = min_char; + charset->max_char = max_char; + } + charset->simple = 0; + } + else if (charset->method == Msuperset) + { + int min_char = 0, max_char = 0; + + if (charset->nparents < 2) + MERROR (MERROR_CHARSET, NULL); + for (i = 0; i < charset->nparents; i++) + if (charset->min_code > charset->parents[i]->min_code + || charset->max_code < charset->parents[i]->max_code) + MERROR (MERROR_CHARSET, NULL); + + for (i = 0; i < charset->nparents; i++) + { + MCharset *parent = charset->parents[i]; + + if (charset->min_code > parent->min_code + || charset->max_code < parent->max_code) + MERROR (MERROR_CHARSET, NULL); + if (i == 0) + min_char = parent->min_char, max_char = parent->max_char; + else if (parent->min_char < min_char) + min_char = parent->min_char; + else if (parent->max_char > max_char) + max_char = parent->max_char; + } + charset->min_char = min_char; + charset->max_char = max_char; + charset->simple = 0; + } + else + { + charset->no_code_gap + = (charset->dimension == 1 + || (range[2] == 256 + && (charset->dimension == 2 + || (range[6] == 256 + && (charset->dimension == 3 + || range[10] == 256))))); + + if (! charset->no_code_gap) + { + int j; + + memset (charset->code_range_mask, 0, + sizeof charset->code_range_mask); + for (i = 0; i < 4; i++) + for (j = range[i * 4]; j <= range[i * 4 + 1]; j++) + charset->code_range_mask[j] |= (1 << i); + } + + if (charset->method == Moffset) + { + charset->max_char = charset->min_char + range[15] - 1; + if (charset->min_char < 0 + || charset->max_char < 0 || charset->max_char > unified_max) + MERROR (MERROR_CHARSET, NULL); + charset->simple = charset->no_code_gap; + } + else if (charset->method == Mmap || charset->method == Munify) + { + MDatabase *mdb = mdatabase_find (Mcharset, charset->name, + Mnil, Mnil); + MPlist *plist; + + charset->simple = 0; + if (charset->method == Munify) + { + /* The magic number 12 below is to align to the + SUB_BITS_2 (defined in chartab.c) boundary in a + char-table. */ + unified_max -= ((range[15] >> 12) + 1) << 12; + charset->unified_max = unified_max; + } + + if (! mdb || ! (plist = mdatabase_load (mdb))) + MERROR (MERROR_CHARSET, NULL); + charset->decoder = mplist_value (plist); + charset->encoder = mplist_value (mplist_next (plist)); + M17N_OBJECT_UNREF (plist); + mchartable_range (charset->encoder, + &charset->min_char, &charset->max_char); + if (charset->method == Mmap) + charset->simple = charset->no_code_gap; + else + charset->max_char + = charset->unified_max + 1 + charset->code_range[15]; + } + else + MERROR (MERROR_CHARSET, NULL); + } + + MLIST_APPEND1 (&charset_list, charsets, charset, MERROR_CHARSET); + + if (charset->final_byte > 0) + { + MLIST_APPEND1 (&mcharset__iso_2022_table, charsets, charset, + MERROR_CHARSET); + if (charset->revision <= 0) + { + int chars = range[2]; + + if (chars == 128) /* ASCII case */ + chars = 94; + else if (chars == 256) /* ISO-8859-X case */ + chars = 96; + MCHARSET_ISO_2022 (charset->dimension, chars, charset->final_byte) + = charset; + } + } + + charset->fully_loaded = 1; + return charset; +} + +static int +load_charset_fully (MCharset *charset) +{ + if (charset->method == Msubset) + { + MCharset *parent = charset->parents[0]; + + if (! parent->fully_loaded + && load_charset_fully (parent) < 0) + MERROR (MERROR_CHARSET, -1); + if (parent->method == Moffset) + { + unsigned code; + + code = charset->min_code - charset->subset_offset; + charset->min_char = DECODE_CHAR (parent, code); + code = charset->max_code - charset->subset_offset; + charset->max_char = DECODE_CHAR (parent, code); + } + else + { + unsigned min_code = charset->min_code - charset->subset_offset; + unsigned max_code = charset->max_code - charset->subset_offset; + int min_char = DECODE_CHAR (parent, min_code); + int max_char = min_char; + + for (++min_code; min_code <= max_code; min_code++) + { + int c = DECODE_CHAR (parent, min_code); + + if (c >= 0) + { + if (c < min_char) + min_char = c; + else if (c > max_char) + max_char = c; + } + } + charset->min_char = min_char; + charset->max_char = max_char; + } + } + else if (charset->method == Msuperset) + { + int min_char = 0, max_char = 0; + int i; + + for (i = 0; i < charset->nparents; i++) + { + MCharset *parent = charset->parents[i]; + + if (! parent->fully_loaded + && load_charset_fully (parent) < 0) + MERROR (MERROR_CHARSET, -1); + if (i == 0) + min_char = parent->min_char, max_char = parent->max_char; + else if (parent->min_char < min_char) + min_char = parent->min_char; + else if (parent->max_char > max_char) + max_char = parent->max_char; + } + charset->min_char = min_char; + charset->max_char = max_char; + } + else /* charset->method is Mmap or Munify */ + { + MDatabase *mdb = mdatabase_find (Mcharset, charset->name, Mnil, Mnil); + MPlist *plist; + + if (! mdb || ! (plist = mdatabase_load (mdb))) + MERROR (MERROR_CHARSET, -1); + charset->decoder = mplist_value (plist); + charset->encoder = mplist_value (mplist_next (plist)); + M17N_OBJECT_UNREF (plist); + mchartable_range (charset->encoder, + &charset->min_char, &charset->max_char); + if (charset->method == Mmap) + charset->simple = charset->no_code_gap; + else + charset->max_char = charset->unified_max + 1 + charset->code_range[15]; + } + + charset->fully_loaded = 1; + return 0; +} + + +/* Internal API */ + +MPlist *mcharset__cache; + +/* Predefined charsets. */ +MCharset *mcharset__ascii; +MCharset *mcharset__binary; +MCharset *mcharset__m17n; +MCharset *mcharset__unicode; + +MCharsetISO2022Table mcharset__iso_2022_table; + +/** Initialize charset handler. */ + +int +mcharset__init () +{ + MPlist *param, *pl; + + mcharset__cache = mplist (); + mplist_set (mcharset__cache, Mt, NULL); + + MLIST_INIT1 (&charset_list, charsets, 128); + MLIST_INIT1 (&mcharset__iso_2022_table, charsets, 128); + charset_definition_list = mplist (); + + memset (mcharset__iso_2022_table.classified, 0, + sizeof (mcharset__iso_2022_table.classified)); + + Mcharset = msymbol ("charset"); + + Mmethod = msymbol ("method"); + Moffset = msymbol ("offset"); + Mmap = msymbol ("map"); + Munify = msymbol ("unify"); + Msubset = msymbol ("subset"); + Msuperset = msymbol ("superset"); + + Mdimension = msymbol ("dimension"); + Mmin_range = msymbol ("min-range"); + Mmax_range = msymbol ("max-range"); + Mmin_code = msymbol ("min-code"); + Mmax_code = msymbol ("max-code"); + Mascii_compatible = msymbol ("ascii-compatible"); + Mfinal_byte = msymbol ("final-byte"); + Mrevision = msymbol ("revision"); + Mmin_char = msymbol ("min-char"); + Mmapfile = msymbol_as_managing_key ("mapfile"); + Mparents = msymbol_as_managing_key ("parents"); + Msubset_offset = msymbol ("subset-offset"); + Mdefine_coding = msymbol ("define-coding"); + Maliases = msymbol_as_managing_key ("aliases"); + + param = mplist (); + pl = param; + /* Setup predefined charsets. */ + pl = mplist_add (pl, Mmethod, Moffset); + pl = mplist_add (pl, Mmin_range, (void *) 0); + pl = mplist_add (pl, Mmax_range, (void *) 0x7F); + pl = mplist_add (pl, Mascii_compatible, Mt); + pl = mplist_add (pl, Mfinal_byte, (void *) 'B'); + pl = mplist_add (pl, Mmin_char, (void *) 0); + Mcharset_ascii = mchar_define_charset ("ascii", param); + + mplist_put (param, Mmax_range, (void *) 0xFF); + mplist_put (param, Mfinal_byte, NULL); + Mcharset_iso_8859_1 = mchar_define_charset ("iso-8859-1", param); + + mplist_put (param, Mmax_range, (void *) 0x10FFFF); + Mcharset_unicode = mchar_define_charset ("unicode", param); + + mplist_put (param, Mmax_range, (void *) MCHAR_MAX); + Mcharset_m17n = mchar_define_charset ("m17n", param); + + mplist_put (param, Mmax_range, (void *) 0xFF); + Mcharset_binary = mchar_define_charset ("binary", param); + + M17N_OBJECT_UNREF (param); + + mcharset__ascii = MCHARSET (Mcharset_ascii); + mcharset__binary = MCHARSET (Mcharset_binary); + mcharset__m17n = MCHARSET (Mcharset_m17n); + mcharset__unicode = MCHARSET (Mcharset_unicode); + + return 0; +} + +void +mcharset__fini (void) +{ + int i; + MPlist *plist; + + for (i = 0; i < charset_list.used; i++) + { + MCharset *charset = charset_list.charsets[i]; + + if (charset->decoder) + free (charset->decoder); + if (charset->encoder) + M17N_OBJECT_UNREF (charset->encoder); + free (charset); + } + M17N_OBJECT_UNREF (mcharset__cache); + MLIST_FREE1 (&charset_list, charsets); + MLIST_FREE1 (&mcharset__iso_2022_table, charsets); + MPLIST_DO (plist, charset_definition_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (charset_definition_list); +} + + +MCharset * +mcharset__find (MSymbol name) +{ + MCharset *charset; + + charset = msymbol_get (name, Mcharset); + if (! charset) + { + MPlist *param = mplist_get (charset_definition_list, name); + + MPLIST_KEY (mcharset__cache) = Mt; + if (! param) + return NULL; + param = mplist__from_plist (param); + mchar_define_charset (MSYMBOL_NAME (name), param); + charset = msymbol_get (name, Mcharset); + M17N_OBJECT_UNREF (param); + } + MPLIST_KEY (mcharset__cache) = name; + MPLIST_VAL (mcharset__cache) = charset; + return charset; +} + + +/** Return the character corresponding to code-point CODE in CHARSET. + If CODE is invalid for CHARSET, return -1. */ + +int +mcharset__decode_char (MCharset *charset, unsigned code) +{ + int idx; + + if (code < 128 && charset->ascii_compatible) + return (int) code; + if (code < charset->min_code || code > charset->max_code) + return -1; + + if (! charset->fully_loaded + && load_charset_fully (charset) < 0) + MERROR (MERROR_CHARSET, -1); + + if (charset->method == Msubset) + { + MCharset *parent = charset->parents[0]; + + code -= charset->subset_offset; + return DECODE_CHAR (parent, code); + } + + if (charset->method == Msuperset) + { + int i; + + for (i = 0; i < charset->nparents; i++) + { + MCharset *parent = charset->parents[i]; + int c = DECODE_CHAR (parent, code); + + if (c >= 0) + return c; + } + return -1; + } + + idx = CODE_POINT_TO_INDEX (charset, code); + if (idx < 0) + return -1; + + if (charset->method == Mmap) + return charset->decoder[idx]; + + if (charset->method == Munify) + { + int c = charset->decoder[idx]; + + if (c < 0) + c = charset->unified_max + 1 + idx; + return c; + } + + /* Now charset->method should be Moffset. */ + return (charset->min_char + idx); +} + + +/** Return the code point of character C in CHARSET. If CHARSET does not + contain C, return MCHAR_INVALID_CODE. */ + +unsigned +mcharset__encode_char (MCharset *charset, int c) +{ + if (! charset->fully_loaded + && load_charset_fully (charset) < 0) + MERROR (MERROR_CHARSET, MCHAR_INVALID_CODE); + + if (charset->method == Msubset) + { + MCharset *parent = charset->parents[0]; + unsigned code = ENCODE_CHAR (parent, c); + + if (code == MCHAR_INVALID_CODE) + return code; + code += charset->subset_offset; + if (code >= charset->min_code && code <= charset->max_code) + return code; + return MCHAR_INVALID_CODE; + } + + if (charset->method == Msuperset) + { + int i; + + for (i = 0; i < charset->nparents; i++) + { + MCharset *parent = charset->parents[i]; + unsigned code = ENCODE_CHAR (parent, c); + + if (code != MCHAR_INVALID_CODE) + return code; + } + return MCHAR_INVALID_CODE; + } + + if (c < charset->min_char || c > charset->max_char) + return MCHAR_INVALID_CODE; + + if (charset->method == Mmap) + return (unsigned) mchartable_lookup (charset->encoder, c); + + if (charset->method == Munify) + { + if (c > charset->unified_max) + { + c -= charset->unified_max - 1; + return INDEX_TO_CODE_POINT (charset, c); + } + return (unsigned) mchartable_lookup (charset->encoder, c); + } + + /* Now charset->method should be Moffset */ + c -= charset->min_char; + return INDEX_TO_CODE_POINT (charset, c); +} + +int +mcharset__load_from_database () +{ + MDatabase *mdb = mdatabase_find (msymbol ("charset-list"), Mnil, Mnil, Mnil); + MPlist *def_list, *plist; + MPlist *definitions = charset_definition_list; + int mdebug_mask = MDEBUG_CHARSET; + + if (! mdb) + return 0; + MDEBUG_PUSH_TIME (); + def_list = (MPlist *) mdatabase_load (mdb); + MDEBUG_PRINT_TIME ("CHARSET", (stderr, " to load data.")); + MDEBUG_POP_TIME (); + if (! def_list) + return -1; + + MDEBUG_PUSH_TIME (); + MPLIST_DO (plist, def_list) + { + MPlist *pl; + MSymbol name; + + if (! MPLIST_PLIST_P (plist)) + MERROR (MERROR_CHARSET, -1); + pl = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_CHARSET, -1); + name = MPLIST_SYMBOL (pl); + pl = MPLIST_NEXT (pl); + definitions = mplist_add (definitions, name, pl); + M17N_OBJECT_REF (pl); + if ((pl = mplist_find_by_value (pl, Mdefine_coding)) + && (MSymbol) MPLIST_VAL (MPLIST_NEXT (pl)) == Mt) + mconv__register_charset_coding (name); + } + + M17N_OBJECT_UNREF (def_list); + MDEBUG_PRINT_TIME ("CHARSET", (stderr, " to parse the loaded data.")); + MDEBUG_POP_TIME (); + return 0; +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nCharset */ +/*** @{ */ +/*=*/ + +#ifdef FOR_DOXYGEN +/***en + @brief Invalid code-point. + + The macro #MCHAR_INVALID_CODE gives the invalid code-point. */ + +/***ja + @brief ̵¸ú¤Ê¥³¡¼¥É¥Ý¥¤¥ó¥È + + ¥Þ¥¯¥í #MCHAR_INVALID_CODE ¤Ï̵¸ú¤Ê¥³¡¼¥É¥Ý¥¤¥ó¥È¤òÍ¿¤¨¤ë¡£ */ + +#define MCHAR_INVALID_CODE +#endif +/*=*/ +/***en + @brief The symbol @c Mcharset. + + Any decoded M-text has a text property whose key is the predefined + symbol @c Mcharset. The name of @c Mcharset is + "charset". */ + +/***ja + @brief ¥·¥ó¥Ü¥ë @c Mcharset + + ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥­¡¼¤¬ @c Mcharset ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È + ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¥·¥ó¥Ü¥ë @c Mcharset ¤Ï "charset" ¤È¤¤ + ¤¦Ì¾Á°¤Ç¤¢¤é¤«¤¸¤áÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ */ + +MSymbol Mcharset; +/*=*/ + +/***en + @name Variables: Symbols representing a charset. + + Each of the following symbols represents a predefined charset. */ + +/***ja + @name ÊÑ¿ô: ʸ»ú¥»¥Ã¥È¤òɽ¤ï¤¹ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë + + °Ê²¼¤Î³Æ¥·¥ó¥Ü¥ë¤Ï¡¢¥­¡¼¤¬ @c Mcharset ¤Ç¤¢¤ê¡¢Ãͤ¬Âбþ¤¹¤ëʸ»ú¥»¥Ã + ¥È¥ª¥Ö¥¸¥§¥¯¥È¡Ê @c MCharset ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ + ¥Æ¥£¤ò»ý¤Ä¡£ */ +/*=*/ +/*** @{ */ +/*=*/ +/***en + @brief Symbol representing the charset ASCII. + + The symbol #Mcharset_ascii has name "ascii" and represents + the charset ISO 646, USA Version X3.4-1968 (ISO-IR-6). */ +/***ja + @brief ISO 646, USA Version ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mcharset_ascii ¤Ï "ascii" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + ISO 646, USA Version X3.4-1968 (ISO-IR-6) ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò»Ø + Äꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£ */ + +MSymbol Mcharset_ascii; + +/*=*/ +/***en + @brief Symbol representing the charset ISO/IEC 8859/1. + + The symbol #Mcharset_iso_8859_1 has name "iso-8859-1" + and represents the charset ISO/IEC 8859-1:1998. */ +/***ja + @brief ISO/IEC 8859-1:1998 ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mcharset_iso_8859_1 ¤Ï "iso-8859-1" ¤È¤¤¤¦Ì¾ + Á°¤ò»ý¤Á¡¢ISO/IEC 8859-1:1998 ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë + »È¤ï¤ì¤ë¡£ */ + +MSymbol Mcharset_iso_8859_1; + +/***en + @brief Symbol representing the charset Unicode. + + The symbol #Mcharset_unicode has name "unicode" and + represents the charset Unicode. */ +/***ja + @brief Unicode ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mcharset_unicode ¤Ï "unicode" ¤È¤¤¤¦Ì¾Á°¤ò»ý + ¤Á¡¢Unicode ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£ */ + +MSymbol Mcharset_unicode; + +/*=*/ +/***en + @brief Symbol representing the largest charset. + + The symbol #Mcharset_m17n has name "m17n" and + represents the charset that contains all characters supported by + the m17n library. */ +/***ja + @brief Á´Ê¸»ú¤ò´Þ¤àʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mcharset_m17n ¤Ï "m17n" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + m17n ¥é¥¤¥Ö¥é¥ê¤¬°·¤¦Á´¤Æ¤Îʸ»ú¤ò´Þ¤àʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»È + ¤ï¤ì¤ë¡£ */ + +MSymbol Mcharset_m17n; + +/*=*/ +/***en + @brief Symbol representing the charset for ill-decoded characters. + + The symbol #Mcharset_binary has name "binary" and + represents the fake charset which the decoding functions put to an + M-text as a text property when they encounter an invalid byte + (sequence). See @ref m17nConv @latexonly + (P.\pageref{group__m17nConv}) @endlatexonly for more detail. */ + +MSymbol Mcharset_binary; + +/*=*/ +/*** @} */ +/*=*/ + +/***en + @name Variables: Parameter keys for mchar_define_charset (). + + These are the predefined symbols to use as parameter keys for the + function mchar_define_charset () (which see). */ + +/***ja + @name ÊÑ¿ô: mchar_define_charset ÍѤΥѥé¥á¡¼¥¿¡¦¥­¡¼ + + ¤³¤ì¤é¤Ï¡¢´Ø¿ô mchar_define_charset () ÍѤΥѥé¥á¡¼¥¿¡¦¥­¡¼¤È¤·¤Æ + »È¤ï¤ì¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£ ¾Ü¤·¤¯¤Ï¤³¤Î´Ø¿ô¤Î²òÀâ¤ò»²¾È¤Î¤³¤È¡£*/ +/*** @{ */ +/*=*/ + +/***en + Parameter key for mchar_define_charset () (which see). */ + +MSymbol Mmethod; +MSymbol Mdimension; +MSymbol Mmin_range; +MSymbol Mmax_range; +MSymbol Mmin_code; +MSymbol Mmax_code; +MSymbol Mascii_compatible; +MSymbol Mfinal_byte; +MSymbol Mrevision; +MSymbol Mmin_char; +MSymbol Mmapfile; +MSymbol Mparents; +MSymbol Msubset_offset; +MSymbol Mdefine_coding; +MSymbol Maliases; +/*=*/ +/*** @} */ +/*=*/ + +/***en + @name Variables: Symbols representing charset methods. + + These are the predefined symbols that can be a value of the + #Mmethod parameter of a charset used in an argument to the + mchar_define_charset () function. + + A method specifies how code-points and character codes are + converted. See the documentation of the mchar_define_charset () + function for the details. */ + +/***ja + @name ÊÑ¿ô: ʸ»ú¥»¥Ã¥È¤Î¥á¥½¥Ã¥É»ØÄê¤Ë»È¤ï¤ì¤ë¥·¥ó¥Ü¥ë + + ¤³¤ì¤é¤Ï¡¢Ê¸»ú¥»¥Ã¥È¤Î @e ¥á¥½¥Ã¥É ¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢ + ´Ø¿ô mchar_define_charset () ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¥á¥½¥Ã¥É¤È¤Ï¡¢¥³¡¼¥É¥Ý¥¤¥ó¥È¤Èʸ»ú¥³¡¼¥É¤òÁê¸ßÊÑ´¹¤¹¤ëºÝ¤ÎÊý¼°¤Î¤³ + ¤È¤Ç¤¢¤ë¡£¾Ü¤·¤¯¤Ï´Ø¿ô mchar_define_charset () ¤Î²òÀâ¤ò»²¾È¤Î¤³¤È¡£ */ +/*** @{ */ +/*=*/ +/***en + @brief Symbol for the offset type method of charset. + + The symbol #Moffset has the name "offset" and, when used + as a value of #Mmethod parameter of a charset, it means that the + conversion of code-points and character codes of the charset is + done by this calculation: + +@verbatim +CHARACTER-CODE = CODE-POINT - MIN-CODE + MIN-CHAR +@endverbatim + + where, MIN-CODE is a value of #Mmin_code parameter of the charset, + and MIN-CHAR is a value of #Mmin_char parameter. */ + +/***ja + @brief ¥ª¥Õ¥»¥Ã¥È·¿¤Î¥á¥½¥Ã¥É¤ò¼¨¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Moffset ¤Ï "offset" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + mchar_define_charset () ¤Ç¥ª¥Õ¥»¥Ã¥È·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú + ¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£*/ + +MSymbol Moffset; +/*=*/ + +/***en @brief Symbol for the map type method of charset. + + The symbol #Mmap has the name "map" and, when use as a + value of #Mmethod parameter of a charset, it means that the + conversion of code-points and character codes of the charset is + done by map looking up. The map must be given by #Mmapfile + parameter. */ + +/***ja @brief ¥Þ¥Ã¥×·¿¤Î¥á¥½¥Ã¥É¤ò¼¨¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mmap ¤Ï "map" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + mchar_define_charset () ¤Ç¥Þ¥Ã¥×·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú¿ô¤È + ¤·¤ÆÍѤ¤¤é¤ì¤ë¡£*/ + +MSymbol Mmap; +/*=*/ + +/***en @brief Symbol for the unify type method of charset. + + The symbol #Munify has the name "unify" and, when used as + a value of #Mmethod parameter of a charset, it means that the + conversion of code-points and character codes of the charset is + done by map looking up and offsetting. The map must be given by + #Mmapfile parameter. For this kind of charset, a unique + consequent character code space for all characters is assigned. + If the map has an entry for a code-point, the conversion is done + by looking up the map. Otherwise, the conversion is done by this + calculation: + +@verbatim +CHARACTER-CODE = CODE-POINT - MIN-CODE + LOWEST-CHAR-CODE +@endverbatim + + where, MIN-CODE is a value of #Mmin_code parameter of the charset, + and LOWEST-CHAR-CODE is the lowest character code of the assigned + code space. */ + +/***ja @brief Áê³·¿¤Î¥á¥½¥Ã¥É¤ò¼¨¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Minherit ¤Ï "inherit" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + mchar_define_charset () ¤ÇÁê³·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú¿ô¤È¤· + ¤ÆÍѤ¤¤é¤ì¤ë¡£*/ + +MSymbol Munify; +/*=*/ + +/***en + @brief Symbol for the subset type method of charset. + + The symbol #Msubset has the name "subset" and, when used + as a value of #Mmethod parameter of a charset, it means that the + charset is a subset of a parent charset. The parent charset must + be given by #Mparents parameter. The conversion of code-points + and character codes of the charset is done conceptually by this + calculation: + +@verbatim +CHARACTER-CODE = PARENT-CODE (CODE-POINT) + SUBSET-OFFSET +@endverbatim + + where, PARENT-CODE is a pseudo function that returns a character + code of CODE-POINT in the parent charset, and SUBSET-OFFSET is a + value given by #Msubset_offset parameter. */ + +MSymbol Msubset; +/*=*/ + +/***en + @brief Symbol for the superset type method of charset. + + The symbol #Msuperset has the name "superset" and, when + used as a value of #Mmethod parameter of a charset, it means that + the charset is a superset of parent charsets. The parent charsets + must be given by #Mparents parameter. */ + +MSymbol Msuperset; +/*=*/ +/*** @} */ + +/***en + @brief Define a charset. + + The mchar_define_charset () function defines a new charset and + makes it accessible via a symbol whose name is $NAME. $PLIST + specifies parameters of the charset as below: + +
    + +
  • Key is #Mmethod, value is a symbol. + + The value specifies the method for decoding/encoding code-points + in the charset. It must be #Moffset, #Mmap (default), #Munify, + #Msubset, or #Msuperset. + +
  • Key is #Mdimension, value is an integer + + The value specifies the dimension of code-points of the charset. + It must be 1 (default), 2, 3, or 4. + +
  • Key is #Mmin_range, value is an unsigned integer + + The value specifies the minimum range of a code-point, which means + that the Nth byte of the value is the minimum Nth byte of + code-points of the charset. The default value is 0. + +
  • Key is #Mmax_range, value is an unsigned integer + + The value specifies the maximum range of a code-point, which means + that the Nth byte of the value is the maximum Nth byte of + code-points of the charset. The default value is 0xFF, 0xFFFF, + 0xFFFFFF, or 0xFFFFFFFF if the dimension is 1, 2, 3, or 4 + respectively. + +
  • Key is #Mmin_code, value is an unsigned integer + + The value specifies the minimum code-point of + the charset. The default value is the minimum range. + +
  • Key is #Mmax_code, value is an unsigned integer + + The value specifies the maximum code-point of + the charset. The default value is the maximum range. + +
  • Key is #Mascii_compatible, value is a symbol + + The value specifies whether the charset is ASCII compatible or + not. If the value is #Mnil (default), it is not ASCII + compatible, else compatible. + +
  • Key is #Mfinal_byte, value is an integer + + The value specifies the @e final @e byte of the charset registered + in The International Registry. It must be 0 (default) or 32..127. + The value 0 means that the charset is not in the registry. + +
  • Key is #Mrevision, value is an integer + + The value specifies the @e revision @e number of the charset + registered in The International Registry. it must be 0..127. If + the charset is not in The International Registry, the value is + ignored. The value 0 means that the charset has no revision + number. + +
  • Key is #Mmin_char, value is an integer + + The value specifies the minimum character code of the charset. + The default value is 0. + +
  • Key is #Mmapfile, value is an M-text + + If the method is #Mmap or #Munify, a data that contains + mapping information is added to the m17n database by calling + mdatabase_define () with the value as an argument $EXTRA_INFO, + i.e. the value is used as a file name of the data. + + Otherwise, this parameter is ignored. + +
  • Key is #Mparents, value is a plist + + If the method is #Msubset, the value must is a plist of length + 1, and the value of the plist must be a symbol representing a + parent charset. + + If the method is #Msuperset, the value must be a plist of length + less than 9, and the values of the plist must be symbols + representing subset charsets. + + Otherwise, this parameter is ignored. + +
  • Key is #Mdefine_coding, value is a symbol + + If the dimension of the charset is 1, the value specifies whether + or not to define a coding system of the same name whose method is + @c charset. + + Otherwise, this parameter is ignored. + +
+ + @return + If the operation was successful, mchar_define_charset () returns a + symbol whose name is $NAME. Otherwise it returns #Mnil and + assigns an error code to the external variable #merror_code. */ + +/***ja + @brief ʸ»ú¥»¥Ã¥È¤òÄêµÁ¤¹¤ë. + + ´Ø¿ô mchar_define_charset () ¤Ï¿·¤·¤¤Ê¸»ú¥»¥Ã¥È¤òÄêµÁ¤·¡¢¤½¤ì¤ò + $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü¥ë·Ðͳ¤Ç¥¢¥¯¥»¥¹¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¡£$METHOD + ¤Ï¤½¤Îʸ»ú¥»¥Ã¥È¤Ë¤ª¤±¤ë¥³¡¼¥É¥Ý¥¤¥ó¥È¤Î¥Ç¥³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥á¥½¥Ã + ¥É¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Moffset, #Mmap, #Munify, + #Msubset, #Msuperset ¤Î¤¤¤º¤ì¤«¤ÎÃͤò¤È¤ë¡£ + + $DIMENSION ¤Ï¤½¤Îʸ»ú¥»¥Ã¥È¤Î¥³¡¼¥É¥Ý¥¤¥ó¥È¤Î¼¡¸µ¤Ç¤¢¤ê¡¢1, 2, 3, + 4¤Î¤¤¤º¤ì¤«¤ÎÃͤò¤È¤ë¡£ + + $CODE_RANGE ¤ÏÂ礭¤µ¤¬8¥Ð¥¤¥È¤ÎÇÛÎó¤Ç¤¢¤ê¡¢ÄêµÁ¤µ¤ì¤ëʸ»ú¥»¥Ã¥È¤Î + ¥³¡¼¥É¥Ý¥¤¥ó¥È¶õ´Ö¤òɽ¤ï¤¹¡£Âè1¥Ð¥¤¥È¤ÈÂè2¥Ð¥¤¥È¤ÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó + ¥È¤ÎºÇ½é¤Î¼¡¸µ¤Ç¤ÎºÇ¾®¡¿ºÇÂç¥Ð¥¤¥È¤ÎÃͤǤ¢¤ë¡£Âè3¥Ð¥¤¥È¤ÈÂè4¥Ð¥¤¥È + ¤Ï¡¢2ÈÖÌܤμ¡¸µ¤ÎºÇ¾®¡¿ºÇÂçÃͤǤ¢¤ê¡¢ °Ê²¼Æ±Íͤ˳¤¯¡£°ìÈÌŪ¤Ë¡¢Âè + (2N-1)¥Ð¥¤¥È¤ÈÂè(2N)¥Ð¥¤¥È¤¬NÈÖÌܤμ¡¸µ¤ÎºÇ¾®¡¿ºÇÂçÃͤȤʤë (N = + 1, 2, 3, 4)¡£¥³¡¼¥É¥Ý¥¤¥ó¥È¤Î @e ʸ»ú¥¤¥ó¥Ç¥Ã¥¯¥¹ ¤Ï¤³¤ì¤é¤ÎÃͤ«¤é + ·×»»¤µ¤ì¤ë¡£ + + $MIN_CODE ¤È $MAX_CODE ¤Ï¡¢¤½¤ì¤¾¤ì¤³¤Îʸ»ú¥»¥Ã¥È¤ÎºÇ¾®¤ª¤è¤ÓºÇÂç + ¥³¡¼¥É¥Ý¥¤¥ó¥È¤òɽ¤ï¤¹¡£0¤¬»ØÄꤵ¤ì¤¿¾ì¹ç¤Ï $CODE_RANGE ¤ÎÃͤ«¤é·× + »»¤µ¤ì¤ë¡£ + + $FINAL_BYTE ¤Ï The International Registry ¤ËÅÐÏ¿¤µ¤ì¤Æ¤¤¤ë + @e ½ªÃ¼¥Ð¥¤¥È ¤Ç¤¢¤ë¡£ÅÐÏ¿¤µ¤ì¤Æ¤¤¤Ê¤¤ CCS ¤Î¾ì¹ç¤Ë¤Ï -1 ¤Ç¤Ê¤¯¤Æ¤Ï + ¤Ê¤é¤Ê¤¤¡£ + + $REVISION ¤Ï¡¢The International Registry ¤ËÅÐÏ¿¤µ¤ì¤Æ¤¤¤ë@e revision + @e number ¤Ç¤¢¤ë¡£¤â¤· revision number ¤¬Â¸ºß¤·¤Ê¤¤¤Ê¤é -1 ¤Ç¤Ê¤¯ + ¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + @par ¥á¥½¥Ã¥É¤¬ Moffset ¤Î¾ì¹ç + + $MIN_CHAR ¤Ë¤ÏºÇ¾®¤Î¥³¡¼¥É¥Ý¥¤¥ó¥È¤ËÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤òÍ¿¤¨¤ë¡£ + $NPARENTS, $PARENTS, ¤ª¤è¤Ó $SUBSET_OFFSET ¤Ï̵»ë¤µ¤ì¤ë¡£ + + @par ¥á¥½¥Ã¥É¤¬ Mmap ¤Î¾ì¹ç + + m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç \<#Mcharset, $NAME\> ¤È¤¤¤¦¥¿¥°¤ÎÉÕ¤¤¤¿ + ¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö¥ë¤ò¡¢¥Ç¥³¡¼¥É¤ª¤è¤Ó¥¨¥ó¥³¡¼¥É¤ËÍѤ¤¤ë¡£$MIN_CHAR, + $NPARENTS, $PARENTS, ¤ª¤è¤Ó $SUBSET_OFFSET ¤Ï̵»ë¤µ¤ì¤ë¡£ + + @par ¥á¥½¥Ã¥É¤¬ Msubset ¤Î¾ì¹ç + + $NPARENTS ¤Ï1¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤Þ¤¿ $PARENTS ¤Ï·Ñ¾µ¤Î¸µ¤È¤Ê¤ëʸ + »ú¥»¥Ã¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¸µ¤Îʸ»ú¥»¥Ã¥È¤Î¥³¡¼¥É + ¥Ý¥¤¥ó¥È¤Ë $SUBSET_OFFSET ¤ò²Ã¤¨¤¿¤â¤Î¤¬¡¢¿·¤·¤¤Ê¸»ú¥»¥Ã¥ÈÃæ¤Ç¤Î¥³¡¼ + ¥É¥Ý¥¤¥ó¥È¤Ë¤Ê¤ë¡£$MIN_CHAR ¤Ï̵»ë¤µ¤ì¤ë¡£ + + @par ¥á¥½¥Ã¥É¤¬ Msuperset ¤Î¾ì¹ç + + $NPARENTS ¤Ï¿Æ¤È¤Ê¤ëʸ»ú¥»¥Ã¥È¤Î¿ô¡¢$PARENTS ¤Ï¿Æʸ»ú¥»¥Ã¥È¤Î¥·¥ó + ¥Ü¥ë¤ÎÇÛÎó¤òɽ¤ï¤¹¡£$MIN_CHAR ¤ª¤è¤Ó $SUBSET_OFFSET ¤Ï̵»ë¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mchar_define_charset () ¤Ï $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥· + ¥ó¥Ü¥ë¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð #Mnil ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c + merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CHARSET */ + +MSymbol +mchar_define_charset (char *name, MPlist *plist) +{ + MSymbol sym = msymbol (name); + MCharset *charset; + int i; + unsigned min_range, max_range; + MPlist *pl; + MText *mapfile = (MText *) mplist_get (plist, Mmapfile); + + MSTRUCT_CALLOC (charset, MERROR_CHARSET); + charset->name = sym; + charset->method = (MSymbol) mplist_get (plist, Mmethod); + if (! charset->method) + { + if (mapfile) + charset->method = Mmap; + else + charset->method = Moffset; + } + if (charset->method == Mmap || charset->method == Munify) + { + if (! mapfile) + MERROR (MERROR_CHARSET, Mnil); + mdatabase_define (Mcharset, sym, Mnil, Mnil, NULL, mapfile->data); + } + if (! (charset->dimension = (int) mplist_get (plist, Mdimension))) + charset->dimension = 1; + + min_range = (unsigned) mplist_get (plist, Mmin_range); + if ((pl = mplist_find_by_key (plist, Mmax_range))) + { + max_range = (unsigned) MPLIST_VAL (pl); + if (max_range >= 0x1000000) + charset->dimension = 4; + else if (max_range >= 0x10000 && charset->dimension < 3) + charset->dimension = 3; + else if (max_range >= 0x100 && charset->dimension < 2) + charset->dimension = 2; + } + else if (charset->dimension == 1) + max_range = 0xFF; + else if (charset->dimension == 2) + max_range = 0xFFFF; + else if (charset->dimension == 3) + max_range = 0xFFFFFF; + else + max_range = 0xFFFFFFFF; + + memset (charset->code_range, 0, sizeof charset->code_range); + for (i = 0; i < charset->dimension; i++, min_range >>= 8, max_range >>= 8) + { + charset->code_range[i * 4] = min_range & 0xFF; + charset->code_range[i * 4 + 1] = max_range & 0xFF; + } + if ((charset->min_code = (int) mplist_get (plist, Mmin_code)) < min_range) + charset->min_code = min_range; + if ((charset->max_code = (int) mplist_get (plist, Mmax_code)) > max_range) + charset->max_code = max_range; + charset->ascii_compatible + = (MSymbol) mplist_get (plist, Mascii_compatible) != Mnil; + charset->final_byte = (int) mplist_get (plist, Mfinal_byte); + charset->revision = (int) mplist_get (plist, Mrevision); + charset->min_char = (int) mplist_get (plist, Mmin_char); + pl = (MPlist *) mplist_get (plist, Mparents); + charset->nparents = pl ? mplist_length (pl) : 0; + if (charset->nparents > 8) + charset->nparents = 8; + for (i = 0; i < charset->nparents; i++, pl = MPLIST_NEXT (pl)) + { + MSymbol parent_name; + + if (MPLIST_KEY (pl) != Msymbol) + MERROR (MERROR_CHARSET, Mnil); + parent_name = MPLIST_SYMBOL (pl); + if (! (charset->parents[i] = MCHARSET (parent_name))) + MERROR (MERROR_CHARSET, Mnil); + } + + charset->subset_offset = (int) mplist_get (plist, Msubset_offset); + + msymbol_put (sym, Mcharset, charset); + charset = make_charset (charset); + if (! charset) + return Mnil; + msymbol_put (msymbol__canonicalize (sym), Mcharset, charset); + + for (pl = (MPlist *) mplist_get (plist, Maliases); + pl && MPLIST_KEY (pl) == Msymbol; + pl = MPLIST_NEXT (pl)) + { + MSymbol alias = MPLIST_SYMBOL (pl); + + msymbol_put (alias, Mcharset, charset); + msymbol_put (msymbol__canonicalize (alias), Mcharset, charset); + } + + if (mplist_get (plist, Mdefine_coding) + && charset->dimension == 1 + && charset->code_range[0] == 0 && charset->code_range[1] == 255) + mconv__register_charset_coding (sym); + return (sym); +} + +/*=*/ + +/***en + @brief Resolve charset name. + + The mchar_resolve_charset () function returns $SYMBOL if it + represents a charset. Otherwise, canonicalize $SYMBOL as to a + charset name, and if the canonicalized name represents a charset, + return it. Otherwise, return #Mnil. */ + +MSymbol +mchar_resolve_charset (MSymbol symbol) +{ + MCharset *charset = (MCharset *) msymbol_get (symbol, Mcharset); + + if (! charset) + { + symbol = msymbol__canonicalize (symbol); + charset = (MCharset *) msymbol_get (symbol, Mcharset); + } + + return (charset ? charset->name : Mnil); +} + +/*=*/ + +/***en + @brief List symbols representing a charset. + + The mchar_list_charsets () function makes an array of symbols + representing a charset, stores the pointer to the array in a place + pointed to by $SYMBOLS, and returns the length of the array. */ + +int +mchar_list_charset (MSymbol **symbols) +{ + int i; + + MTABLE_MALLOC ((*symbols), charset_list.used, MERROR_CHARSET); + for (i = 0; i < charset_list.used; i++) + (*symbols)[i] = charset_list.charsets[i]->name; + return i; +} + +/*=*/ + +/***en + @brief Decode a code-point. + + The mchar_decode () function decodes code-point $CODE in the + charset represented by the symbol $CHARSET_NAME to get a character + code. + + @return + If decoding was successful, mchar_decode () returns the decoded + character code. Otherwise it returns -1. */ + +/***ja + @brief ¥³¡¼¥É¥Ý¥¤¥ó¥È¤ò¥Ç¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mchar_decode () ¤Ï¡¢¥·¥ó¥Ü¥ë $CHARSET_NAME ¤Ç¼¨¤µ¤ì¤ëʸ»ú¥»¥Ã + ¥ÈÆâ¤Î $CODE ¤È¤¤¤¦¥³¡¼¥É¥Ý¥¤¥ó¥È¤ò¥Ç¥³¡¼¥É¤·¤Æʸ»ú¥³¡¼¥É¤òÆÀ¤ë¡£ + + @return + ¥Ç¥³¡¼¥É¤¬À®¸ù¤¹¤ì¤Ð¡¢mchar_decode () ¤Ï¥Ç¥³¡¼¥É¤µ¤ì¤¿Ê¸»ú¥³¡¼¥É¤ò + ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£ */ + +/*** + @seealso + mchar_encode () */ + +int +mchar_decode (MSymbol charset_name, unsigned code) +{ + MCharset *charset = MCHARSET (charset_name); + + if (! charset) + return MCHAR_INVALID_CODE; + return DECODE_CHAR (charset, code); +} + +/*=*/ + +/***en + @brief Encode a character code. + + The mchar_encode () function encodes character code $C to get a + code-point in the charset represented by the symbol $CHARSET_NAME. + + @return + If encoding was successful, mchar_encode () returns the encoded + code-point. Otherwise it returns #MCHAR_INVALID_CODE. */ + +/***ja + @brief ʸ»ú¥³¡¼¥É¤ò¥¨¥ó¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mchar_encode () ¤Ï¡¢Ê¸»ú¥³¡¼¥É $C ¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¥·¥ó¥Ü¥ë + $CHARSET_NAME ¤Ç¼¨¤µ¤ì¤ëʸ»ú¥»¥Ã¥ÈÆâ¤Ë¤ª¤±¤ë¥³¡¼¥É¥Ý¥¤¥ó¥È¤òÆÀ¤ë¡£ + + @return + ¥¨¥ó¥³¡¼¥É¤¬À®¸ù¤¹¤ì¤Ð¡¢mchar_encode () ¤Ï¥¨¥ó¡¼¥É¤µ¤ì¤¿¥³¡¼¥É¥Ý¥¤ + ¥ó¥È¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð #MCHAR_INVALID_CODE ¤òÊÖ¤¹¡£ */ + +/*** + @seealso + mchar_decode () */ + +unsigned +mchar_encode (MSymbol charset_name, int c) +{ + MCharset *charset = MCHARSET (charset_name); + + if (! charset) + return MCHAR_INVALID_CODE; + return ENCODE_CHAR (charset, c); +} + +/*=*/ + +/***en + @brief Call a function for all the characters in a specified charset. + + The mcharset_map_chars () function calls $FUNC for all the + characters in the charset named $CHARSET_NAME. A call is done for + a chunk of consecutive characters rather than character by + character. + + $FUNC receives three arguments: $FROM, $TO, and $ARG. $FROM and + $TO specify the range of character codes in $CHARSET. $ARG is the + same as $FUNC_ARG. + + @return + If the operation was successful, mcharset_map_chars () returns 0. + Otherwise, it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief »ØÄꤷ¤¿Ê¸»ú¥»¥Ã¥È¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤Æ´Ø¿ô¤ò¸Æ¤Ö + + ´Ø¿ô mcharset_map_chars () ¤Ï $CHARSET_NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Äʸ»ú¥»¥Ã + ¥ÈÃæ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤Æ $FUNC ¤ò¸Æ¤Ö¡£¸Æ¤Ó½Ð¤·¤Ï°ìʸ»úËè¤Ç¤Ï¤Ê + ¤¯¡¢Ï¢Â³¤·¤¿Ê¸»ú¤Î¤Þ¤È¤Þ¤êñ°Ì¤Ç¹Ô¤Ê¤ï¤ì¤ë¡£ + + ´Ø¿ô $FUNC ¤Ë¤Ï$FROM, $TO, $ARG ¤Î£³°ú¿ô¤¬ÅϤµ¤ì¤ë¡£$FROM ¤È $TO + ¤Ï $CHARSET Ãæ¤Îʸ»ú¥³¡¼¥É¤ÎÈϰϤò»ØÄꤹ¤ë¡£$ARG ¤Ï $FUNC_ARG ¤ÈƱ + ¤¸¤Ç¤¢¤ë¡£ + + @return + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mcharset_map_chars () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð + -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CHARSET */ + +int +mchar_map_charset (MSymbol charset_name, + void (*func) (int from, int to, void *arg), + void *func_arg) +{ + MCharset *charset; + + charset = MCHARSET (charset_name); + if (! charset) + MERROR (MERROR_CHARSET, -1); + + if (charset->encoder) + { + int c = charset->min_char; + int next_c; + + if ((int) mchartable__lookup (charset->encoder, c, &next_c, 1) < 0) + c = next_c; + while (c <= charset->max_char) + { + if ((int) mchartable__lookup (charset->encoder, c, &next_c, 1) >= 0) + (*func) (c, next_c - 1, func_arg); + c = next_c; + } + } + else + (*func) (charset->min_char, charset->max_char, func_arg); + return 0; +} + +/*=*/ + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/charset.h b/src/charset.h new file mode 100644 index 0000000..5b9e430 --- /dev/null +++ b/src/charset.h @@ -0,0 +1,253 @@ +/* charset.h -- header file for the charset module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_CHARSET_H_ +#define _M17N_CHARSET_H_ + +/** @file charset.h + @brief Header for charset handlers. +*/ + +enum mcharset_method + { + MCHARSET_METHOD_OFFSET, + MCHARSET_METHOD_MAP, + MCHARSET_METHOD_DEFERRED, + MCHARSET_METHOD_SUBSET, + MCHARSET_METHOD_SUPERSET, + MCHARSET_METHOD_MAX + }; + +/** Structure for charset. */ + +typedef struct MCharset MCharset; + +struct MCharset +{ + /** The value is always 0 because all charsets are static. */ + unsigned ref_count; + + /** Symbol indicating the name of the charset. */ + MSymbol name; + + /** Number of dimensions of the charset. It must be 1, 2, 3, or + 4. */ + int dimension; + + /** Byte code range of each dimension. [4N] is a + minimum byte code of the (N+1)th dimension, [4N+1] + is a maximum byte code of the (N+1)th dimension, + [4N+2] is ([4N+1] - [4N] + + 1), [4N+3] is a number of characters contained in the + first to (N+1)th dimensions. We get "char-index" of a + "code-point" from this information. */ + int code_range[16]; + + /** The minimum code-point calculated from . It may be + smaller than . */ + int code_range_min_code; + + /** Nonzero means there is no gap in code points of the charset. If + is 1, is always 1. Otherwise, + is 1 iff [4N] is zero and + [4N+1] is 256 for N = 0..-2. If + is nonzero, "char-index" is "code-point" - + . */ + int no_code_gap; + + /** If the byte code B is valid in the (N+1)th dimension, + ([B] & (1 << N)) is 1. Otherwise, + ([B] & (1 << N)) is 0. */ + unsigned char code_range_mask[256]; + + /** Minimum and maximum code-point of the charset. */ + unsigned min_code, max_code; + + /** Nonzero means the charset encodes ASCII characters as is. */ + int ascii_compatible; + + /** Minimum and maximum character of the charset. If + is nonzero, is actually the + minimum non-ASCII character of the charset. */ + int min_char, max_char; + + /** ISO 2022 final byte of the charset. It must be in the range + 48..127, or -1. The value -1 means that the charset is not + encodable by ISO 2022 based coding systems. */ + int final_byte; + + /** ISO 2022 revision number of the charset, or -1. The value -1 + means that the charset has no revision number. Used only when + is not -1. */ + int revision; + + /** Specify how to encode/decode code-point of the charset. It must + be Moffset, Mmap, Munify, Msubset, or Msuperset. */ + MSymbol method; + + /** Array of integers to decode a code-point of the charset. It is + indexed by a "char-index" of the code-point, and the + corresponding element is a character of the charset, or -1 if + the code point is not valid in the charset. Used only when + is Mmap or Munify. */ + int *decoder; + + /** Char-table to encode a character of the charset. It is indexed + by a character code, and the corresponding element is a code + point of the character in the charset, or + MCHAR_INVALID_CODE if the character is not included in the + charset. Used only when is Mmap or Munify. */ + MCharTable *encoder; + + int unified_max; + + /** Array of pointers to parent charsets. Used only when + is Msubset or Msuperset. Atmost 8 parents are supported. */ + MCharset *parents[8]; + + /* Number of parent charsets. */ + int nparents; + + unsigned subset_min_code, subset_max_code; + int subset_offset; + + int simple; + + /** If the charset is fully loaded (i.e. all the above member are + set to correct values), the value is 1. Otherwise, the value is + 0. */ + int fully_loaded; +}; + +extern MPlist *mcharset__cache; + +/** Return a charset associated with the symbol CHARSET_SYM. */ + +#define MCHARSET(charset_sym) \ + (((charset_sym) == MPLIST_KEY (mcharset__cache) \ + || (MPLIST_KEY (mcharset__cache) = (charset_sym), \ + MPLIST_VAL (mcharset__cache) \ + = (MCharset *) msymbol_get ((charset_sym), Mcharset))) \ + ? MPLIST_VAL (mcharset__cache) \ + : mcharset__find (charset_sym)) + + +/** Return index of a character whose code-point in CHARSET is CODE. + If CODE is not valid, return -1. */ + +#define CODE_POINT_TO_INDEX(charset, code) \ + ((charset)->no_code_gap \ + ? (code) - (charset)->min_code \ + : (((charset)->code_range_mask[(code) >> 24] & 0x8) \ + && ((charset)->code_range_mask[((code) >> 16) & 0xFF] & 0x4) \ + && ((charset)->code_range_mask[((code) >> 8) & 0xFF] & 0x2) \ + && ((charset)->code_range_mask[(code) & 0xFF] & 0x1)) \ + ? (((((code) >> 24) - (charset)->code_range[12]) \ + * (charset)->code_range[11]) \ + + (((((code) >> 16) & 0xFF) - (charset)->code_range[8]) \ + * (charset)->code_range[7]) \ + + (((((code) >> 8) & 0xFF) - (charset)->code_range[4]) \ + * (charset)->code_range[3]) \ + + (((code) & 0xFF) - (charset)->code_range[0]) \ + - ((charset)->min_code - (charset)->code_range_min_code)) \ + : -1) + + +/* Return code-point of a character whose index is IDX. + The validness of IDX is not checked. IDX may be modified. */ + +#define INDEX_TO_CODE_POINT(charset, idx) \ + ((charset)->no_code_gap \ + ? (idx) + (charset)->min_code \ + : (idx += (charset)->min_code - (charset)->code_range_min_code, \ + (((charset)->code_range[0] + (idx) % (charset)->code_range[2]) \ + | (((charset)->code_range[4] \ + + ((idx) / (charset)->code_range[3] % (charset)->code_range[6])) \ + << 8) \ + | (((charset)->code_range[8] \ + + ((idx) / (charset)->code_range[7] % (charset)->code_range[10])) \ + << 16) \ + | (((charset)->code_range[12] + ((idx) / (charset)->code_range[11])) \ + << 24)))) + + +/** Return a character whose code-point in CHARSET is CODE. If CODE + is invalid, return -1. */ + +#define DECODE_CHAR(charset, code) \ + (((code) < 128 && (charset)->ascii_compatible) \ + ? (int) (code) \ + : ((code) < (charset)->min_code || (code) > (charset)->max_code) \ + ? -1 \ + : ! (charset)->simple \ + ? mcharset__decode_char ((charset), (code)) \ + : (charset)->method == Moffset \ + ? (code) - (charset)->min_code + (charset)->min_char \ + : (charset)->decoder[(code) - (charset)->min_code]) + + +/** Return a code-point in CHARSET for character C. If CHARSET + does not contain C, return MCHAR_INVALID_CODE. */ + +#define ENCODE_CHAR(charset, c) \ + (! (charset)->simple \ + ? mcharset__encode_char ((charset), (c)) \ + : ((c) < (charset)->min_char || (c) > (charset)->max_char) \ + ? MCHAR_INVALID_CODE \ + : (charset)->method == Moffset \ + ? (c) - (charset)->min_char + (charset)->min_code \ + : (unsigned) mchartable_lookup ((charset)->encoder, (c))) + + +extern MCharset *mcharset__ascii; +extern MCharset *mcharset__binary; +extern MCharset *mcharset__m17n; +extern MCharset *mcharset__unicode; + +#define ISO_MAX_DIMENSION 3 +#define ISO_MAX_CHARS 2 +#define ISO_MAX_FINAL 0x80 /* only 0x30..0xFF are used */ + +typedef struct +{ + /* Table of ISO-2022 charsets. */ + int size, inc, used; + MCharset **charsets; + + /** A 3-dimensional table indexed by "dimension", "chars", and + "final byte" of an ISO-2022 charset to get the correponding + charset. A charset that has a revision number is not stored in + this table. */ + MCharset *classified[ISO_MAX_DIMENSION][ISO_MAX_CHARS][ISO_MAX_FINAL]; +} MCharsetISO2022Table; + +extern MCharsetISO2022Table mcharset__iso_2022_table; + +#define MCHARSET_ISO_2022(dim, chars, final) \ + mcharset__iso_2022_table.classified[(dim) - 1][(chars) == 96][(final)] + +extern MCharset *mcharset__find (MSymbol name); +extern int mcharset__decode_char (MCharset *charset, unsigned code); +extern unsigned mcharset__encode_char (MCharset *charset, int c); +extern int mcharset__load_from_database (); + +#endif /* _M17N_CHARSET_H_ */ diff --git a/src/chartab.c b/src/chartab.c new file mode 100644 index 0000000..a48f342 --- /dev/null +++ b/src/chartab.c @@ -0,0 +1,970 @@ +/* chartab.h -- character table module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nChartable + @brief Chartable objects and API for them. + + The m17n library supports enormous number of characters. Thus, if + attributes of each character are to be stored in a simple array, + such an array would be impractically big. The attributes usually + used, however, are often assigned only to a range of characters. + Even when all characters have attributes, characters of + consecutive character code tend to have the same attribute values. + + The m17n library utilizes this tendency to store characters and + their attribute values efficiently in an object called @e + Chartable. Although a chartable object is not a simple array, + application programs can handle chartables as if they were arrays. + Attribute values of a character can be obtained by accessing a + Chartable for the attribute with the character code of the + specified character. + + A chartable is a managed object. */ + +/***ja + @addtogroup m17nChartable ʸ»ú¥Æ¡¼¥Ö¥ë + + @brief ʸ»ú¥Æ¡¼¥Ö¥ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤¬°·¤¦Ê¸»ú¤Î¶õ´Ö¤Ï¹­Âç¤Ç¤¢¤ë¤¿¤á¡¢Ê¸»úËè¤Î¾ðÊó¤òñ + ½ã¤ÊÇÛÎó¤Ë³ÊǼ¤·¤è¤¦¤È¤¹¤ë¤È¡¢¤½¤ÎÇÛÎó¤ÏµðÂç¤Ë¤Ê¤ê¤¹¤®¡¢Èó¼ÂÍÑŪ¤Ç + ¤¢¤ë¡£¤·¤«¤·Ä̾ïɬÍפȤʤëʸ»ú¤Ë¤Ä¤¤¤Æ¤Î¾ðÊó¤Ï¡¢¤¢¤ëÆÃÄê¤ÎÈϰϤÎʸ + »ú¤Ë¤Î¤ßÉÕ¤¤¤Æ¤¤¤ë¤³¤È¤¬Â¿¤¤¡£Á´Ê¸»ú¤Ë´Ø¤·¤Æ¾ðÊ󤬤¢¤ë¾ì¹ç¤Ë¤â¡¢Ï¢ + ³¤·¤¿Ê¸»ú¥³¡¼¥É¤ò»ý¤Äʸ»ú¤ÏƱ¤¸¾ðÊó¤ò»ý¤Ä¤³¤È¤¬Â¿¤¤¡£¤½¤³¤Ç¡¢¤³¤Î + ¤è¤¦¤Ê¾õ¶·¤Ë¤ª¤¤¤Æ¸úΨŪ¤Ë¾ðÊó¤ò³ÊǼ¤Ç¤­¤ë¤â¤Î¤È¤·¤Æ¡¢m17n ¥é¥¤¥Ö + ¥é¥ê¤Ï @e ʸ»ú¥Æ¡¼¥Ö¥ë (chartable) ¤È¸Æ¤Ö¥ª¥Ö¥¸¥§¥¯¥È¤òÍѤ¤¤ë¡£¥¢ + ¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢Ê¸»ú¥Æ¡¼¥Ö¥ë¤òÇÛÎó¤Î°ì¼ï¤È¸«¤Ê¤¹¤³¤È¤¬ + ¤Ç¤­¤ë¡£¤¢¤ëʸ»ú¤Ë¤Ä¤¤¤Æ¤ÎÆÃÄê¤Î¾ðÊó¤Ï¡¢¤½¤Î¾ðÊó¤ò»ý¤Äʸ»ú¥Æ¡¼¥Ö¥ë + ¤ò¤½¤Îʸ»ú¤Î¥³¡¼¥É¤Ç°ú¤¯¤³¤È¤ÇÆÀ¤é¤ì¤ë¡£ */ + +/*=*/ +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" + +static M17NObjectArray chartable_table; + +/*** Maximum depth of char-table. */ +#define CHAR_TAB_MAX_DEPTH 3 + +/** @name Define: Number of characters covered by char-table of each level. + @{ */ + +/** BITs for number of characters covered by char-table of each + level. */ +#if MCHAR_MAX < 0x400000 + +#define SUB_BITS_0 22 /* i.e. 0x400000 chars */ +#define SUB_BITS_1 16 /* i.e. 0x10000 chars */ +#define SUB_BITS_2 12 /* i.e. 0x1000 chars */ +#define SUB_BITS_3 7 /* i.e. 0x80 chars */ + +#else /* MCHAR_MAX >= 0x400000 */ + +#define SUB_BITS_0 31 +#define SUB_BITS_1 24 +#define SUB_BITS_2 16 +#define SUB_BITS_3 8 +#endif + +/** @} */ + +/** How many characters a char-table covers at each level. */ +static const int chartab_chars[] = + { (1 << SUB_BITS_0), + (1 << SUB_BITS_1), + (1 << SUB_BITS_2), + (1 << SUB_BITS_3) }; + +/** How many slots a char-table has at each level. */ +static const int chartab_slots[] = + { (1 << (SUB_BITS_0 - SUB_BITS_1)), + (1 << (SUB_BITS_1 - SUB_BITS_2)), + (1 << (SUB_BITS_2 - SUB_BITS_3)), + (1 << SUB_BITS_3) }; + +/** Mask bits to obtain the valid bits from a character code for looking + up a char-table of each level. */ +static const int chartab_mask[] = + { (int) ((((unsigned) 1) << SUB_BITS_0) - 1), + (1 << SUB_BITS_1) - 1, + (1 << SUB_BITS_2) - 1, + (1 << SUB_BITS_3) - 1 }; + +/** Bit-shifting counts to obtain a valid index from a character code + for looking up a char-table of each level. */ +static const int chartab_shift[] = + { SUB_BITS_1, SUB_BITS_2, SUB_BITS_3, 0 }; + + +/** Index for looking up character C in a char-table at DEPTH. */ +#define SUB_IDX(depth, c) \ + (((c) & chartab_mask[depth]) >> chartab_shift[depth]) + + +/** Structure of sub char-table. */ +typedef struct MSubCharTable MSubCharTable; + +struct MSubCharTable +{ +#if SUB_BITS_0 > 24 + + /* The depth of the table; 0, 1, 2, or 3. */ + int depth; + + /* The minimum character covered by the table. */ + int min_char; + +#else /* SUB_BITS_0 <= 24 */ + + /* The value is (( << 24) | ). */ + int depth_min_char; + +#endif /* SUB_BITS_0 <= 24 */ + + /** The default value of characters covered by the table. */ + void *default_value; + + /** For a table of bottom level, array of values. For a non-bottom + table, array of sub char-tables. It may be NULL if all + characters covered by the table has . */ + union { + void **values; + MSubCharTable *tables; + } contents; +}; + +#if SUB_BITS_0 > 24 +#define TABLE_DEPTH(table) ((table)->depth) +#define TABLE_MIN_CHAR(table) ((table)->min_char) +#define SET_DEPTH_MIN_CHAR(table, DEPTH, MIN_CHAR) \ + ((table)->depth = (DEPTH), (table)->min_char = (MIN_CHAR)) +#else /* SUB_BITS_0 <= 24 */ +#define TABLE_DEPTH(table) ((table)->depth_min_char >> 24) +#define TABLE_MIN_CHAR(table) ((table)->depth_min_char & 0xFFFFFF) +#define SET_DEPTH_MIN_CHAR(table, DEPTH, MIN_CHAR) \ + ((table)->depth_min_char = ((DEPTH) << 24) | (MIN_CHAR)) +#endif /* SUB_BITS_0 <= 24 */ + +/** Structure of char-table. */ + +struct MCharTable +{ + /** Common header for a managed object. */ + M17NObject control; + + /** Key of the table. */ + MSymbol key; + + /** The minimum and maximum characters covered by the table. */ + int min_char, max_char; + + MSubCharTable subtable; +}; + + + + +/* Local functions. */ + +/** Allocate and initialize an array of sub-tables for sub char-table + TABLE. It is assumed that TABLE_DEPTH (TABLE) < + CHAR_TAB_MAX_DEPTH.*/ + +static void +make_sub_tables (MSubCharTable *table, int managedp) +{ + int depth = TABLE_DEPTH (table); + int min_char = TABLE_MIN_CHAR (table); + int slots = chartab_slots[depth]; + int chars = chartab_chars[depth + 1]; + MSubCharTable *tables; + int i; + + MTABLE_MALLOC (tables, slots, MERROR_CHARTABLE); + + for (i = 0; i < slots; i++, min_char += chars) + { + SET_DEPTH_MIN_CHAR (tables + i, depth + 1, min_char); + tables[i].default_value = table->default_value; + tables[i].contents.tables = NULL; + } + if (managedp && table->default_value) + M17N_OBJECT_REF_NTIMES (tables->default_value, slots); + table->contents.tables = tables; +} + + +/** Allocate and initialize an array of values for sub char-table + TABLE. It is assumed that TABLE_DEPTH (TABLE) == + CHAR_TAB_MAX_DEPTH. */ + +static void +make_sub_values (MSubCharTable *table, int managedp) +{ + int slots = chartab_slots[CHAR_TAB_MAX_DEPTH]; + void **values; + int i; + + MTABLE_MALLOC (values, slots, MERROR_CHARTABLE); + + for (i = 0; i < slots; i++) + values[i] = table->default_value; + if (managedp && table->default_value) + M17N_OBJECT_REF_NTIMES (table->default_value, slots); + table->contents.values = values; +} + + +/** Free contents of sub char-table TABLE and the default value of + TABLE. Free also the sub-tables recursively. */ + +static void +free_sub_tables (MSubCharTable *table, int managedp) +{ + int depth = TABLE_DEPTH (table); + int slots = chartab_slots[depth]; + + if (table->contents.tables) + { + if (depth < CHAR_TAB_MAX_DEPTH) + { + while (slots--) + free_sub_tables (table->contents.tables + slots, managedp); + free (table->contents.tables); + } + else + { + if (managedp) + while (slots--) + { + if (table->contents.values[slots]) + M17N_OBJECT_UNREF (table->contents.values[slots]); + } + free (table->contents.values); + } + table->contents.tables = NULL; + } + if (managedp && table->default_value) + M17N_OBJECT_UNREF (table->default_value); +} + + +/** In sub char-table TABLE, set value VAL for characters of the range + FROM and TO. */ + +static void +set_chartable_range (MSubCharTable *table, int from, int to, void *val, + int managedp) +{ + int depth = TABLE_DEPTH (table); + int min_char = TABLE_MIN_CHAR (table); + int max_char = min_char + (chartab_chars[depth] - 1); + int i; + + if (max_char < 0 || max_char > MCHAR_MAX) + max_char = MCHAR_MAX; + + if (from < min_char) + from = min_char; + if (to > max_char) + to = max_char; + + if (from == min_char && to == max_char) + { + free_sub_tables (table, managedp); + if (managedp && val) + M17N_OBJECT_REF (val); + table->default_value = val; + return; + } + + if (depth < CHAR_TAB_MAX_DEPTH) + { + if (! table->contents.tables) + make_sub_tables (table, managedp); + i = SUB_IDX (depth, from); + table = table->contents.tables + i; + while (i < chartab_slots[depth] && TABLE_MIN_CHAR (table) <= to) + { + set_chartable_range (table, from, to, val, managedp); + table++, i++; + } + } + else + { + int idx_from = SUB_IDX (depth, from); + int idx_to = SUB_IDX (depth, to); + + if (! table->contents.values) + make_sub_values (table, managedp); + for (i = idx_from; i <= idx_to; i++) + { + if (managedp && table->contents.values[i]) + M17N_OBJECT_UNREF (table->contents.values[i]); + table->contents.values[i] = val; + } + if (managedp && val) + M17N_OBJECT_REF_NTIMES (val, (idx_to - idx_from + 1)); + } +} + + +/** Lookup the sub char-table TABLE for the character C. If NEXT_C is + not NULL, set *NEXT_C to the next interesting character to lookup + for. If DEFAULT_P is zero, the next interesting character is what + possibly has the different value than C. Otherwise, the next + interesting character is what possibly has the default value (if C + has a value deferent from the default value) or has a value + different from the default value (if C has the default value). */ + +static void * +lookup_chartable (MSubCharTable *table, int c, int *next_c, int default_p) +{ + int depth = TABLE_DEPTH (table); + void *val; + void *default_value = table->default_value; + int idx; + + while (1) + { + if (! table->contents.tables) + { + if (next_c) + *next_c = TABLE_MIN_CHAR (table) + chartab_chars[depth]; + return table->default_value; + } + if (depth == CHAR_TAB_MAX_DEPTH) + break; + table = table->contents.tables + SUB_IDX (depth, c); + depth++; + } + + idx = SUB_IDX (depth, c); + val = table->contents.values[idx]; + + if (next_c) + { + int max_char = TABLE_MIN_CHAR (table) + (chartab_chars[depth] - 1); + + if (max_char < 0 || max_char > MCHAR_MAX) + max_char = MCHAR_MAX; + if (default_p && val != default_value) + { + do { c++, idx++; } + while (c >= 0 && c <= max_char + && table->contents.values[idx] != default_value); + } + else + { + do { c++, idx++; } + while (c >= 0 && c <= max_char + && table->contents.values[idx] == val); + } + *next_c = c; + } + return val; +} + +/** Call FUNC for characters in sub char-table TABLE. Ignore such + characters that has a value IGNORE. FUNC is called with four + arguments; FROM, TO, VAL, and ARG (same as FUNC_ARG). If + DEFAULT_P is zero, FROM and TO are range of characters that has + the same value VAL. Otherwise, FROM and TO are range of + characters that has the different value than the default value of + TABLE. */ + +static void +map_chartable (MSubCharTable *table, void *ignore, int default_p, + void (*func) (int, int, void *, void *), + void *func_arg) +{ + void *current; + int from = 0; + int c, next_c; + + current = lookup_chartable (table, 0, &next_c, default_p); + c = next_c; + while (c >= 0 && c <= MCHAR_MAX) + { + void *next = lookup_chartable (table, c, &next_c, default_p); + + if (current != next) + { + if (current != ignore) + (*func) (from, c - 1, current, func_arg); + current = next; + from = c; + } + c = next_c; + } + if (from <= MCHAR_MAX && current != ignore) + (*func) (from, MCHAR_MAX, current, func_arg); +} + + +/* Return the smallest character whose value is not DEFAULT_VALUE in + TABLE. If all characters in TABLE have DEFAULT_VALUE, return + -1. */ + +static int +chartab_min_non_default_char (MSubCharTable *table, void *default_value) +{ + int depth = TABLE_DEPTH (table); + int slots; + int i, c; + + if (!table->contents.tables) + return (default_value == table->default_value + ? -1 : TABLE_MIN_CHAR (table)); + + slots = chartab_slots[depth]; + + if (depth == CHAR_TAB_MAX_DEPTH) + { + for (i = 0; i < slots; i++) + if (table->contents.values[i] != default_value) + return (TABLE_MIN_CHAR (table) + i); + } + else + { + for (i = 0; i < slots; i++) + if ((c = chartab_min_non_default_char (table->contents.tables + i, + default_value)) + >= 0) + return c; + } + return -1; +} + + +/* Return the largest character whose value is not DEFAULT_VALUE in + TABLE. If all characters in TABLE have DEFAULT_VALUE, return + -1. */ + +static int +chartab_max_non_default_char (MSubCharTable *table, void *default_value) +{ + int depth = TABLE_DEPTH (table); + int slots; + int i, c; + + if (!table->contents.tables) + return (default_value == table->default_value + ? -1 : TABLE_MIN_CHAR (table) + chartab_chars[depth] - 1); + + slots = chartab_slots[depth]; + + if (depth == CHAR_TAB_MAX_DEPTH) + { + for (i = slots - 1; i >= 0; i--) + if (table->contents.values[i] != default_value) + return (TABLE_MIN_CHAR (table) + i); + } + else + { + for (i = slots - 1; i >= 0; i--) + if ((c = chartab_max_non_default_char (table->contents.tables + i, + default_value)) + >= 0) + return c; + } + return -1; +} + +static void +free_chartable (void *object) +{ + MCharTable *table = (MCharTable *) object; + int managedp = table->key != Mnil && table->key->managing_key; + + if (table->subtable.contents.tables) + { + int i; + + for (i = 0; i < chartab_slots[0]; i++) + free_sub_tables (table->subtable.contents.tables + i, managedp); + free (table->subtable.contents.tables); + if (managedp && table->subtable.default_value) + M17N_OBJECT_UNREF (table->subtable.default_value); + } + M17N_OBJECT_UNREGISTER (chartable_table, table); + free (object); +} + +#include + +/* Support function of mdebug_dump_chartab. */ + +static void +dump_sub_chartab (MSubCharTable *table, void *default_value, + MSymbol key, int indent) +{ + int depth = TABLE_DEPTH (table); + int min_char = TABLE_MIN_CHAR (table); + int max_char = min_char + (chartab_chars[depth] - 1); + char *prefix = (char *) alloca (indent + 1); + int i; + + if (max_char < 0 || max_char > MCHAR_MAX) + max_char = MCHAR_MAX; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + if (! table->contents.tables && table->default_value == default_value) + return; + fprintf (stderr, "\n%s(sub%d (U+%04X U+%04X) ", + prefix, depth, min_char, max_char); + if (key == Msymbol) + { + if (table->default_value) + fprintf (stderr, "(default %s)", + ((MSymbol) table->default_value)->name); + else + fprintf (stderr, "(default nil)"); + } + else + fprintf (stderr, "(default #x%X)", (unsigned) table->default_value); + + default_value = table->default_value; + if (table->contents.tables) + { + if (depth < CHAR_TAB_MAX_DEPTH) + for (i = 0; i < chartab_slots[depth]; i++) + dump_sub_chartab (table->contents.tables + i, default_value, + key, indent + 2); + else + for (i = 0; i < chartab_slots[depth]; i++, min_char++) + { + void **val = table->contents.values + i; + + if (val == default_value) + continue; + default_value = *val; + fprintf (stderr, "\n%s (U+%04X", prefix, min_char); + while (i + 1 < chartab_slots[depth] + && val[1] == default_value) + i++, val++, min_char++; + fprintf (stderr, "-U+%04X ", min_char); + if (key == Msymbol) + { + if (default_value) + fprintf (stderr, "%s)", ((MSymbol) default_value)->name); + else + fprintf (stderr, "nil)"); + } + else + fprintf (stderr, " #xx%X)", (unsigned) default_value); + } + } + fprintf (stderr, ")"); +} + + +/* Internal API */ + +int +mchartable__init () +{ + chartable_table.count = 0; + return 0; +} + +void +mchartable__fini () +{ + mdebug__report_object ("Chartable", &chartable_table); +} + +void * +mchartable__lookup (MCharTable *table, int c, int *next_c, int default_p) +{ + return lookup_chartable (&table->subtable, c, next_c, default_p); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nChartable */ +/*** @{ */ +/*=*/ + +/***en + @brief Symbol whose name is "char-table". + + The symbol @c Mchar_table has the name "char-table". */ + +/***ja + @brief "char-table" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë + + ÊÑ¿ô @c Mchar_table ¤Ï̾Á° "char-table" ¤ò»ý¤ÄÄêµÁºÑ¤ß¥·¥ó + ¥Ü¥ë¤Ç¤¢¤ë¡£ */ + +MSymbol Mchar_table; + +/*=*/ + +/***en + @brief Create a new chartable. + + The mchartable () function creates a new chartable object with + symbol $KEY and the default value $DEFAULT_VALUE. If $KEY is a + managing key, the elements of the table (including the default + value) are managed objects or NULL. + + @return + If the operation was successful, mchartable () returns a pointer + to the created chartable. Otherwise it returns @c NULL and + assigns an error code to the external variable @c merror_code. */ + +/***ja + @brief ¿·¤·¤¤Ê¸»ú¥Æ¡¼¥Ö¥ë¤òºî¤ë + + ´Ø¿ô mchartable () ¤Ï¥­¡¼¤¬ $KEY ¤ÇÍ×ÁǤΥǥե©¥ë¥ÈÃͤ¬ + $DEFAULT_VALUE ¤Ç¤¢¤ë¿·¤·¤¤Ê¸»ú¥Æ¡¼¥Ö¥ë¤òºî¤ë¡£¤â¤· $KEY ¤¬´ÉÍý¥­¡¼ + ¤Ç¤¢¤ì¤Ð¡¢¤³¤Î¥Æ¡¼¥Ö¥ë¤ÎÍ×ÁǤϡʥǥե©¥ë¥ÈÃͤò´Þ¤á¤Æ¡Ë´ÉÍý²¼¥ª¥Ö¥¸¥§ + ¥¯¥È¤« NULL ¤Ç¤¢¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mchartable () ¤ÏºîÀ®¤µ¤ì¤¿Ê¸»ú¥Æ¡¼¥Ö¥ë¤Ø¤Î¥Ý¥¤¥ó + ¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨ + ¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +MCharTable * +mchartable (MSymbol key, void *default_value) +{ + MCharTable *table; + + M17N_OBJECT (table, free_chartable, MERROR_CHARTABLE); + M17N_OBJECT_REGISTER (chartable_table, table); + table->key = key; + table->min_char = 0; + table->max_char = -1; + SET_DEPTH_MIN_CHAR (&table->subtable, 0, 0); + table->subtable.default_value = default_value; + if (key != Mnil && key->managing_key && default_value) + M17N_OBJECT_REF (default_value); + table->subtable.contents.tables = NULL; + return table; +} + +/*=*/ + +/***en + @brief Return the assigned value of a character in a chartable. + + The mchartable_lookup () function returns the value assigned to + character $C in chartable $TABLE. If no value has been set for $C + explicitly, the default value of $TABLE is returned. If $C is not + a valid character, mchartable_lookup () returns @c NULL and + assigns an error code to the external variable @c merror_code. */ + +/***ja + @brief ʸ»ú¥Æ¡¼¥Ö¥ëÃæ¤Çʸ»ú¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ÃͤòÊÖ¤¹ + + ´Ø¿ô mchartable_lookup () ¤Ïʸ»ú¥Æ¡¼¥Ö¥ë $TABLE Ãæ¤Çʸ»ú $C ¤Ë³ä¤ê + Åö¤Æ¤é¤ì¤¿ÃͤòÊÖ¤¹¡£$C ¤ËÂФ¹¤ëÌÀ¼¨Åª¤ÊÃͤ¬¤Ê¤±¤ì¤Ð¡¢$TABLE ¤Î¥Ç¥Õ¥© + ¥ë¥ÈÃͤòÊÖ¤¹¡£$C ¤¬ÂÅÅö¤Êʸ»ú¤Ç¤Ê¤±¤ì¤Ð¡¢mchartable_lookup () ¤Ï + @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CHAR + + @seealso + mchartable_set () */ + +void * +mchartable_lookup (MCharTable *table, int c) +{ + M_CHECK_CHAR (c, NULL); + + if (c < table->min_char || c > table->max_char) + return table->subtable.default_value; + return lookup_chartable (&table->subtable, c, NULL, 0); +} + +/*=*/ + +/***en + @brief Assign a value to a character in a chartable. + + The mchartable_set () function sets the value of character $C in + chartable $TABLE to $VAL. + + @return + If the operation was successful, mchartable_set () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable @c merror_code. */ + +/***ja + @brief ʸ»ú¥Æ¡¼¥Ö¥ëÃæ¤Ç¤Îʸ»ú¤ÎÃͤòÀßÄꤹ¤ë + + ´Ø¿ô mchartable_set () ¤Ï¡¢Ê¸»ú¥Æ¡¼¥Ö¥ë $TABLE Ãæ¤Îʸ»ú $C ¤Ë + ÃÍ $VAL ¤ò³ä¤êÅö¤Æ¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mchartable_set () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 + ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CHAR + + @seealso + mchartable_lookup (), mchartable_set_range () */ + + +int +mchartable_set (MCharTable *table, int c, void *val) +{ + int managedp = table->key != Mnil && table->key->managing_key; + MSubCharTable *sub = &table->subtable; + int i; + + M_CHECK_CHAR (c, -1); + + if (table->max_char < 0) + table->min_char = table->max_char = c; + else + { + if (c < table->min_char) + table->min_char = c; + else if (c > table->max_char) + table->max_char = c; + } + + for (i = 0; i < CHAR_TAB_MAX_DEPTH; i++) + { + if (! sub->contents.tables) + { + if (sub->default_value == val) + return 0; + make_sub_tables (sub, managedp); + } + sub = sub->contents.tables + SUB_IDX (i, c); + } + if (! sub->contents.values) + { + if (sub->default_value == val) + return 0; + make_sub_values (sub, managedp); + } + sub->contents.values[SUB_IDX (3, c)] = val; + if (managedp && val) + M17N_OBJECT_REF (val); + return 0; +} + +/*=*/ + +/***en + @brief Assign a value to the characters in the specified range. + + The mchartable_set_range () function assigns value $VAL to the + characters from $FROM to $TO (both inclusive) in chartable $TABLE. + + @return + If the operation was successful, mchartable_set_range () returns + 0. Otherwise it returns -1 and assigns an error code to the + external variable @c merror_code. If $FROM is greater than $TO, + mchartable_set_range () returns immediately without an error. */ + +/***ja + @brief »ØÄêÈϰϤÎʸ»ú¤ÎÃͤòÀßÄꤹ¤ë + + ´Ø¿ô mchartable_set_range () ¤Ï¡¢Ê¸»ú¥Æ¡¼¥Ö¥ë $TABLE Ãæ¤Î $FROM ¤« + ¤é $TO ¤Þ¤Ç¡Êξü¤ò´Þ¤à¡Ë¤Îʸ»ú¤Ë¡¢ÃͤȤ·¤Æ $VAL ¤òÀßÄꤹ¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mchartable_set_range () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð + -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£$FROM ¤¬ + $TO ¤è¤êÂ礭¤¤¤È¤­¤Ë¤Ï¡¢ mchartable_set_range () ¤Ï²¿¤â¤»¤º¡¢¥¨¥é¡¼ + ¤âµ¯¤³¤µ¤Ê¤¤¡£ */ + +/*** + @errors + @c MERROR_CHAR + + @seealso + mchartable_set () */ + +int +mchartable_set_range (MCharTable *table, int from, int to, void *val) +{ + int managedp = table->key != Mnil && table->key->managing_key; + + M_CHECK_CHAR (from, -1); + M_CHECK_CHAR (to, -1); + + if (from > to) + return 0; + + if (table->max_char < 0) + table->min_char = from, table->max_char = to; + else{ + if (from < table->min_char) + table->min_char = from; + if (to > table->max_char) + table->max_char = to; + } + set_chartable_range (&table->subtable, from, to, val, managedp); + return 0; +} + +/*=*/ + +/***en + @brief Search for characters that have non-default value. + + The mchartable_range () function searches chartable $TABLE for the + first and the last character codes that do not have the default + value of $TABLE, and set $FROM and $TO to them, respectively. If + all characters have the default value, both $FROM and $TO are set + to -1. */ + +/***ja + @brief Ãͤ¬¥Ç¥Õ¥©¥ë¥È¤È°Û¤Ê¤ëʸ»ú¤òõ¤¹ + + ´Ø¿ô mchartable_range () ¤Ïʸ»ú¥Æ¡¼¥Ö¥ë $TABLE Ãæ¤Ç¡¢$TABLE ¤Î¥Ç¥Õ¥© + ¥ë¥ÈÃͰʳ°¤ÎÃͤò»ý¤ÄºÇ½é¤ÈºÇ¸å¤Îʸ»ú¤òÄ´¤Ù¡¢¤½¤ì¤¾¤ì¤ò $FROM ¤È + $TO ¤ËÀßÄꤹ¤ë¡£¤¹¤Ù¤Æ¤Îʸ»ú¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤòÃͤȤ·¤Æ»ý¤Ã¤Æ¤¤¤ì¤Ð¡¢ + $FROM ¤È $TO ¤ò -1¤ËÀßÄꤹ¤ë¡£ */ + +void +mchartable_range (MCharTable *table, int *from, int *to) +{ + *from = chartab_min_non_default_char (&table->subtable, + table->subtable.default_value); + if (*from == -1) + *to = -1; + else + *to = chartab_max_non_default_char (&table->subtable, + table->subtable.default_value); +} + +/*=*/ + +/***en + @brief Call a function for characters in a chartable. + + The mchartable_map () function calls function $FUNC for characters + in chartable $TABLE. No function call occurs for characters that + have value $IGNORE in $TABLE. Comparison of $IGNORE and character + value is done with the operator @c ==. Be careful when you use + string literals or pointers. + + Instead of calling $FUNC for each character, mchartable_map () + tries to optimize the number of function calls, i.e. it makes a + single function call for a chunk of characters when those + consecutive characters have the same value. + + No matter how long the character chunk is, $FUNC is called with + four arguments; $FROM, $TO, $VAL, and $ARG. $FROM and $TO (both + inclusive) defines the range of characters that have value $VAL. + $ARG is the same as $FUNC_ARG. + + @return + This function always returns 0. */ + +/***ja + @brief ʸ»ú¥Æ¡¼¥Ö¥ëÃæ¤Îʸ»ú¤ËÂФ·¤Æ»ØÄê¤Î´Ø¿ô¤ò¸Æ¤Ö + + ´Ø¿ô mchartable_map () ¤Ï¡¢Ê¸»ú¥Æ¡¼¥Ö¥ë $TABLE Ãæ¤Îʸ»ú¤ËÂФ·¤Æ´Ø + ¿ô $FUNC ¤ò¸Æ¤Ö¡£¤¿¤À¤·Ãͤ¬ $IGNORE ¤Ç¤¢¤ëʸ»ú¤Ë¤Ä¤¤¤Æ¤Ï´Ø¿ô¸Æ¤Ó½Ð + ¤·¤ò¹Ô¤Ê¤ï¤Ê¤¤¡£$IGNORE ¤Èʸ»ú¤ÎÃͤÎÈæ³Ó¤Ï @c == ¤Ç¹Ô¤Ê¤¦¤Î¤Ç¡¢Ê¸»úÎó + ¥ê¥Æ¥é¥ë¤ä¥Ý¥¤¥ó¥¿¤ò»È¤¦ºÝ¤Ë¤ÏÃí°Õ¤òÍפ¹¤ë¡£ + + mchartable_map () ¤Ï¡¢°ìʸ»ú¤´¤È¤Ë $FUNC ¤ò¸Æ¤Ö¤Î¤Ç¤Ï¤Ê¤¯¡¢´Ø¿ô¸Æ + ¤Ó½Ð¤·¤Î²ó¿ô¤òºÇŬ²½¤·¤è¤¦¤È¤¹¤ë¡£¤¹¤Ê¤ï¤Á¡¢Ï¢Â³¤·¤¿Ê¸»ú¤¬Æ±¤¸Ãͤò + »ý¤Ã¤Æ¤¤¤¿¾ì¹ç¤Ë¤Ï¡¢¤½¤Îʸ»ú¤Î¤Þ¤È¤Þ¤êÁ´ÂΤˤĤ¤¤Æ°ìÅ٤δؿô¸Æ¤Ó½Ð + ¤·¤·¤«¹Ô¤Ê¤ï¤Ê¤¤¡£ + + ʸ»ú¤Î¤Þ¤È¤Þ¤ê¤ÎÂ礭¤µ¤Ë¤«¤«¤ï¤é¤º¡¢$FUNC ¤Ï $FROM, $TO, $VAL, $ARG + ¤Î£´°ú¿ô¤Ç¸Æ¤Ð¤ì¤ë¡£$FROM ¤È $TO ¡Êξü¤ò´Þ¤à¡Ë¤Ï $VAL ¤òÃͤȤ·¤Æ + »ý¤Äʸ»ú¤ÎÈϰϤò¼¨¤·¡¢$ARG ¤Ï $FUNC_ARG ¤½¤Î¤â¤Î¤Ç¤¢¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë0¤òÊÖ¤¹¡£ */ + +int +mchartable_map (MCharTable *table, void *ignore, + void (*func) (int, int, void *, void *), + void *func_arg) +{ + map_chartable (&table->subtable, ignore, 0, func, func_arg); + return 0; +} + +/*=*/ + +/*** @} */ + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump a chartable. + + The mdebug_dump_chartab () function prints a chartable $TABLE in a + human readable way to the stderr. $INDENT specifies how many + columns to indent the lines but the first one. + + @return + This function returns $TABLE. */ + +MCharTable * +mdebug_dump_chartab (MCharTable *table, int indent) +{ + fprintf (stderr, "(chartab (U+%04X U+%04X)", + table->min_char, table->max_char); + dump_sub_chartab (&table->subtable, table->subtable.default_value, + table->key, indent + 2); + fprintf (stderr, ")"); + return table; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/chartab.h b/src/chartab.h new file mode 100644 index 0000000..715acbf --- /dev/null +++ b/src/chartab.h @@ -0,0 +1,30 @@ +/* chartab.h -- header file for the character table module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_CHARTAB_H_ +#define _M17N_CHARTAB_H_ + +extern void *mchartable__lookup (MCharTable *table, int c, + int *next_c, int default_p); + +#endif /* not _M17N_CHARTAB_H_ */ + diff --git a/src/coding.c b/src/coding.c new file mode 100644 index 0000000..3828d2c --- /dev/null +++ b/src/coding.c @@ -0,0 +1,4863 @@ +/* coding.c -- code conversion module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nConv + @brief Coding system objects and API for them. + + The m17n library represents a character encoding scheme (CES) of + coded character sets (CCS) as an object called @e coding @e + system. Application programs can add original coding systems. + + To @e encode means converting code-points to character codes and + to @e decode means converting character codes back to code-points. + + Application programs can decode a byte sequence with a specified + coding system into an M-text, and inversely, can encode an M-text + into a byte sequence. */ + +/***ja + @addtogroup m17nConv + @brief ¥³¡¼¥É·Ï¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¡¢Éä¹æ²½Ê¸»ú½¸¹ç (coded character sets; CCS) ¤Îʸ + »úÉä¹ç²½Êý¼° (character encoding scheme; CES) ¤ò @e ¥³¡¼¥É·Ï ¤È¸Æ + ¤Ö¥ª¥Ö¥¸¥§¥¯¥È¤Çɽ¸½¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬¥µ¥Ý¡¼¥È¤¹¤ëCES ¤Ï¡¢ + UTF-8, UTF-16, ISO-2022, DIRECT-CHARSET, ¤½¤Î¾¡¢¤ËÂçÊ̤µ¤ì¤ë¡£¥¢ + ¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Ë¥³¡¼¥É·Ï¤òÄɲ乤뤳¤È¤â²Äǽ¤Ç¤¢¤ë¡£ + + ¥³¡¼¥É¥Ý¥¤¥ó¥È¤«¤éʸ»ú¥³¡¼¥É¤Ø¤ÎÊÑ´¹¤ò @e ¥¨¥ó¥³¡¼¥É ¤È¸Æ¤Ó¡¢Ê¸»ú + ¥³¡¼¥É¤«¤é¥³¡¼¥É¥Ý¥¤¥ó¥È¤Ø¤ÎÊÑ´¹¤ò @e ¥Ç¥³¡¼¥É ¤È¸Æ¤Ö¡£ + + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢»ØÄꤵ¤ì¤¿¥³¡¼¥É·Ï¤Ç¥Ð¥¤¥ÈÎó¤ò¥Ç¥³¡¼ + ¥É¤¹¤ë¤³¤È¤Ç M-text ¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤Þ¤¿µÕ¤Ë¡¢»ØÄꤵ¤ì¤¿¥³¡¼¥É + ·Ï¤Ç M-text ¤ò¥¨¥ó¥³¡¼¥É¤·¤¹¤ë¤³¤È¤Ç¥Ð¥¤¥ÈÎó¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "plist.h" +#include "character.h" +#include "charset.h" +#include "coding.h" +#include "mtext.h" +#include "symbol.h" +#include "mlocale.h" + +#define NUM_SUPPORTED_CHARSETS 32 + +/** Structure for coding system object. */ + +typedef struct +{ + /** Name of the coding system. */ + MSymbol name; + + /** Type of the coding system. */ + MSymbol type; + + /* Number of supported charsets. */ + int ncharsets; + + /** Array of supported charsets. */ + MCharset *charsets[NUM_SUPPORTED_CHARSETS]; + + /** If non-NULL, function to call at the time of creating and + reseting a converter. */ + int (*resetter) (MConverter *converter); + + int (*decoder) (unsigned char *str, int str_bytes, MText *mt, + MConverter *converter); + + int (*encoder) (MText *mt, int from, int to, + unsigned char *str, int str_bytes, + MConverter *converter); + + /** If non-zero, the coding system decode/encode ASCII characters as + is. */ + int ascii_compatible; + + /** Pointer to extra information given when the coding system is + defined. The meaning depends on . */ + void *extra_info; + + /** Pointer to information referred on conversion. The meaning + depends on . The value NULL means that the coding system + is not yet setup. */ + void *extra_spec; + + int ready; +} MCodingSystem; + +struct MCodingList +{ + int size, inc, used; + MCodingSystem **codings; +}; + +static struct MCodingList coding_list; + +static MPlist *coding_definition_list; + +typedef struct { + /**en + Pointer to a structure of a coding system. */ + /**ja + ¥³¡¼¥É·Ï¤òɽ¤ï¤¹¥Ç¡¼¥¿¹½Â¤¤Ø¤Î¥Ý¥¤¥ó¥¿ */ + MCodingSystem *coding; + + /**en + Buffer for carryover bytes generated while decoding. */ + /**ja + ¥Ç¥³¡¼¥ÉÃæ¤Î¥­¥ã¥ê¥£¥ª¡¼¥Ð¡¼¥Ð¥¤¥ÈÍѥХåե¡ */ + unsigned char carryover[256]; + + /**en + Number of carryover bytes. */ + /**ja + ¥­¥ã¥ê¥£¥ª¡¼¥Ð¡¼¥Ð¥¤¥È¿ô */ + int carryover_bytes; + + /**en + Beginning of the byte sequence bound to this converter. */ + /**ja + ¤³¤Î¥³¥ó¥Ð¡¼¥¿¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥Ð¥¤¥ÈÎó¤ÎÀèƬ°ÌÃÖ */ + unsigned char *buf; + + /**en + Size of buf. */ + /**ja + buf ¤ÎÂ礭¤µ */ + int bufsize; + + /**en + Number of bytes already consumed in buf. */ + /**ja + buf Æâ¤Ç¤¹¤Ç¤Ë¾ÃÈñ¤µ¤ì¤¿¥Ð¥¤¥È¿ô */ + int used; + + /**en + Stream bound to this converter. */ + /**ja + ¤³¤Î¥³¥ó¥Ð¡¼¥¿¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥¹¥È¥ê¡¼¥à */ + FILE *fp; + + /**en + Which of above two is in use. */ + /**ja + ¾åµ­2¼Ô¤Î¤¤¤º¤ì¤¬»È¤ï¤ì¤Æ¤¤¤ë¤« */ + int binding; + + /**en + Buffer for unget. */ + /**ja + Unget ÍѥХåե¡ */ + MText *unread; + + /*en + Working area. */ + /*ja + ºî¶ÈÎΰè */ + MText *work_mt; + + int seekable; +} MConverterStatus; + + + +/* Local macros and functions. */ + +/** At first, set SRC_BASE to SRC. Then check if we have already + produced AT_MOST chars. If so, set SRC_END to SRC, and jump to + source_end. Otherwise, get one more byte C from SRC. In that + case, if SRC == SRC_END, jump to the label source_end. */ + +#define ONE_MORE_BASE_BYTE(c) \ + do { \ + src_base = src; \ + if (nchars == at_most) \ + { \ + src_end = src; \ + goto source_end; \ + } \ + if (src == src_stop) \ + { \ + if (src == src_end) \ + goto source_end; \ + src_base = src = source; \ + if (src == src_end) \ + goto source_end; \ + src_stop = src_end; \ + } \ + (c) = *src++; \ + } while (0) + + +/** Get one more byte C from SRC. If SRC == SRC_END, jump to the + label source_end. */ + +#define ONE_MORE_BYTE(c) \ + do { \ + if (src == src_stop) \ + { \ + if (src == src_end) \ + goto source_end; \ + src = source; \ + if (src == src_end) \ + goto source_end; \ + src_stop = src_end; \ + } \ + (c) = *src++; \ + } while (0) + + +#define REWIND_SRC_TO_BASE() \ + do { \ + if (src_base < source || src_base >= src_end) \ + src_stop = internal->carryover + internal->carryover_bytes; \ + src = src_base; \ + } while (0) + + +/** Push back byte C to SRC. */ + +#define UNGET_ONE_BYTE(c) \ + do { \ + if (src > source) \ + src--; \ + else \ + { \ + internal->carryover[0] = c; \ + internal->carryover_bytes = 1; \ + src = internal->carryover; \ + src_stop = src + 1; \ + } \ + } while (0); + + +/** Store multibyte representation of character C at DST and increment + DST to the next of the produced bytes. DST must be a pointer to + data area of M-text MT. If the produced bytes are going to exceed + DST_END, enlarge the data area of MT. */ + +#define EMIT_CHAR(c) \ + do { \ + int bytes = CHAR_BYTES (c); \ + int len; \ + \ + if (dst + bytes + 1 > dst_end) \ + { \ + len = dst - mt->data; \ + bytes = mt->allocated + bytes + (src_stop - src); \ + mtext__enlarge (mt, bytes); \ + dst = mt->data + len; \ + dst_end = mt->data + mt->allocated; \ + } \ + dst += CHAR_STRING (c, dst); \ + nchars++; \ + } while (0) + + +/* Check if there is enough room to produce LEN bytes at DST. If not, + go to the label insufficient_destination. */ + +#define CHECK_DST(len) \ + do { \ + if (dst + (len) > dst_end) \ + goto insufficient_destination; \ + } while (0) + + +/** Take NUM_CHARS characters (NUM_BYTES bytes) already stored at + (MT->data + MT->nbytes) into MT, and put charset property on + them with CHARSET->name. */ + +#define TAKEIN_CHARS(mt, num_chars, num_bytes, charset) \ + do { \ + int chars = (num_chars); \ + \ + if (chars > 0) \ + { \ + mtext__takein ((mt), chars, (num_bytes)); \ + if (charset) \ + mtext_put_prop ((mt), (mt)->nchars - chars, (mt)->nchars, \ + Mcharset, (void *) ((charset)->name)); \ + } \ + } while (0) + + +#define SET_SRC(mt, format, from, to) \ + do { \ + if (format <= MTEXT_FORMAT_UTF_8) \ + { \ + src = mt->data + POS_CHAR_TO_BYTE (mt, from); \ + src_end = mt->data + POS_CHAR_TO_BYTE (mt, to); \ + } \ + else if (format <= MTEXT_FORMAT_UTF_16BE) \ + { \ + src \ + = mt->data + (sizeof (short)) * POS_CHAR_TO_BYTE (mt, from); \ + src_end \ + = mt->data + (sizeof (short)) * POS_CHAR_TO_BYTE (mt, to); \ + } \ + else \ + { \ + src = mt->data + (sizeof (int)) * from; \ + src_end = mt->data + (sizeof (int)) * to; \ + } \ + } while (0) + + +#define ONE_MORE_CHAR(c, bytes, format) \ + do { \ + if (src == src_end) \ + goto finish; \ + if (format <= MTEXT_FORMAT_UTF_8) \ + c = STRING_CHAR_AND_BYTES (src, bytes); \ + else if (format <= MTEXT_FORMAT_UTF_16BE) \ + { \ + c = mtext_ref_char (mt, from++); \ + bytes = (sizeof (short)) * CHAR_UNITS_UTF16 (c); \ + } \ + else \ + { \ + c = ((unsigned *) (mt->data))[from++]; \ + bytes = sizeof (int); \ + } \ + } while (0) + + +static int +encode_unsupporeted_char (int c, unsigned char *dst, unsigned char *dst_end, + MText *mt, int pos) +{ + int len; + char *format; + + len = c < 0x10000 ? 8 : 10; + if (dst + len > dst_end) + return 0; + + format = (c < 0xD800 ? "" + : c < 0xE000 ? "" + : c < 0x10000 ? "" + : c < 0x110000 ? "" + : ""); + sprintf ((char *) dst, format, c); + return len; +} + + + +/** Finish decoding of bytes at SOURCE (ending at SRC_END) into NCHARS + characters by CONVERTER into M-text MT. SRC is a pointer to the + not-yet processed bytes. ERROR is 1 iff an invalid byte was + found. */ + +static int +finish_decoding (MText *mt, MConverter *converter, int nchars, + unsigned char *source, unsigned char *src_end, + unsigned char *src, + int error) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + if (src == src_end) + internal->carryover_bytes = 0; + else if (error + || (converter->last_block + && ! converter->lenient)) + converter->result = MCONVERSION_RESULT_INVALID_BYTE; + else if (! converter->last_block) + { + unsigned char *dst = internal->carryover; + + if (src < source || src > src_end) + { + dst += internal->carryover_bytes; + src = source; + } + while (src < src_end) + *dst++ = *src++; + internal->carryover_bytes = dst - internal->carryover; + converter->result = MCONVERSION_RESULT_INSUFFICIENT_SRC; + } + else + { + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + unsigned char *src_stop = src_end; + int c; + int last_nchars = nchars; + + if (src < source || src > src_end) + src_stop = internal->carryover + internal->carryover_bytes; + while (1) + { + if (converter->at_most && nchars == converter->at_most) + break; + if (src == src_stop) + { + if (src == src_end) + break; + src = source; + if (src == src_end) + break; + src_stop = src_end; + } + c = *src++; + EMIT_CHAR (c); + } + TAKEIN_CHARS (mt, nchars - last_nchars, dst - (mt->data + mt->nbytes), + mcharset__binary); + internal->carryover_bytes = 0; + } + + converter->nchars += nchars; + converter->nbytes += ((src < source || src > src_end) ? 0 : src - source); + return (converter->result == MCONVERSION_RESULT_INVALID_BYTE ? -1 : 0); +} + + + +/* Staffs for coding-systems of type MCODING_TYPE_CHARSET. */ + +static int +setup_coding_charset (MCodingSystem *coding) +{ + int ncharsets = coding->ncharsets; + unsigned *code_charset_table; + + if (ncharsets > 1) + { + /* At first, reorder charset list by dimensions (a charset of + smaller dimension comes first). As the number of charsets is + usually very small (at most 32), we do a simple sort. */ + MCharset **charsets; + int idx = 0; + int i, j; + + MTABLE_ALLOCA (charsets, NUM_SUPPORTED_CHARSETS, MERROR_CODING); + memcpy (charsets, coding->charsets, + sizeof (MCharset *) * NUM_SUPPORTED_CHARSETS); + for (i = 0; i < 4; i++) + for (j = 0; j < ncharsets; j++) + if (charsets[j]->dimension == i) + coding->charsets[idx++] = charsets[j]; + } + + MTABLE_CALLOC (code_charset_table, 256, MERROR_CODING); + while (ncharsets--) + { + int dim = coding->charsets[ncharsets]->dimension; + int from = coding->charsets[ncharsets]->code_range[(dim - 1) * 4]; + int to = coding->charsets[ncharsets]->code_range[(dim - 1) * 4 + 1]; + + if (coding->charsets[ncharsets]->ascii_compatible) + coding->ascii_compatible = 1; + while (from <= to) + code_charset_table[from++] |= 1 << ncharsets; + } + + coding->extra_spec = (void *) code_charset_table; + return 0; +} + +static int +reset_coding_charset (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + + if (! coding->ready + && setup_coding_charset (coding) < 0) + return -1; + coding->ready = 1; + return 0; +} + +static int +decode_coding_charset (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + + unsigned *code_charset_table = (unsigned *) coding->extra_spec; + MCharset **charsets = coding->charsets; + MCharset *charset = mcharset__ascii; + int error = 0; + + while (1) + { + MCharset *this_charset = NULL; + int c; + unsigned mask; + + ONE_MORE_BASE_BYTE (c); + mask = code_charset_table[c]; + if (mask) + { + int idx = 0; + unsigned code = c; + int nbytes = 1; + int dim; + + while (mask) + { + while (! (mask & 1)) mask >>= 1, idx++; + this_charset = charsets[idx]; + dim = this_charset->dimension; + while (nbytes < dim) + { + ONE_MORE_BYTE (c); + code = (code << 8) | c; + nbytes++; + } + c = DECODE_CHAR (this_charset, code); + if (c >= 0) + goto emit_char; + mask >>= 1, idx++; + } + } + + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + c = *src++; + this_charset = mcharset__binary; + + emit_char: + if (this_charset != mcharset__ascii + && this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c); + } + /* We reach here because of an invalid byte. */ + error = 1; + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); +} + +static int +encode_coding_charset (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + int ncharsets = coding->ncharsets; + MCharset **charsets = coding->charsets; + int ascii_compatible = coding->ascii_compatible; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + while (1) + { + int c, bytes; + + ONE_MORE_CHAR (c, bytes, format); + + if (c < 0x80 && ascii_compatible) + { + CHECK_DST (1); + *dst++ = c; + } + else + { + unsigned code; + MCharset *charset = NULL; + int i = 0; + + while (1) + { + charset = charsets[i]; + code = ENCODE_CHAR (charset, c); + if (code != MCHAR_INVALID_CODE) + break; + if (++i == ncharsets) + goto unsupported_char; + } + + CHECK_DST (charset->dimension); + if (charset->dimension == 1) + { + *dst++ = code; + } + else if (charset->dimension == 2) + { + *dst++ = code >> 8; + *dst++ = code & 0xFF; + } + else if (charset->dimension == 3) + { + *dst++ = code >> 16; + *dst++ = (code >> 8) & 0xFF; + *dst++ = code & 0xFF; + } + else + { + *dst++ = code >> 24; + *dst++ = (code >> 16) & 0xFF; + *dst++ = (code >> 8) & 0xFF; + *dst++ = code & 0xFF; + } + } + src += bytes; + nchars++; + continue; + + unsupported_char: + { + int len; + + if (! converter->lenient) + break; + len = encode_unsupporeted_char (c, dst, dst_end, mt, from + nchars); + if (len == 0) + goto insufficient_destination; + dst += len; + src += bytes; + nchars++; + } + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + + +/* Staffs for coding-systems of type MCODING_TYPE_UTF (8). */ + +#define UTF8_CHARSET(p) \ + (! ((p)[0] & 0x80) ? (mcharset__unicode) \ + : CHAR_HEAD_P ((p) + 1) ? (mcharset__binary) \ + : ! ((p)[0] & 0x20) ? (mcharset__unicode) \ + : CHAR_HEAD_P ((p) + 2) ? (mcharset__binary) \ + : ! ((p)[0] & 0x10) ? (mcharset__unicode) \ + : CHAR_HEAD_P ((p) + 3) ? (mcharset__binary) \ + : ! ((p)[0] & 0x08) ? ((((((p)[0] & 0x07) << 2) \ + & (((p)[1] & 0x30) >> 4)) <= 0x10) \ + ? (mcharset__unicode) \ + : (mcharset__m17n)) \ + : CHAR_HEAD_P ((p) + 4) ? (mcharset__binary) \ + : ! ((p)[0] & 0x04) ? (mcharset__m17n) \ + : CHAR_HEAD_P ((p) + 5) ? (mcharset__binary) \ + : ! ((p)[0] & 0x02) ? (mcharset__m17n) \ + : (mcharset__binary)) + + +static int +decode_coding_utf_8 (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + int error = 0; + int full = converter->lenient || (coding->charsets[0] == mcharset__m17n); + MCharset *charset = NULL; + + while (1) + { + int c, c1, bytes; + MCharset *this_charset = NULL; + + ONE_MORE_BASE_BYTE (c); + + if (!(c & 0x80)) + bytes = 1; + else if (!(c & 0x40)) + goto invalid_byte; + else if (!(c & 0x20)) + bytes = 2, c &= 0x1F; + else if (!(c & 0x10)) + bytes = 3, c &= 0x0F; + else if (!(c & 0x08)) + bytes = 4, c &= 0x07; + else if (!(c & 0x04)) + bytes = 5, c &= 0x03; + else if (!(c & 0x02)) + bytes = 6, c &= 0x01; + else + goto invalid_byte; + + while (bytes-- > 1) + { + ONE_MORE_BYTE (c1); + if ((c1 & 0xC0) != 0x80) + goto invalid_byte; + c = (c << 6) | (c1 & 0x3F); + } + + if (full + || c < 0xD800 || (c >= 0xE000 && c < 0x110000)) + goto emit_char; + + invalid_byte: + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + c = *src++; + this_charset = mcharset__binary; + + emit_char: + if (this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c); + } + /* We reach here because of an invalid byte. */ + error = 1; + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); +} + +static int +encode_coding_utf_8 (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + + if (format <= MTEXT_FORMAT_UTF_8 + && (converter->lenient + || coding->charsets[0] == mcharset__m17n)) + { + if (dst_bytes < src_end - src) + { + int byte_pos = (src + dst_bytes) - mt->data; + + to = POS_BYTE_TO_CHAR (mt, byte_pos); + byte_pos = POS_CHAR_TO_BYTE (mt, to); + src_end = mt->data + byte_pos; + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + } + memcpy (destination, src, src_end - src); + nchars = to - from; + dst += src_end - src; + goto finish; + } + + while (1) + { + int c, bytes; + + ONE_MORE_CHAR (c, bytes, format); + + if ((c >= 0xD800 && c < 0xE000) || c >= 0x110000) + break; + CHECK_DST (bytes); + dst += CHAR_STRING (c, dst); + src += bytes; + nchars++; + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + + +/* Staffs for coding-systems of type MCODING_TYPE_UTF (16 & 32). */ + +enum utf_bom + { + UTF_BOM_MAYBE, + UTF_BOM_NO, + UTF_BOM_YES, + UTF_BOM_MAX + }; + +enum utf_endian + { + UTF_BIG_ENDIAN, + UTF_LITTLE_ENDIAN, + UTF_ENDIAN_MAX + }; + +struct utf_status +{ + int surrogate; + enum utf_bom bom; + enum utf_endian endian; +}; + +static int +setup_coding_utf (MCodingSystem *coding) +{ + MCodingInfoUTF *info = (MCodingInfoUTF *) (coding->extra_info); + MCodingInfoUTF *spec; + + if (info->code_unit_bits == 8) + coding->ascii_compatible = 1; + else if (info->code_unit_bits == 16 + || info->code_unit_bits == 32) + { + if (info->bom < 0 || info->bom > 2 + || info->endian < 0 || info->endian > 1) + MERROR (MERROR_CODING, -1); + } + else + return -1; + + MSTRUCT_CALLOC (spec, MERROR_CODING); + *spec = *info; + coding->extra_spec = (void *) (spec); + return 0; +} + +static int +reset_coding_utf (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + struct utf_status *status = (struct utf_status *) &(converter->status); + + if (! coding->ready + && setup_coding_utf (coding) < 0) + return -1; + coding->ready = 1; + + status->surrogate = 0; + status->bom = ((MCodingInfoUTF *) (coding->extra_spec))->bom; + status->endian = ((MCodingInfoUTF *) (coding->extra_spec))->endian; + return 0; +} + +static int +decode_coding_utf_16 (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + struct utf_status *status = (struct utf_status *) &(converter->status); + unsigned char b1, b2; + MCharset *charset = NULL; + int error = 0; + + if (status->bom != UTF_BOM_NO) + { + int c; + + ONE_MORE_BASE_BYTE (b1); + ONE_MORE_BYTE (b2); + c = (b1 << 8) | b2; + if (c == 0xFEFF) + status->endian = UTF_BIG_ENDIAN; + else if (c == 0xFFFE) + status->endian = UTF_LITTLE_ENDIAN; + else if (status->bom == UTF_BOM_MAYBE + || converter->lenient) + { + status->endian = UTF_BIG_ENDIAN; + REWIND_SRC_TO_BASE (); + } + else + { + error = 1; + goto source_end; + } + status->bom = UTF_BOM_NO; + } + + while (1) + { + int c, c1; + MCharset *this_charset = NULL; + + ONE_MORE_BASE_BYTE (b1); + ONE_MORE_BYTE (b2); + if (status->endian == UTF_BIG_ENDIAN) + c = ((b1 << 8) | b2); + else + c = ((b2 << 8) | b1); + if (c < 0xD800 || c >= 0xE000) + goto emit_char; + else if (c < 0xDC00) + { + ONE_MORE_BYTE (b1); + ONE_MORE_BYTE (b2); + if (status->endian == UTF_BIG_ENDIAN) + c1 = ((b1 << 8) | b2); + else + c1 = ((b2 << 8) | b1); + if (c1 < 0xDC00 || c1 >= 0xE000) + goto invalid_byte; + c = 0x10000 + ((c - 0xD800) << 10) + (c1 - 0xDC00); + goto emit_char; + } + + invalid_byte: + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + ONE_MORE_BYTE (b1); + ONE_MORE_BYTE (b2); + if (status->endian == UTF_BIG_ENDIAN) + c = ((b1 << 8) | b2); + else + c = ((b2 << 8) | b1); + this_charset = mcharset__binary; + + emit_char: + if (this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c); + } + /* We reach here because of an invalid byte. */ + error = 1; + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); +} + + +static int +decode_coding_utf_32 (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + struct utf_status *status = (struct utf_status *) &(converter->status); + unsigned char b1, b2, b3, b4; + MCharset *charset = NULL; + int error = 0; + + if (status->bom != UTF_BOM_NO) + { + unsigned c; + + ONE_MORE_BASE_BYTE (b1); + ONE_MORE_BYTE (b2); + ONE_MORE_BYTE (b3); + ONE_MORE_BYTE (b4); + c = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + if (c == 0x0000FEFF) + status->endian = UTF_BIG_ENDIAN; + else if (c == 0xFFFE0000) + status->endian = UTF_LITTLE_ENDIAN; + else if (status->bom == UTF_BOM_MAYBE + || converter->lenient) + { + status->endian = UTF_BIG_ENDIAN; + REWIND_SRC_TO_BASE (); + } + else + { + error = 1; + goto source_end; + } + status->bom = UTF_BOM_NO; + } + + while (1) + { + unsigned c; + MCharset *this_charset = NULL; + + ONE_MORE_BASE_BYTE (b1); + ONE_MORE_BYTE (b2); + ONE_MORE_BYTE (b3); + ONE_MORE_BYTE (b4); + if (status->endian == UTF_BIG_ENDIAN) + c = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + else + c = (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + if (c < 0xD800 || (c >= 0xE000 && c < 0x110000)) + goto emit_char; + + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + ONE_MORE_BYTE (c); + this_charset = mcharset__binary; + + emit_char: + if (this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c); + } + /* We reach here because of an invalid byte. */ + error = 1; + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); +} + + +static int +encode_coding_utf_16 (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + struct utf_status *status = (struct utf_status *) &(converter->status); + int big_endian = status->endian == UTF_BIG_ENDIAN; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + + if (status->bom != UTF_BOM_NO) + { + CHECK_DST (2); + if (big_endian) + *dst++ = 0xFE, *dst++ = 0xFF; + else + *dst++ = 0xFF, *dst++ = 0xFE; + status->bom = UTF_BOM_NO; + } + + while (1) + { + int c, bytes; + + ONE_MORE_CHAR (c, bytes, format); + + if (c < 0xD800 || (c >= 0xE000 && c < 0x10000)) + { + CHECK_DST (2); + if (big_endian) + *dst++ = c >> 8, *dst++ = c & 0xFF; + else + *dst++ = c & 0xFF, *dst++ = c >> 8; + } + else if (c >= 0x10000 && c < 0x110000) + { + int c1, c2; + + CHECK_DST (4); + c -= 0x10000; + c1 = (c >> 10) + 0xD800; + c2 = (c & 0x3FF) + 0xDC00; + if (big_endian) + *dst++ = c1 >> 8, *dst++ = c1 & 0xFF, + *dst++ = c2 >> 8, *dst++ = c2 & 0xFF; + else + *dst++ = c1 & 0xFF, *dst++ = c1 >> 8, + *dst++ = c2 & 0xFF, *dst++ = c2 >> 8; + } + else + { + unsigned char buf[11]; + int len, i; + + if (! converter->lenient) + break; + len = encode_unsupporeted_char (c, buf, buf + (dst_end - dst), + mt, from + nchars); + if (len == 0) + goto insufficient_destination; + if (big_endian) + for (i = 0; i < len; i++) + *dst++ = 0, *dst++ = buf[i]; + else + for (i = 0; i < len; i++) + *dst++ = buf[i], *dst++ = 0; + } + src += bytes; + nchars++; + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + +static int +encode_coding_utf_32 (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + struct utf_status *status = (struct utf_status *) &(converter->status); + int big_endian = status->endian == UTF_BIG_ENDIAN; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + + if (status->bom != UTF_BOM_NO) + { + CHECK_DST (4); + if (big_endian) + *dst++ = 0x00, *dst++ = 0x00, *dst++ = 0xFE, *dst++ = 0xFF; + else + *dst++ = 0xFF, *dst++ = 0xFE, *dst++ = 0x00, *dst++ = 0x00; + status->bom = UTF_BOM_NO; + } + + while (1) + { + int c, bytes; + + ONE_MORE_CHAR (c, bytes, format); + + if (c < 0xD800 || (c >= 0xE000 && c < 0x110000)) + { + CHECK_DST (4); + if (big_endian) + *dst++ = 0x00, *dst++ = c >> 16, + *dst++ = (c >> 8) & 0xFF, *dst++ = c & 0xFF; + else + *dst++ = c & 0xFF, *dst++ = (c >> 8) & 0xFF, + *dst++ = c >> 16, *dst++ = 0x00; + } + else + { + unsigned char buf[11]; + int len, i; + + if (! converter->lenient) + break; + len = encode_unsupporeted_char (c, buf, buf + (dst_end - dst), + mt, from + nchars); + if (len == 0) + goto insufficient_destination; + if (big_endian) + for (i = 0; i < len; i++) + *dst++ = 0, *dst++ = buf[i]; + else + for (i = 0; i < len; i++) + *dst++ = buf[i], *dst++ = 0; + } + src += bytes; + nchars++; + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + + +/* Staffs for coding-systems of type MCODING_TYPE_ISO_2022. */ + +#define ISO_CODE_STX 0x02 /* start text */ +#define ISO_CODE_SO 0x0E /* shift-out */ +#define ISO_CODE_SI 0x0F /* shift-in */ +#define ISO_CODE_SS2_7 0x19 /* single-shift-2 for 7-bit code */ +#define ISO_CODE_ESC 0x1B /* escape */ +#define ISO_CODE_SS2 0x8E /* single-shift-2 */ +#define ISO_CODE_SS3 0x8F /* single-shift-3 */ + +/** Structure pointed by MCodingSystem.extra_spec. */ + +struct iso_2022_spec +{ + unsigned flags; + + /** Initial graphic registers (0..3) invoked to each graphic + plane left and right. */ + int initial_invocation[2]; + + /** Initially designated charsets for each graphic register. */ + MCharset *initial_designation[4]; + + int n_designations; + char *designations; + + int use_esc; +}; + +struct iso_2022_status +{ + int invocation[2]; + MCharset *designation[4]; + unsigned single_shifting : 1; + unsigned bol : 1; + unsigned r2l : 1; + unsigned utf8_shifting : 1; + MCharset *non_standard_charset; + int non_standard_charset_bytes; + int non_standard_encoding; +}; + +enum iso_2022_code_class { + ISO_control_0, /* Control codes in the range + 0x00..0x1F and 0x7F, except for the + following 4 codes. */ + ISO_shift_out, /* ISO_CODE_SO (0x0E) */ + ISO_shift_in, /* ISO_CODE_SI (0x0F) */ + ISO_single_shift_2_7, /* ISO_CODE_SS2_7 (0x19) */ + ISO_escape, /* ISO_CODE_SO (0x1B) */ + ISO_control_1, /* Control codes in the range + 0x80..0x9F, except for the + following 3 codes. */ + ISO_single_shift_2, /* ISO_CODE_SS2 (0x8E) */ + ISO_single_shift_3, /* ISO_CODE_SS3 (0x8F) */ + ISO_control_sequence_introducer, /* ISO_CODE_CSI (0x9B) */ + ISO_0x20_or_0x7F, /* Codes of the values 0x20 or 0x7F. */ + ISO_graphic_plane_0, /* Graphic codes in the range 0x21..0x7E. */ + ISO_0xA0_or_0xFF, /* Codes of the values 0xA0 or 0xFF. */ + ISO_graphic_plane_1 /* Graphic codes in the range 0xA1..0xFE. */ +} iso_2022_code_class[256]; + + +#define MCODING_ISO_DESIGNATION_MASK \ + (MCODING_ISO_DESIGNATION_G0 \ + | MCODING_ISO_DESIGNATION_G1 \ + | MCODING_ISO_DESIGNATION_CTEXT \ + | MCODING_ISO_DESIGNATION_CTEXT_EXT) + +static int +setup_coding_iso_2022 (MCodingSystem *coding) +{ + MCodingInfoISO2022 *info = (MCodingInfoISO2022 *) (coding->extra_info); + int ncharsets = coding->ncharsets; + struct iso_2022_spec *spec; + int designation_policy = info->flags & MCODING_ISO_DESIGNATION_MASK; + int i; + + coding->ascii_compatible = 0; + + MSTRUCT_CALLOC (spec, MERROR_CODING); + + spec->flags = info->flags; + spec->initial_invocation[0] = info->initial_invocation[0]; + spec->initial_invocation[1] = info->initial_invocation[1]; + for (i = 0; i < 4; i++) + spec->initial_designation[i] = NULL; + if (designation_policy) + { + spec->n_designations = ncharsets; + if (spec->flags & MCODING_ISO_FULL_SUPPORT) + spec->n_designations += mcharset__iso_2022_table.used; + MTABLE_CALLOC (spec->designations, spec->n_designations, MERROR_CODING); + for (i = 0; i < spec->n_designations; i++) + spec->designations[i] = -1; + } + else + { + if (spec->flags & MCODING_ISO_FULL_SUPPORT) + MERROR (MERROR_CODING, -1); + spec->designations = NULL; + } + + for (i = 0; i < ncharsets; i++) + { + int reg = info->designations[i]; + + if (reg != -5 + && coding->charsets[i]->final_byte > 0 + && (reg < -4 || reg > 3)) + MERROR (MERROR_CODING, -1); + if (reg >= 0) + { + if (spec->initial_designation[reg]) + MERROR (MERROR_CODING, -1); + spec->initial_designation[reg] = coding->charsets[i]; + } + else if (reg >= -4) + { + if (! designation_policy + && ! (spec->flags & MCODING_ISO_EUC_TW_SHIFT)) + MERROR (MERROR_CODING, -1); + reg += 4; + } + + if (designation_policy) + spec->designations[i] = reg; + if (coding->charsets[i] == mcharset__ascii) + coding->ascii_compatible = 1; + } + + if (coding->ascii_compatible + && (spec->flags & (MCODING_ISO_DESIGNATION_G0 + | MCODING_ISO_DESIGNATION_CTEXT + | MCODING_ISO_DESIGNATION_CTEXT_EXT + | MCODING_ISO_LOCKING_SHIFT))) + coding->ascii_compatible = 0; + + if (spec->flags & MCODING_ISO_FULL_SUPPORT) + for (i = 0; i < mcharset__iso_2022_table.used; i++) + { + MCharset *charset = mcharset__iso_2022_table.charsets[i]; + + spec->designations[ncharsets + i] + = ((designation_policy == MCODING_ISO_DESIGNATION_CTEXT + || designation_policy == MCODING_ISO_DESIGNATION_CTEXT_EXT) + ? (charset->code_range[0] == 32 + || charset->code_range[1] == 255) + : designation_policy == MCODING_ISO_DESIGNATION_G1); + } + + spec->use_esc = ((spec->flags & MCODING_ISO_DESIGNATION_MASK) + || ((spec->flags & MCODING_ISO_LOCKING_SHIFT) + && (spec->initial_designation[2] + || spec->initial_designation[3])) + || (! (spec->flags & MCODING_ISO_EIGHT_BIT) + && (spec->flags & MCODING_ISO_SINGLE_SHIFT)) + || (spec->flags & MCODING_ISO_ISO6429)); + + coding->extra_spec = (void *) spec; + + return 0; +} + +static int +reset_coding_iso_2022 (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + struct iso_2022_status *status + = (struct iso_2022_status *) &(converter->status); + struct iso_2022_spec *spec; + int i; + + if (! coding->ready + && setup_coding_iso_2022 (coding) < 0) + return -1; + coding->ready = 1; + + spec = (struct iso_2022_spec *) coding->extra_spec; + status->invocation[0] = spec->initial_invocation[0]; + status->invocation[1] = spec->initial_invocation[1]; + for (i = 0; i < 4; i++) + status->designation[i] = spec->initial_designation[i]; + status->single_shifting = 0; + status->bol = 1; + status->r2l = 0; + + return 0; +} + +#define ISO2022_DECODE_DESIGNATION(reg, dim, chars, final, rev) \ + do { \ + MCharset *charset; \ + \ + if ((final) < '0' || (final) >= 128) \ + goto invalid_byte; \ + if (rev < 0) \ + { \ + charset = MCHARSET_ISO_2022 ((dim), (chars), (final)); \ + if (! (spec->flags & MCODING_ISO_FULL_SUPPORT)) \ + { \ + int i; \ + \ + for (i = 0; i < coding->ncharsets; i++) \ + if (charset == coding->charsets[i]) \ + break; \ + if (i == coding->ncharsets) \ + goto invalid_byte; \ + } \ + } \ + else \ + { \ + int i; \ + \ + for (i = 0; i < mcharset__iso_2022_table.used; i++) \ + { \ + charset = mcharset__iso_2022_table.charsets[i]; \ + if (charset->revision == (rev) \ + && charset->dimension == (dim) \ + && charset->final_byte == (final) \ + && (charset->code_range[1] == (chars) \ + || ((chars) == 96 && charset->code_range[1] == 255))) \ + break; \ + } \ + if (i == mcharset__iso_2022_table.used) \ + goto invalid_byte; \ + } \ + status->designation[reg] = charset; \ + } while (0) + + +static MCharset * +find_ctext_non_standard_charset (char *charset_name) +{ + MCharset *charset; + + if (! strcmp (charset_name, "koi8-r")) + charset = MCHARSET (msymbol ("koi8-r")); + else if (! strcmp (charset_name, "big5-0")) + charset = MCHARSET (msymbol ("big5")); + else + charset = NULL; + return charset; +} + +static int +decode_coding_iso_2022 (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + struct iso_2022_spec *spec = (struct iso_2022_spec *) coding->extra_spec; + struct iso_2022_status *status + = (struct iso_2022_status *) &(converter->status); + MCharset *charset0, *charset1, *charset; + int error = 0; + MCharset *cns_charsets[15]; + + charset0 = (status->invocation[0] >= 0 + ? status->designation[status->invocation[0]] : NULL); + charset1 = (status->invocation[1] >= 0 + ? status->designation[status->invocation[1]] : NULL); + charset = mcharset__ascii; + + if (spec->flags & MCODING_ISO_EUC_TW_SHIFT) + { + int i; + + memset (cns_charsets, 0, sizeof (cns_charsets)); + for (i = 0; i < coding->ncharsets; i++) + if (coding->charsets[i]->dimension == 2 + && coding->charsets[i]->code_range[1] == 126) + { + int final = coding->charsets[i]->final_byte; + + if (final >= 'G' && final <= 'M') + cns_charsets[final - 'G'] = coding->charsets[i]; + else if (final < 0) + cns_charsets[14] = coding->charsets[i]; + } + } + + while (1) + { + MCharset *this_charset = NULL; + int c1, c2, c3; + + ONE_MORE_BASE_BYTE (c1); + + if (status->utf8_shifting) + { + int buf[6]; + int bytes = CHAR_BYTES_BY_HEAD (c1); + int i; + + buf[0] = c1; + for (i = 1; i < bytes; i++) + { + ONE_MORE_BYTE (c1); + buf[i] = c1; + } + this_charset = UTF8_CHARSET (buf); + c1 = STRING_CHAR_UTF8 (buf); + goto emit_char; + } + + if (status->non_standard_encoding > 0) + { + int i; + + this_charset = status->non_standard_charset; + for (i = 1; i < status->non_standard_charset_bytes; i++) + { + ONE_MORE_BYTE (c2); + c1 = (c1 << 8) | c2; + } + c1 = DECODE_CHAR (this_charset, c1); + goto emit_char; + } + + switch (iso_2022_code_class[c1]) + { + case ISO_graphic_plane_0: + this_charset = charset0; + break; + + case ISO_0x20_or_0x7F: + if (! charset0 + || (charset0->code_range[0] != 32 + && charset0->code_range[1] != 255)) + /* This is SPACE or DEL. */ + this_charset = mcharset__ascii; + else + /* This is a graphic character of plane 0. */ + this_charset = charset0; + break; + + case ISO_graphic_plane_1: + if (!charset1) + goto invalid_byte; + this_charset = charset1; + break; + + case ISO_0xA0_or_0xFF: + if (! charset1 + || charset1->code_range[0] == 33 + || ! (spec->flags & MCODING_ISO_EIGHT_BIT)) + goto invalid_byte; + /* This is a graphic character of plane 1. */ + if (! charset1) + goto invalid_byte; + this_charset = charset1; + break; + + case ISO_control_0: + this_charset = mcharset__ascii; + break; + + case ISO_control_1: + goto invalid_byte; + + case ISO_shift_out: + if ((spec->flags & MCODING_ISO_LOCKING_SHIFT) + && status->designation[1]) + { + status->invocation[0] = 1; + charset0 = status->designation[1]; + continue; + } + this_charset = mcharset__ascii; + break; + + case ISO_shift_in: + if (spec->flags & MCODING_ISO_LOCKING_SHIFT) + { + status->invocation[0] = 0; + charset0 = status->designation[0]; + continue; + } + this_charset = mcharset__ascii; + break; + + case ISO_single_shift_2_7: + if (! (spec->flags & MCODING_ISO_SINGLE_SHIFT_7)) + { + this_charset = mcharset__ascii; + break; + } + c1 = 'N'; + goto label_escape_sequence; + + case ISO_single_shift_2: + if (spec->flags & MCODING_ISO_EUC_TW_SHIFT) + { + ONE_MORE_BYTE (c1); + if (c1 < 0xA1 || (c1 > 0xA7 && c1 < 0xAF) || c1 > 0xAF + || ! cns_charsets[c1 - 0xA1]) + goto invalid_byte; + status->designation[2] = cns_charsets[c1 - 0xA1]; + } + else if (! (spec->flags & MCODING_ISO_SINGLE_SHIFT)) + goto invalid_byte; + /* SS2 is handled as an escape sequence of ESC 'N' */ + c1 = 'N'; + goto label_escape_sequence; + + case ISO_single_shift_3: + if (! (spec->flags & MCODING_ISO_SINGLE_SHIFT)) + goto invalid_byte; + /* SS2 is handled as an escape sequence of ESC 'O' */ + c1 = 'O'; + goto label_escape_sequence; + + case ISO_control_sequence_introducer: + /* CSI is handled as an escape sequence of ESC '[' ... */ + c1 = '['; + goto label_escape_sequence; + + case ISO_escape: + if (! spec->use_esc) + { + this_charset = mcharset__ascii; + break; + } + ONE_MORE_BYTE (c1); + label_escape_sequence: + /* Escape sequences handled here are invocation, + designation, and direction specification. */ + switch (c1) + { + case '&': /* revision of following character set */ + if (! (spec->flags & MCODING_ISO_DESIGNATION_MASK)) + goto unused_escape_sequence; + ONE_MORE_BYTE (c1); + if (c1 < '@' || c1 > '~') + goto invalid_byte; + ONE_MORE_BYTE (c1); + if (c1 != ISO_CODE_ESC) + goto invalid_byte; + ONE_MORE_BYTE (c1); + goto label_escape_sequence; + + case '$': /* designation of 2-byte character set */ + if (! (spec->flags & MCODING_ISO_DESIGNATION_MASK)) + goto unused_escape_sequence; + ONE_MORE_BYTE (c1); + if (c1 >= '@' && c1 <= 'B') + { /* designation of JISX0208.1978, GB2312.1980, or + JISX0208.1980 */ + ISO2022_DECODE_DESIGNATION (0, 2, 94, c1, -1); + } + else if (c1 >= 0x28 && c1 <= 0x2B) + { /* designation of (dimension 2, chars 94) character set */ + ONE_MORE_BYTE (c2); + ISO2022_DECODE_DESIGNATION (c1 - 0x28, 2, 94, c2, -1); + } + else if (c1 >= 0x2C && c1 <= 0x2F) + { /* designation of (dimension 2, chars 96) character set */ + ONE_MORE_BYTE (c2); + ISO2022_DECODE_DESIGNATION (c1 - 0x2C, 2, 96, c2, -1); + } + else + goto invalid_byte; + /* We must update these variables now. */ + charset0 = status->designation[status->invocation[0]]; + charset1 = status->designation[status->invocation[1]]; + continue; + + case 'n': /* invocation of locking-shift-2 */ + if (! (spec->flags & MCODING_ISO_LOCKING_SHIFT) + || ! status->designation[2]) + goto invalid_byte; + status->invocation[0] = 2; + charset0 = status->designation[2]; + continue; + + case 'o': /* invocation of locking-shift-3 */ + if (! (spec->flags & MCODING_ISO_LOCKING_SHIFT) + || ! status->designation[3]) + goto invalid_byte; + status->invocation[0] = 3; + charset0 = status->designation[3]; + continue; + + case 'N': /* invocation of single-shift-2 */ + if (! ((spec->flags & MCODING_ISO_SINGLE_SHIFT) + || (spec->flags & MCODING_ISO_EUC_TW_SHIFT)) + || ! status->designation[2]) + goto invalid_byte; + this_charset = status->designation[2]; + ONE_MORE_BYTE (c1); + if (c1 < 0x20 || (c1 >= 0x80 && c1 < 0xA0)) + goto invalid_byte; + break; + + case 'O': /* invocation of single-shift-3 */ + if (! (spec->flags & MCODING_ISO_SINGLE_SHIFT) + || ! status->designation[3]) + goto invalid_byte; + this_charset = status->designation[3]; + ONE_MORE_BYTE (c1); + if (c1 < 0x20 || (c1 >= 0x80 && c1 < 0xA0)) + goto invalid_byte; + break; + + case '[': /* specification of direction */ + if (! (spec->flags & MCODING_ISO_ISO6429)) + goto invalid_byte; + /* For the moment, nested direction is not supported. + So, (coding->mode & CODING_MODE_DIRECTION) zero means + left-to-right, and nonzero means right-to-left. */ + ONE_MORE_BYTE (c1); + switch (c1) + { + case ']': /* end of the current direction */ + case '0': /* end of the current direction */ + status->r2l = 0; + break; + + case '1': /* start of left-to-right direction */ + ONE_MORE_BYTE (c1); + if (c1 != ']') + goto invalid_byte; + status->r2l = 0; + break; + + case '2': /* start of right-to-left direction */ + ONE_MORE_BYTE (c1); + if (c1 != ']') + goto invalid_byte; + status->r2l = 1; + break; + + default: + goto invalid_byte; + } + continue; + + case '%': + { + char charset_name[16]; + int bytes; + int i; + + if (! spec->flags & MCODING_ISO_DESIGNATION_CTEXT_EXT) + goto invalid_byte; + /* Compound-text uses these escape sequences: + + ESC % G -- utf-8 bytes -- ESC % @ + ESC % / 1 M L -- charset name -- STX -- bytes -- + ESC % / 2 M L -- charset name -- STX -- bytes -- + ESC % / 3 M L -- charset name -- STX -- bytes -- + ESC % / 4 M L -- charset name -- STX -- bytes -- + + It also uses this sequence but that is not yet + supported here. + + ESC % / 0 M L -- charset name -- STX -- bytes -- */ + + ONE_MORE_BYTE (c1); + if (c1 == 'G') + { + status->utf8_shifting = 1; + continue; + } + if (c1 == '@') + { + if (! status->utf8_shifting) + goto invalid_byte; + status->utf8_shifting = 0; + continue; + } + if (c1 != '/') + goto invalid_byte; + ONE_MORE_BYTE (c1); + if (c1 < '1' || c1 > '4') + goto invalid_byte; + status->non_standard_charset_bytes = c1 - '0'; + ONE_MORE_BYTE (c1); + ONE_MORE_BYTE (c2); + if (c1 < 128 || c2 < 128) + goto invalid_byte; + bytes = (c1 - 128) * 128 + (c2 - 128); + for (i = 0; i < 16; i++) + { + ONE_MORE_BYTE (c1); + if (c1 == ISO_CODE_STX) + break; + charset_name[i] = TOLOWER (c1); + } + if (i == 16) + goto invalid_byte; + charset_name[i++] = '\0'; + this_charset = find_ctext_non_standard_charset (charset_name); + if (! this_charset) + goto invalid_byte; + status->non_standard_charset = this_charset; + status->non_standard_encoding = bytes - i; + continue; + } + + default: + if (! (spec->flags & MCODING_ISO_DESIGNATION_MASK)) + goto unused_escape_sequence; + if (c1 >= 0x28 && c1 <= 0x2B) + { /* designation of (dimension 1, chars 94) charset */ + ONE_MORE_BYTE (c2); + ISO2022_DECODE_DESIGNATION (c1 - 0x28, 1, 94, c2, -1); + } + else if (c1 >= 0x2C && c1 <= 0x2F) + { /* designation of (dimension 1, chars 96) charset */ + ONE_MORE_BYTE (c2); + ISO2022_DECODE_DESIGNATION (c1 - 0x2C, 1, 96, c2, -1); + } + else + goto invalid_byte; + /* We must update these variables now. */ + charset0 = status->designation[status->invocation[0]]; + charset1 = status->designation[status->invocation[1]]; + continue; + + unused_escape_sequence: + UNGET_ONE_BYTE (c1); + c1 = ISO_CODE_ESC; + this_charset = mcharset__ascii; + } + } + + if (this_charset->dimension == 1) + { + if (this_charset->code_range[1] <= 128) + c1 &= 0x7F; + } + else if (this_charset->dimension == 2) + { + ONE_MORE_BYTE (c2); + c1 = ((c1 & 0x7F) << 8) | (c2 & 0x7F); + } + else /* i.e. (dimension == 3) */ + { + ONE_MORE_BYTE (c2); + ONE_MORE_BYTE (c3); + c1 = ((c1 & 0x7F) << 16) | ((c2 & 0x7F) << 8) | (c3 & 0x7F); + } + c1 = DECODE_CHAR (this_charset, c1); + goto emit_char; + + invalid_byte: + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + c1 = *src++; + this_charset = mcharset__binary; + + emit_char: + if (this_charset != mcharset__ascii + && this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c1); + if (status->non_standard_encoding > 0) + status->non_standard_encoding -= status->non_standard_charset_bytes; + } + /* We reach here because of an invalid byte. */ + error = 1; + + + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); + +} + +/* Produce codes (escape sequence) for designating CHARSET to graphic + register REG at DST, and increment DST. If CHARSET->final-char is + '@', 'A', or 'B' and SHORT_FORM is nonzero, produce designation + sequence of short-form. Update STATUS->designation. */ + +#define ISO2022_ENCODE_DESIGNATION(reg, charset, spec, status) \ + do { \ + char *intermediate_char_94 = "()*+"; \ + char *intermediate_char_96 = ",-./"; \ + \ + if (dst + 4 > dst_end) \ + goto memory_shortage; \ + *dst++ = ISO_CODE_ESC; \ + if (charset->dimension == 1) \ + { \ + if (charset->code_range[0] != 32 \ + && charset->code_range[1] != 255) \ + *dst++ = (unsigned char) (intermediate_char_94[reg]); \ + else \ + *dst++ = (unsigned char) (intermediate_char_96[reg]); \ + } \ + else \ + { \ + *dst++ = '$'; \ + if (charset->code_range[0] != 32 \ + && charset->code_range[1] != 255) \ + { \ + if (spec->flags & MCODING_ISO_LONG_FORM \ + || reg != 0 \ + || charset->final_byte < '@' || charset->final_byte > 'B') \ + *dst++ = (unsigned char) (intermediate_char_94[reg]); \ + } \ + else \ + *dst++ = (unsigned char) (intermediate_char_96[reg]); \ + } \ + *dst++ = charset->final_byte; \ + \ + status->designation[reg] = charset; \ + } while (0) + + +/* The following two macros produce codes (control character or escape + sequence) for ISO-2022 single-shift functions (single-shift-2 and + single-shift-3). */ + +#define ISO2022_ENCODE_SINGLE_SHIFT_2(spec, status) \ + do { \ + if (dst + 2 > dst_end) \ + goto memory_shortage; \ + if (! (spec->flags & MCODING_ISO_EIGHT_BIT)) \ + *dst++ = ISO_CODE_ESC, *dst++ = 'N'; \ + else \ + *dst++ = ISO_CODE_SS2; \ + status->single_shifting = 1; \ + } while (0) + + +#define ISO2022_ENCODE_SINGLE_SHIFT_3(spec, status) \ + do { \ + if (dst + 2 > dst_end) \ + goto memory_shortage; \ + if (! (spec->flags & MCODING_ISO_EIGHT_BIT)) \ + *dst++ = ISO_CODE_ESC, *dst++ = 'O'; \ + else \ + *dst++ = ISO_CODE_SS3; \ + status->single_shifting = 1; \ + } while (0) + + +/* The following four macros produce codes (control character or + escape sequence) for ISO-2022 locking-shift functions (shift-in, + shift-out, locking-shift-2, and locking-shift-3). */ + +#define ISO2022_ENCODE_SHIFT_IN(status) \ + do { \ + if (dst + 1 > dst_end) \ + goto memory_shortage; \ + *dst++ = ISO_CODE_SI; \ + status->invocation[0] = 0; \ + } while (0) + + +#define ISO2022_ENCODE_SHIFT_OUT(status) \ + do { \ + if (dst + 1 > dst_end) \ + goto memory_shortage; \ + *dst++ = ISO_CODE_SO; \ + status->invocation[0] = 1; \ + } while (0) + + +#define ISO2022_ENCODE_LOCKING_SHIFT_2(status) \ + do { \ + if (dst + 2 > dst_end) \ + goto memory_shortage; \ + *dst++ = ISO_CODE_ESC, *dst++ = 'n'; \ + status->invocation[0] = 2; \ + } while (0) + + +#define ISO2022_ENCODE_LOCKING_SHIFT_3(status) \ + do { \ + if (dst + 2 > dst_end) \ + goto memory_shortage; \ + *dst++ = ISO_CODE_ESC, *dst++ = 'o'; \ + status->invocation[0] = 3; \ + } while (0) + +#define ISO2022_ENCODE_UTF8_SHIFT_START(len) \ + do { \ + CHECK_DST (3 + len); \ + *dst++ = ISO_CODE_ESC; \ + *dst++ = '%'; \ + *dst++ = 'G'; \ + status->utf8_shifting = 1; \ + } while (0) + + +#define ISO2022_ENCODE_UTF8_SHIFT_END() \ + do { \ + CHECK_DST (3); \ + *dst++ = ISO_CODE_ESC; \ + *dst++ = '%'; \ + *dst++ = '@'; \ + status->utf8_shifting = 0; \ + } while (0) + + +#define ISO2022_ENCODE_NON_STANDARD(name, len) \ + do { \ + CHECK_DST (6 + len + 1 + non_standard_charset_bytes); \ + non_standard_begin = dst; \ + *dst++ = ISO_CODE_ESC; \ + *dst++ = '%'; \ + *dst++ = '/'; \ + *dst++ = '0' + non_standard_charset_bytes; \ + *dst++ = 0, *dst++ = 0; /* filled later */ \ + memcpy (dst, name, len); \ + dst += len; \ + *dst++ = ISO_CODE_STX; \ + non_standard_bytes = len + 1; \ + } while (0) + + +static char * +find_ctext_non_standard_name (MCharset *charset, int *bytes) +{ + char *name = msymbol_name (charset->name); + + if (! strcmp (name, "koi8-r")) + *bytes = 1; + else if (! strcmp (name, "big5")) + name = "big5-0", *bytes = 2; + else + return NULL; + return name; +} + +/* Designate CHARSET to a graphic register specified in + SPEC->designation. If the register is not yet invoked to graphic + left not right, invoke it to graphic left. DSTP points to a + variable containing a memory address where the output must go. + DST_END is the limit of that memory. + + Return 0 if it succeeds. Return -1 otherwise, which means that the + memory area is too short. By side effect, update the variable that + DSTP points to. */ + +static int +iso_2022_designate_invoke_charset (MCodingSystem *coding, + MCharset *charset, + struct iso_2022_spec *spec, + struct iso_2022_status *status, + unsigned char **dstp, + unsigned char *dst_end) +{ + int i; + unsigned char *dst = *dstp; + + for (i = 0; i < 4; i++) + if (charset == status->designation[i]) + break; + + if (i >= 4) + { + /* CHARSET is not yet designated to any graphic registers. */ + for (i = 0; i < coding->ncharsets; i++) + if (charset == coding->charsets[i]) + break; + if (i == coding->ncharsets) + { + for (i = 0; i < mcharset__iso_2022_table.used; i++) + if (charset == mcharset__iso_2022_table.charsets[i]) + break; + i += coding->ncharsets; + } + i = spec->designations[i]; + ISO2022_ENCODE_DESIGNATION (i, charset, spec, status); + } + + if (status->invocation[0] != i + && status->invocation[1] != i) + { + /* Graphic register I is not yet invoked. */ + switch (i) + { + case 0: /* graphic register 0 */ + ISO2022_ENCODE_SHIFT_IN (status); + break; + + case 1: /* graphic register 1 */ + ISO2022_ENCODE_SHIFT_OUT (status); + break; + + case 2: /* graphic register 2 */ + if (spec->flags & MCODING_ISO_SINGLE_SHIFT) + ISO2022_ENCODE_SINGLE_SHIFT_2 (spec, status); + else + ISO2022_ENCODE_LOCKING_SHIFT_2 (status); + break; + + case 3: /* graphic register 3 */ + if (spec->flags & MCODING_ISO_SINGLE_SHIFT) + ISO2022_ENCODE_SINGLE_SHIFT_3 (spec, status); + else + ISO2022_ENCODE_LOCKING_SHIFT_3 (status); + break; + } + } + *dstp = dst; + return 0; + + memory_shortage: + *dstp = dst; + return -1; +} + + +/* Reset the invocation/designation status to the initial one. SPEC + and STATUS contain information about the current and initial + invocation /designation status respectively. DSTP points to a + variable containing a memory address where the output must go. + DST_END is the limit of that memory. + + Return 0 if it succeeds. Return -1 otherwise, which means that the + memory area is too short. By side effect, update the variable that + DSTP points to. */ + +static int +iso_2022_reset_invocation_designation (struct iso_2022_spec *spec, + struct iso_2022_status *status, + unsigned char **dstp, + unsigned char *dst_end) +{ + unsigned char *dst = *dstp; + int i; + + /* Reset the invocation status of GL. We have not yet supported GR + invocation. */ + if (status->invocation[0] != spec->initial_invocation[0] + && spec->initial_invocation[0] >= 0) + { + if (spec->initial_invocation[0] == 0) + ISO2022_ENCODE_SHIFT_IN (status); + else if (spec->initial_invocation[0] == 1) + ISO2022_ENCODE_SHIFT_OUT (status); + else if (spec->initial_invocation[0] == 2) + ISO2022_ENCODE_LOCKING_SHIFT_2 (status); + else /* i.e. spec->initial_invocation[0] == 3 */ + ISO2022_ENCODE_LOCKING_SHIFT_3 (status); + } + + /* Reset the designation status of G0..G3. */ + for (i = 0; i < 4; i++) + if (status->designation[i] != spec->initial_designation[i] + && spec->initial_designation[i]) + { + MCharset *charset = spec->initial_designation[i]; + + ISO2022_ENCODE_DESIGNATION (i, charset, spec, status); + } + + *dstp = dst; + return 0; + + memory_shortage: + *dstp = dst; + return -1; +} + + +static int +encode_coding_iso_2022 (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + unsigned char *dst_base; + struct iso_2022_spec *spec = (struct iso_2022_spec *) coding->extra_spec; + int full_support = spec->flags & MCODING_ISO_FULL_SUPPORT; + struct iso_2022_status *status + = (struct iso_2022_status *) &(converter->status); + MCharset *primary, *charset0, *charset1; + int next_primary_change; + int ncharsets = coding->ncharsets; + MCharset **charsets = coding->charsets; + MCharset *cns_charsets[15]; + int ascii_compatible = coding->ascii_compatible; + MCharset *non_standard_charset = NULL; + int non_standard_charset_bytes = 0; + int non_standard_bytes = 0; + unsigned char *non_standard_begin = NULL; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + + if (spec->flags & MCODING_ISO_EUC_TW_SHIFT) + { + int i; + + memset (cns_charsets, 0, sizeof (cns_charsets)); + for (i = 0; i < ncharsets; i++) + if (charsets[i]->dimension == 2) + { + int final = charsets[i]->final_byte; + + if (final >= 'G' && final <= 'M') + cns_charsets[final - 'G'] = charsets[i]; + else if (final < 0) + cns_charsets[14] = charsets[i]; + } + } + + next_primary_change = from; + primary = NULL; + charset0 = status->designation[status->invocation[0]]; + charset1 = (status->invocation[1] < 0 ? NULL + : status->designation[status->invocation[1]]); + + while (1) + { + int bytes, c; + + dst_base = dst; + ONE_MORE_CHAR (c, bytes, format); + + if (c < 128 && ascii_compatible) + { + if (status->utf8_shifting) + ISO2022_ENCODE_UTF8_SHIFT_END (); + CHECK_DST (1); + *dst++ = c; + } + else if (c <= 32 || c == 127) + { + if (status->utf8_shifting) + ISO2022_ENCODE_UTF8_SHIFT_END (); + if (spec->flags & MCODING_ISO_RESET_AT_CNTL + || (c == '\n' && spec->flags & MCODING_ISO_RESET_AT_EOL)) + { + if (iso_2022_reset_invocation_designation (spec, status, + &dst, dst_end) < 0) + goto insufficient_destination; + charset0 = status->designation[status->invocation[0]]; + charset1 = (status->invocation[1] < 0 ? NULL + : status->designation[status->invocation[1]]); + } + CHECK_DST (1); + *dst++ = c; + } + else + { + unsigned code = MCHAR_INVALID_CODE; + MCharset *charset = NULL; + int gr_mask; + int pos = from + nchars; + + if (pos >= next_primary_change) + { + MSymbol primary_charset + = (MSymbol) mtext_get_prop (mt, pos, Mcharset); + primary = MCHARSET (primary_charset); + if (primary && primary != mcharset__binary) + { + if (primary->final_byte <= 0) + primary = NULL; + else if (! full_support) + { + int i; + + for (i = 0; i < ncharsets; i++) + if (primary == charsets[i]) + break; + if (i == ncharsets) + primary = NULL; + } + } + + mtext_prop_range (mt, Mcharset, pos, + NULL, &next_primary_change, 0); + } + + if (primary && primary != mcharset__binary) + { + code = ENCODE_CHAR (primary, c); + if (code != MCHAR_INVALID_CODE) + charset = primary; + } + if (! charset) + { + if (c <= 32 || c == 127) + { + code = c; + charset = mcharset__ascii; + } + else + { + int i; + + for (i = 0; i < ncharsets; i++) + { + charset = charsets[i]; + code = ENCODE_CHAR (charset, c); + if (code != MCHAR_INVALID_CODE) + break; + } + if (i == ncharsets) + { + if (spec->flags & MCODING_ISO_FULL_SUPPORT) + { + for (i = 0; i < mcharset__iso_2022_table.used; i++) + { + charset = mcharset__iso_2022_table.charsets[i]; + code = ENCODE_CHAR (charset, c); + if (code != MCHAR_INVALID_CODE) + break; + } + if (i == mcharset__iso_2022_table.used) + { + if (spec->flags & MCODING_ISO_DESIGNATION_CTEXT_EXT) + goto unsupported_char; + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + } + } + else + goto unsupported_char; + } + } + } + + if (charset + && (charset->final_byte >= 0 + || spec->flags & MCODING_ISO_EUC_TW_SHIFT)) + { + if (code >= 0x80 && code < 0xA0) + goto unsupported_char; + code &= 0x7F7F7F7F; + if (status->utf8_shifting) + ISO2022_ENCODE_UTF8_SHIFT_END (); + if (charset == charset0) + gr_mask = 0; + else if (charset == charset1) + gr_mask = 0x80; + else + { + unsigned char *p = NULL; + + if (spec->flags & MCODING_ISO_EUC_TW_SHIFT) + { + int i; + + if (cns_charsets[0] == charset) + { + CHECK_DST (2); + } + else + { + for (i = 1; i < 15; i++) + if (cns_charsets[i] == charset) + break; + CHECK_DST (4); + *dst++ = ISO_CODE_SS2; + *dst++ = 0xA1 + i; + } + status->single_shifting = 1; + p = dst; + } + else + { + if (iso_2022_designate_invoke_charset + (coding, charset, spec, status, &dst, dst_end) < 0) + goto insufficient_destination; + charset0 = status->designation[status->invocation[0]]; + charset1 = (status->invocation[1] < 0 ? NULL + : status->designation[status->invocation[1]]); + } + if (status->single_shifting) + gr_mask + = (spec->flags & MCODING_ISO_EIGHT_BIT) ? 0x80 : 0; + else if (charset == charset0) + gr_mask = 0; + else + gr_mask = 0x80; + } + if (charset->dimension == 1) + { + CHECK_DST (1); + *dst++ = code | gr_mask; + } + else if (charset->dimension == 2) + { + CHECK_DST (2); + *dst++ = (code >> 8) | gr_mask; + *dst++ = (code & 0xFF) | gr_mask; + } + else + { + CHECK_DST (3); + *dst++ = (code >> 16) | gr_mask; + *dst++ = ((code >> 8) & 0xFF) | gr_mask; + *dst++ = (code & 0xFF) | gr_mask; + } + status->single_shifting = 0; + } + else if (charset && spec->flags & MCODING_ISO_DESIGNATION_CTEXT_EXT) + { + if (charset != non_standard_charset) + { + char *name = (find_ctext_non_standard_name + (charset, &non_standard_charset_bytes)); + + if (name) + { + int len = strlen (name); + + ISO2022_ENCODE_NON_STANDARD (name, len); + non_standard_charset = charset; + } + else + non_standard_charset = NULL; + } + + if (non_standard_charset) + { + if (dst + non_standard_charset_bytes > dst_end) + goto insufficient_destination; + non_standard_bytes += non_standard_charset_bytes; + non_standard_begin[4] = (non_standard_bytes / 128) | 0x80; + non_standard_begin[5] = (non_standard_bytes % 128) | 0x80; + if (non_standard_charset_bytes == 1) + *dst++ = code; + else if (non_standard_charset_bytes == 2) + *dst++ = code >> 8, *dst++ = code & 0xFF; + else if (non_standard_charset_bytes == 3) + *dst++ = code >> 16, *dst++ = (code >> 8) & 0xFF, + *dst++ = code & 0xFF; + else /* i.e non_standard_charset_bytes == 3 */ + *dst++ = code >> 24, *dst++ = (code >> 16) & 0xFF, + *dst++ = (code >> 8) & 0xFF, *dst++ = code & 0xFF; + } + else + { + int len = CHAR_BYTES (c); + + if (c >= 0x110000) + goto unsupported_char; + if (! status->utf8_shifting) + ISO2022_ENCODE_UTF8_SHIFT_START (len); + else + CHECK_DST (len); + CHAR_STRING (c, dst); + } + } + else + goto unsupported_char; + } + src += bytes; + nchars++; + continue; + + unsupported_char: + { + int len; + + if (iso_2022_designate_invoke_charset (coding, mcharset__ascii, + spec, status, + &dst, dst_end) < 0) + goto insufficient_destination; + if (! converter->lenient) + break; + len = encode_unsupporeted_char (c, dst, dst_end, mt, from + nchars); + if (len == 0) + goto insufficient_destination; + dst += len; + src += bytes; + nchars++; + } + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + dst = dst_base; + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + if (converter->result == MCONVERSION_RESULT_SUCCESS + && converter->last_block) + { + if (status->utf8_shifting) + { + ISO2022_ENCODE_UTF8_SHIFT_END (); + dst_base = dst; + } + if (spec->flags & MCODING_ISO_RESET_AT_EOL + && charset0 != spec->initial_designation[0]) + { + if (iso_2022_reset_invocation_designation (spec, status, + &dst, dst_end) < 0) + goto insufficient_destination; + } + } + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + + +/* Staffs for coding-systems of type MCODING_TYPE_MISC. */ + +/* For SJIS handling... */ + +#define SJIS_TO_JIS(s1, s2) \ + (s2 >= 0x9F \ + ? (((s1 * 2 - (s1 >= 0xE0 ? 0x160 : 0xE0)) << 8) \ + | (s2 - 0x7E)) \ + : (((s1 * 2 - ((s1 >= 0xE0) ? 0x161 : 0xE1)) << 8) \ + | (s2 - ((s2 >= 0x7F) ? 0x20 : 0x1F)))) + +#define JIS_TO_SJIS(c1, c2) \ + ((c1 & 1) \ + ? (((c1 / 2 + ((c1 < 0x5F) ? 0x71 : 0xB1)) << 8) \ + | (c2 + ((c2 >= 0x60) ? 0x20 : 0x1F))) \ + : (((c1 / 2 + ((c1 < 0x5F) ? 0x70 : 0xB0)) << 8) \ + | (c2 + 0x7E))) + + +static int +reset_coding_sjis (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + + if (! coding->ready) + { + MSymbol kanji_sym = msymbol ("jisx0208.1983"); + MCharset *kanji = MCHARSET (kanji_sym); + MSymbol kana_sym = msymbol ("jisx0201-kana"); + MCharset *kana = MCHARSET (kana_sym); + + if (! kanji_sym || ! kana_sym) + return -1; + coding->ncharsets = 3; + coding->charsets[1] = kanji; + coding->charsets[2] = kana; + } + coding->ready = 1; + return 0; +} + +static int +decode_coding_sjis (unsigned char *source, int src_bytes, MText *mt, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src = internal->carryover; + unsigned char *src_stop = src + internal->carryover_bytes; + unsigned char *src_end = source + src_bytes; + unsigned char *src_base; + unsigned char *dst = mt->data + mt->nbytes; + unsigned char *dst_end = mt->data + mt->allocated - MAX_UTF8_CHAR_BYTES; + int nchars = 0; + int last_nchars = 0; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + + MCharset *charset_roman = coding->charsets[0]; + MCharset *charset_kanji = coding->charsets[1]; + MCharset *charset_kana = coding->charsets[2]; + MCharset *charset = mcharset__ascii; + int error = 0; + + while (1) + { + MCharset *this_charset; + int c, c1, c2; + + ONE_MORE_BASE_BYTE (c1); + + c2 = -1; + if (c1 < 0x80) + { + this_charset = ((c1 <= 0x20 || c1 == 0x7F) + ? mcharset__ascii + : charset_roman); + } + else if ((c1 >= 0x81 && c1 <= 0x9F) || (c1 >= 0xE0 && c1 <= 0xEF)) + { + ONE_MORE_BYTE (c2); + if ((c2 >= 0x40 && c2 <= 0x7F) || (c2 >= 80 && c2 <= 0xFC)) + { + this_charset = charset_kanji; + c1 = SJIS_TO_JIS (c1, c2); + } + else + goto invalid_byte; + } + else if (c1 >= 0xA1 && c1 <= 0xDF) + { + this_charset = charset_kana; + c1 &= 0x7F; + } + else + goto invalid_byte; + + c = DECODE_CHAR (this_charset, c1); + if (c >= 0) + goto emit_char; + + invalid_byte: + if (! converter->lenient) + break; + REWIND_SRC_TO_BASE (); + c = *src++; + this_charset = mcharset__binary; + + emit_char: + if (this_charset != mcharset__ascii + && this_charset != charset) + { + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + charset = this_charset; + last_nchars = nchars; + } + EMIT_CHAR (c); + } + /* We reach here because of an invalid byte. */ + error = 1; + + source_end: + TAKEIN_CHARS (mt, nchars - last_nchars, + dst - (mt->data + mt->nbytes), charset); + return finish_decoding (mt, converter, nchars, + source, src_end, src_base, error); +} + +static int +encode_coding_sjis (MText *mt, int from, int to, + unsigned char *destination, int dst_bytes, + MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + MCodingSystem *coding = internal->coding; + unsigned char *src, *src_end; + unsigned char *dst = destination; + unsigned char *dst_end = dst + dst_bytes; + int nchars = 0; + MCharset *charset_roman = coding->charsets[0]; + MCharset *charset_kanji = coding->charsets[1]; + MCharset *charset_kana = coding->charsets[2]; + enum MTextFormat format = mt->format; + + SET_SRC (mt, format, from, to); + + while (1) + { + int c, bytes, len; + unsigned code; + + ONE_MORE_CHAR (c, bytes, format); + + if (c <= 0x20 || c == 0x7F) + { + CHECK_DST (1); + *dst++ = c; + } + else + { + if ((code = ENCODE_CHAR (charset_roman, c)) != MCHAR_INVALID_CODE) + { + CHECK_DST (1); + *dst++ = c; + } + else if ((code = ENCODE_CHAR (charset_kanji, c)) + != MCHAR_INVALID_CODE) + { + int c1 = code >> 8, c2 = code & 0xFF; + code = JIS_TO_SJIS (c1, c2); + CHECK_DST (2); + *dst++ = code >> 8; + *dst++ = code & 0xFF; + } + else if ((code = ENCODE_CHAR (charset_kana, c)) + != MCHAR_INVALID_CODE) + { + CHECK_DST (1); + *dst++ = code | 0x80; + } + else + { + if (! converter->lenient) + break; + len = encode_unsupporeted_char (c, dst, dst_end, + mt, from + nchars); + if (len == 0) + goto insufficient_destination; + dst += len; + } + } + src += bytes; + nchars++; + } + /* We reach here because of an unsupported char. */ + converter->result = MCONVERSION_RESULT_INVALID_CHAR; + goto finish; + + insufficient_destination: + converter->result = MCONVERSION_RESULT_INSUFFICIENT_DST; + + finish: + converter->nchars += nchars; + converter->nbytes += dst - destination; + return (converter->result == MCONVERSION_RESULT_INVALID_CHAR ? -1 : 0); +} + + +static MCodingSystem * +find_coding (MSymbol name) +{ + MCodingSystem *coding = (MCodingSystem *) msymbol_get (name, Mcoding); + + if (! coding) + { + MPlist *param = mplist_get (coding_definition_list, name); + + if (! param) + return NULL; + param = mplist__from_plist (param); + mconv_define_coding (MSYMBOL_NAME (name), param, NULL, NULL, NULL, NULL); + coding = (MCodingSystem *) msymbol_get (name, Mcoding); + M17N_OBJECT_UNREF (param); + } + return coding; +} + +#define BINDING_NONE 0 +#define BINDING_BUFFER 1 +#define BINDING_STREAM 2 + +#define CONVERT_WORKSIZE 0x10000 + + +/* Internal API */ + +int +mcoding__init (void) +{ + int i; + MPlist *param, *charsets, *pl; + + MLIST_INIT1 (&coding_list, codings, 128); + coding_definition_list = mplist (); + + /* ISO-2022 specific initialize routine. */ + for (i = 0; i < 0x20; i++) + iso_2022_code_class[i] = ISO_control_0; + for (i = 0x21; i < 0x7F; i++) + iso_2022_code_class[i] = ISO_graphic_plane_0; + for (i = 0x80; i < 0xA0; i++) + iso_2022_code_class[i] = ISO_control_1; + for (i = 0xA1; i < 0xFF; i++) + iso_2022_code_class[i] = ISO_graphic_plane_1; + iso_2022_code_class[0x20] = iso_2022_code_class[0x7F] = ISO_0x20_or_0x7F; + iso_2022_code_class[0xA0] = iso_2022_code_class[0xFF] = ISO_0xA0_or_0xFF; + iso_2022_code_class[0x0E] = ISO_shift_out; + iso_2022_code_class[0x0F] = ISO_shift_in; + iso_2022_code_class[0x19] = ISO_single_shift_2_7; + iso_2022_code_class[0x1B] = ISO_escape; + iso_2022_code_class[0x8E] = ISO_single_shift_2; + iso_2022_code_class[0x8F] = ISO_single_shift_3; + iso_2022_code_class[0x9B] = ISO_control_sequence_introducer; + + Mcoding = msymbol ("coding"); + + Mutf = msymbol ("utf"); + Miso_2022 = msymbol ("iso-2022"); + + Mreset_at_eol = msymbol ("reset-at-eol"); + Mreset_at_cntl = msymbol ("reset-at-cntl"); + Meight_bit = msymbol ("eight-bit"); + Mlong_form = msymbol ("long-form"); + Mdesignation_g0 = msymbol ("designation-g0"); + Mdesignation_g1 = msymbol ("designation-g1"); + Mdesignation_ctext = msymbol ("designation-ctext"); + Mdesignation_ctext_ext = msymbol ("designation-ctext-ext"); + Mlocking_shift = msymbol ("locking-shift"); + Msingle_shift = msymbol ("single-shift"); + Msingle_shift_7 = msymbol ("single-shift-7"); + Meuc_tw_shift = msymbol ("euc-tw-shift"); + Miso_6429 = msymbol ("iso-6429"); + Mrevision_number = msymbol ("revision-number"); + Mfull_support = msymbol ("full-support"); + Mmaybe = msymbol ("maybe"); + + Mtype = msymbol ("type"); + Mcharsets = msymbol_as_managing_key ("charsets"); + Mflags = msymbol_as_managing_key ("flags"); + Mdesignation = msymbol_as_managing_key ("designation"); + Minvocation = msymbol_as_managing_key ("invocation"); + Mcode_unit = msymbol ("code-unit"); + Mbom = msymbol ("bom"); + Mlittle_endian = msymbol ("little-endian"); + + param = mplist (); + charsets = mplist (); + pl = param; + /* Setup predefined codings. */ + mplist_set (charsets, Msymbol, Mcharset_ascii); + pl = mplist_add (pl, Mtype, Mcharset); + pl = mplist_add (pl, Mcharsets, charsets); + Mcoding_us_ascii = mconv_define_coding ("us-ascii", param, + NULL, NULL, NULL, NULL); + + { + MSymbol alias = msymbol ("ANSI_X3.4-1968"); + MCodingSystem *coding + = (MCodingSystem *) msymbol_get (Mcoding_us_ascii, Mcoding); + + msymbol_put (alias, Mcoding, coding); + alias = msymbol__canonicalize (alias); + msymbol_put (alias, Mcoding, coding); + } + + mplist_set (charsets, Msymbol, Mcharset_iso_8859_1); + Mcoding_iso_8859_1 = mconv_define_coding ("iso-8859-1", param, + NULL, NULL, NULL, NULL); + + mplist_set (charsets, Msymbol, Mcharset_m17n); + mplist_put (param, Mtype, Mutf); + mplist_put (param, Mcode_unit, (void *) 8); + Mcoding_utf_8_full = mconv_define_coding ("utf-8-full", param, + NULL, NULL, NULL, NULL); + + mplist_set (charsets, Msymbol, Mcharset_unicode); + Mcoding_utf_8 = mconv_define_coding ("utf-8", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 16); + mplist_put (param, Mbom, Mmaybe); +#ifndef WORDS_BIGENDIAN + mplist_put (param, Mlittle_endian, Mt); +#endif + Mcoding_utf_16 = mconv_define_coding ("utf-16", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 32); + Mcoding_utf_32 = mconv_define_coding ("utf-32", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 16); + mplist_put (param, Mbom, Mnil); + mplist_put (param, Mlittle_endian, Mnil); + Mcoding_utf_16be = mconv_define_coding ("utf-16be", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 32); + Mcoding_utf_32be = mconv_define_coding ("utf-32be", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 16); + mplist_put (param, Mlittle_endian, Mt); + Mcoding_utf_16le = mconv_define_coding ("utf-16le", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mcode_unit, (void *) 32); + Mcoding_utf_32le = mconv_define_coding ("utf-32le", param, + NULL, NULL, NULL, NULL); + + mplist_put (param, Mtype, Mnil); + mplist_set (charsets, Msymbol, Mcharset_ascii); + Mcoding_sjis = mconv_define_coding ("sjis", param, + reset_coding_sjis, + decode_coding_sjis, + encode_coding_sjis, NULL); + + M17N_OBJECT_UNREF (charsets); + M17N_OBJECT_UNREF (param); + + return 0; +} + +void +mcoding__fini (void) +{ + int i; + MPlist *plist; + + for (i = 0; i < coding_list.used; i++) + { + MCodingSystem *coding = coding_list.codings[i]; + + if (coding->extra_info) + free (coding->extra_info); + if (coding->extra_spec) + free (coding->extra_spec); + free (coding); + } + MLIST_FREE1 (&coding_list, codings); + MPLIST_DO (plist, coding_definition_list) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (coding_definition_list); +} + +void +mconv__define_coding_from_charset (MSymbol sym) +{ + MPlist *param = mplist (), *charsets = mplist (); + + mplist_set (charsets, Msymbol, sym); + mplist_add (param, Mtype, Mcharset); + mplist_add (param, Mcharsets, charsets); + mconv_define_coding (msymbol_name (sym), param, NULL, NULL, NULL, NULL); + M17N_OBJECT_UNREF (charsets); + M17N_OBJECT_UNREF (param); +} + +void +mconv__register_charset_coding (MSymbol sym) +{ + if (! mplist_find_by_key (coding_definition_list, sym)) + { + MPlist *param = mplist (), *charsets = mplist (); + + mplist_set (charsets, Msymbol, sym); + mplist_add (param, Msymbol, Mtype); + mplist_add (param, Msymbol, Mcharset); + mplist_add (param, Msymbol, Mcharsets); + mplist_add (param, Mplist, charsets); + mplist_put (coding_definition_list, sym, param); + M17N_OBJECT_UNREF (charsets); + } +} + + +int +mcoding__load_from_database () +{ + MDatabase *mdb = mdatabase_find (msymbol ("coding-list"), Mnil, Mnil, Mnil); + MPlist *def_list, *plist; + MPlist *definitions = coding_definition_list; + int mdebug_mask = MDEBUG_CODING; + + if (! mdb) + return 0; + MDEBUG_PUSH_TIME (); + def_list = (MPlist *) mdatabase_load (mdb); + MDEBUG_PRINT_TIME ("CODING", (stderr, " to load the data.")); + MDEBUG_POP_TIME (); + if (! def_list) + return -1; + + MDEBUG_PUSH_TIME (); + MPLIST_DO (plist, def_list) + { + MPlist *pl; + MSymbol name; + + if (! MPLIST_PLIST_P (plist)) + MERROR (MERROR_CHARSET, -1); + pl = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_CHARSET, -1); + name = MPLIST_SYMBOL (pl); + pl = MPLIST_NEXT (pl); + definitions = mplist_add (definitions, name, pl); + M17N_OBJECT_REF (pl); + } + + M17N_OBJECT_UNREF (def_list); + MDEBUG_PRINT_TIME ("CODING", (stderr, " to parse the loaded data.")); + MDEBUG_POP_TIME (); + return 0; +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + +/* External API */ + +/*** @addtogroup m17nConv */ +/*** @{ */ +/*=*/ + +/***en @name Variables: Symbols representing a coding system */ +/***ja @name ÊÑ¿ô: ÄêµÁºÑ¤ß¥³¡¼¥É·Ï¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë */ +/*** @{ */ +/*=*/ + +/***en + @brief Symbol for the coding system US-ASCII + + The symbol #Mcoding_us_ascii has name "us-ascii" and + represents a coding system for the CES US-ASCII. */ + +/***ja + @brief MIME charset "US-ASCII" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mcoding_us_ascii ¤Ï "us-ascii" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + MIME charset "US-ASCII" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ¤ë¤¿¤á + ¤Ë»È¤ï¤ì¤ë¡£ + */ +MSymbol Mcoding_us_ascii; +/*=*/ + +/***en + @brief Symbol for the coding system ISO-8859-1 + + The symbol #Mcoding_iso_8859_1 has name "iso-8859-1" and + represents a coding system for the CES ISO-8859-1. */ + +/***ja + @brief MIME charset "ISO-8859-1" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mcoding_iso_8859_1 ¤Ï "iso-8859-1" ¤È¤¤¤¦Ì¾Á° + ¤ò»ý¤Á¡¢MIME charset "ISO-8859-1" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»Ø + Äꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£ */ + +MSymbol Mcoding_iso_8859_1; +/*=*/ + +/***en + @brief Symbol for the coding system UTF-8 + + The symbol #Mcoding_utf_8 has name "utf-8" and represents + a coding system for the CES UTF-8. */ + +/***ja + @brief RFC 2279 ¤Î "UTF-8" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë¡ÊUnicode ÍÑ¡Ë + + ¥·¥ó¥Ü¥ë @c Mcoding_utf_8 ¤Ï "utf-8" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë"UTF-8" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ¤ë + ¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£ + */ + +MSymbol Mcoding_utf_8; +/*=*/ + +/***en + @brief UTF-8-FULL + + + The symbol #Mcoding_utf_8_full has name "utf-8-full" and + represents a coding system that is a extension of UTF-8. This + coding system uses the same encoding algorithm as UTF-8 but is not + limited to the Unicode characters. It can encode all characters + supported by the m17n library. */ + +/***ja + @brief RFC 2279 ¤Î "UTF-8" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë¡ÊÁ´Ê¸»úÍÑ¡Ë + + ¥·¥ó¥Ü¥ë @c Mcoding_utf_8_full ¤Ï "utf-8-full" ¤È¤¤¤¦Ì¾Á° + ¤ò»ý¤Á¡¢RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë"UTF-8" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò + »ØÄꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤¬°·¤¦Á´¤Æ¤Î + ʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£ */ + +MSymbol Mcoding_utf_8_full; +/*=*/ + +/***en + @brief UTF-16 + + The symbol #Mcoding_utf_16 has name "utf-16" and + represents a coding system for the CES UTF-16 (RFC 2279). */ +/***ja + @brief RFC 2781 ¤Î "UTF-16" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mcoding_utf_16 ¤Ï "utf-16" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë"UTF-16" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ + ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹ + ¤ë¡£ */ + +MSymbol Mcoding_utf_16; +/*=*/ + +/***en + @brief UTF-16BE + + The symbol #Mcoding_utf_16be has name "utf-16be" and + represents a coding system for the CES UTF-16BE (RFC 2279). */ + +MSymbol Mcoding_utf_16be; +/*=*/ + +/***en + @brief UTF-16LE + + The symbol #Mcoding_utf_16le has name "utf-16le" and + represents a coding system for the CES UTF-16LE (RFC 2279). */ + +MSymbol Mcoding_utf_16le; +/*=*/ + +/***en + @brief UTF-32 + + The symbol #Mcoding_utf_32 has name "utf-32" and + represents a coding system for the CES UTF-32 (RFC 2279). */ + +MSymbol Mcoding_utf_32; +/*=*/ + +/***en + @brief UTF-32be + + The symbol #Mcoding_utf_32be has name "utf-32be" and + represents a coding system for the CES UTF-32BE (RFC 2279). */ + +MSymbol Mcoding_utf_32be; +/*=*/ + +/***en + @brief UTF-32LE + + The symbol #Mcoding_utf_32le has name "utf-32le" and + represents a coding system for the CES UTF-32LE (RFC 2279). */ +MSymbol Mcoding_utf_32le; +/*=*/ + +/***en + @brief SJIS + + The symbol #Mcoding_sjis has name "sjis" and represents a coding + system for the CES Shift-JIS. */ + +MSymbol Mcoding_sjis; +/*** @} */ +/*=*/ + +/***en + @name Variables: Parameter keys for mconv_define_coding (). */ +/*** @{ */ +/*=*/ + +/***en + Parameter key for mconv_define_coding () (which see). */ +MSymbol Mtype; +/*=*/ + +MSymbol Mcharsets; +MSymbol Mflags; +MSymbol Mdesignation; +MSymbol Minvocation; +MSymbol Mcode_unit; +MSymbol Mbom; +MSymbol Mlittle_endian; +/*** @} */ +/*=*/ + +/***en + @name Variables: Symbols representing coding system type. */ +/*** @{ */ +/*=*/ + +/***en + Symbol that can be a value of the #Mtype parameter of a coding + system used in an argument to the mconv_define_coding () function + (which see). */ + +MSymbol Mutf; +/*=*/ +MSymbol Miso_2022; +/*=*/ +/*** @} */ +/*=*/ + +/***en + @name Variables: Symbols appearing in the value of #Mfrag parameter. */ +/*** @{ */ +/*=*/ + +/***en + Symbol that can be a value of the #Mflags parameter of a coding + system used in an argument to the mconv_define_coding () function + (which see). */ +MSymbol Mreset_at_eol; +/*=*/ +MSymbol Mreset_at_cntl; +MSymbol Meight_bit; +MSymbol Mlong_form; +MSymbol Mdesignation_g0; +MSymbol Mdesignation_g1; +MSymbol Mdesignation_ctext; +MSymbol Mdesignation_ctext_ext; +MSymbol Mlocking_shift; +MSymbol Msingle_shift; +MSymbol Msingle_shift_7; +MSymbol Meuc_tw_shift; +MSymbol Miso_6429; +MSymbol Mrevision_number; +MSymbol Mfull_support; +/*** @} */ +/*=*/ + +/***en + @name Variables: etc + + Remaining variables. */ +/***ja @name ÊÑ¿ô: ¤½¤Î¾ */ +/*** @{ */ +/*=*/ +/***en + @brief Symbol whose name is "maybe". + + The variable #Mmaybe is a symbol of name "maybe". It is + used a value of #Mbom parameter of the function + mconv_define_coding () (which see). */ + +MSymbol Mmaybe; +/*=*/ + +/***en + @brief The symbol @c Mcoding + + Any decoded M-text has a text property whose key is the predefined + symbol @c Mcoding. The name of @c Mcoding is + "coding". */ + +/***ja + @brief ¥·¥ó¥Ü¥ë @c Mcoding + + ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥­¡¼¤¬ @c Mcoding ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È¥× + ¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¥·¥ó¥Ü¥ë @c Mcoding ¤Ï "coding" ¤È¤¤¤¦Ì¾ + Á°¤Ç¤¢¤é¤«¤¸¤áÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ */ + +MSymbol Mcoding; +/*=*/ +/*** @} */ +/*=*/ + +/***en + @brief Define a coding system + + The mconv_define_coding () function defines a new coding system + and makes it accessive via a symbol whose name is $NAME. $PLIST + specifies parameters of the charset as below: + +
    + +
  • Key is @c Mtype, value is a symbol + + The value specifies the type of the coding system. It must be + #Mcharset, #Mutf, #Miso_2022, or #Mnil. + + If the type is #Mcharset, $EXTRA_INFO is ignored. + + If the type is #Miso_2022, $EXTRA_INFO must be a pointer to + #MCodingInfoISO2022. + + If the type is #Mutf, $EXTRA_INFO must be a pointer to + #MCodingInfoUTF. + + If the type is #Mnil, the argument $RESETTER, $DECODER, and + $ENCODER must be supplied. $EXTRA_INFO is ignored. Otherwise, + they can be @c NULL and the m17n library provides proper defaults. + +
  • Key is #Mcharsets, value is a plist + + The value specifies a list charsets supported by the coding + system. The keys of the plist must be #Msymbol, and the values + must be symbols representing charsets. + +
  • Key is #Mflags, value is a plist + + If the type is #Miso_2022, the values specifies flags to control + the ISO 2022 interpreter. The keys of the plist must e @c + Msymbol, and values must be one of the following. + +
      + +
    • #Mreset_at_eol + + If this flag exits, designation and invocation status is reset to + the initial state at the end of line. + +
    • #Mreset_at_cntl + + If this flag exists, designation and invocation status is reset to + the initial state at a control character. + +
    • #Meight_bit + + If this flag exists, the graphic plane right is used. + +
    • #Mlong_form + + If this flag exists, the over-long escape sequences (ESC '$' '(' + ) are used for designating the charsets JISX0208.1978, + GB2312, and JISX0208. + +
    • #Mdesignation_g0 + + If this flag and #Mfull_support exists, designates charsets not + listed in the charset list to the graphic register G0. + +
    • #Mdesignation_g1 + + If this flag and #Mfull_support exists, designates charsets not + listed in the charset list to the graphic register G1. + +
    • #Mdesignation_ctext + + If this flag and #Mfull_support exists, designates charsets not + listed in the charset list to a graphic register G0 or G1 based on + the criteria of the Compound Text. + +
    • #Mdesignation_ctext_ext + + If this flag and #Mfull_support exists, designates charsets not + listed in the charset list to a graphic register G0 or G1, or use + extended segment for such charsets based on the criteria of the + Compound Text. + +
    • #Mlocking_shift + + If this flag exists, use locking shift. + +
    • #Msingle_shift + + If this flag exists, use single shift. + +
    • #Msingle_shift_7 + + If this flag exists, use 7-bit single shift code (0x19). + +
    • #Meuc_tw_shift; + + If this flag exists, use a special shifting according to EUC-TW. + +
    • #Miso_6429 + + This flag is currently ignored. + +
    • #Mrevision_number + + If this flag exists, use a revision number escape sequence to + designate a charset that has a revision number. + +
    • #Mfull_support + + If this flag exists, support all charsets registered in the + International Registry. + +
    + +
  • Key is #Mdesignation, value is a plist + + If the type is #Miso_2022, the value specifies how to designate + each supported characters. The keys of the plist must be @c + Minteger, and the values must be numbers indicating a graphic + registers. The Nth element value is for the Nth charset of the + charset list. The value 0..3 means that it is assumed that a + charset is already designated to the graphic register 0..3. The + negative value G (-4..-1) means that a charset is not designated + to any register at first, and if necessary, is designated to the + (G+4) graphic register. + +
  • Key is #Minvocation, value is a plist + + If the type is #Miso_2022, the value specifies how to invocate + each graphic registers. The plist length must be one or two. The + keys of the plist must be #Minteger, and the values must be + numbers indicating a graphic register. The value of the first + element specifies which graphic register is invocated to the + graphic plane left. If the length is one, no graphic register is + invocated to the graphic plane right. Otherwise, the value of the + second element specifies which graphic register is invocated to + the graphic plane right. + +
  • Key is #Mcode_unit, value is an integer + + If the type is #Mutf, the value specifies the bit length of a + code-unit. It must be 8, 16, or 32. + +
  • Key is #Mbom, value is a symbol + + If the type is #Mutf and the code-unit bit length is 16 or 32, + it specifies whether or not to use BOM (Byte Order Mark). If the + value is #Mnil (default), BOM is not used, else if the value is + #Mmaybe, the existence of BOM is detected at decoding time, else + BOM is used. + +
  • Key is #Mlittle_endian, value is a symbol + + If the type is #Mutf and the code-unit bit length is 16 or 32, + it specifies whether or not the encoding is little endian. If the + value is #Mnil (default), it is big endian, else it is little + endian. + +
+ + $RESETTER is a pointer to a function that resets a converter for + the coding system to the initial status. The pointed function is + called with one argument, a pointer to a converter object. + + $DECODER is a pointer to a function that decodes a byte sequence + according to the coding system. The pointed function is called + with four arguments: + + @li A pointer to the byte sequence to decode. + @li The number of bytes to decode. + @li A pointer to an M-text to which the decoded characters are appended. + @li A pointer to a converter object. + + $DECODER must return 0 if it succeeds. Otherwise it must return -1. + + $ENCODER is a pointer to a function that encodes an M-text + according to the coding system. The pointed function is called + with six arguments: + + @li A pointer to the M-text to encode. + @li The starting position of the encoding. + @li The ending position of the encoding. + @li A pointer to a memory area where the produced bytes are stored. + @li The size of the memory area. + @li A pointer to a converter object. + + $ENCODER must return 0 if it succeeds. Otherwise it must return -1. + + $EXTRA_INFO is a pointer to a data structure that contains extra + information about the coding system. The type of the data + structure depends on $TYPE. + + @return + + If the operation was successful, mconv_define_coding () returns a + symbol whose name is $NAME. If an error is detected, it returns + #Mnil and assigns an error code to the external variable @c + merror_code. */ + +/***ja + @brief ¥³¡¼¥É·Ï¤ÎÄêµÁ + + ´Ø¿ô mconv_define_coding () ¤Ï¡¢¿·¤·¤¤¥³¡¼¥É·Ï¤òÄêµÁ¤·¡¢¤½¤ì¤ò + $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü¥ë·Ðͳ¤Ç¥¢¥¯¥»¥¹¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¡£ + + $TYPE ¤Ï Îóµó·¿ #MCodingType ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ê¡¢¥³¡¼¥É·Ï¤Î¹½Â¤¤ò + »ØÄꤹ¤ë¡£ + + $CHARSET_NAMES ¤Ï¥µ¥Ý¡¼¥È¤¹¤ëʸ»ú¥»¥Ã¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤ÎÇÛÎó¤Ç¤¢¤ê¡¢ + $NCHARSETS ¤Ï¤½¤ÎÍ×ÁÇ¿ô¤Ç¤¢¤ë¡£ + + $TYPE ¤¬ #MCODING_TYPE_MISC ¤Ç¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢$RESETTER, $DECODER, + $ENCODER ¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¤½¤ì°Ê³°¤Î¾ì¹ç¤Ë¤Ï¤³¤ì¤é¤Ï @c + NULL ¤Ç¹½¤ï¤Ê¤¤¡£¤½¤ÎºÝ¤Ë¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤¬Å¬Àڤʥǥե©¥ë¥ÈÃͤò + Í¿¤¨¤ë¡£ + + $RESETTER ¤Ï¤³¤Î¥³¡¼¥É·ÏÍѤΥ³¥ó¥Ð¡¼¥¿¤ò½é´ü¾õÂ֤˥ꥻ¥Ã¥È¤¹¤ë´Ø¿ô + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï¥³¥ó¥Ð¡¼¥¿¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤È + ¤¤¤¦£±°ú¿ô¤ò¤È¤ë¡£ + + $DECODER ¤Ï¥Ð¥¤¥ÈÎó¤ò¤³¤Î¥³¡¼¥É·Ï¤Ë½¾¤Ã¤Æ¥Ç¥³¡¼¥É¤¹¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤ + ¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï°Ê²¼¤Î4°ú¿ô¤ò¤È¤ë¡£ + + @li ¥Ð¥¤¥ÈÎó¤Ø¤Î¥Ý¥¤¥ó¥¿ + @li ¥Ç¥³¡¼¥É¤¹¤Ù¤­¥Ð¥¤¥È¿ô + @li ¥Ç¥³¡¼¥É·ë²Ì¤Îʸ»ú¤òÉղ乤ë M-text ¤Ø¤Î¥Ý¥¤¥ó¥¿ + @li ¥³¥ó¥Ð¡¼¥¿¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿ + + $DECODER ¤ÏÀ®¸ù¤·¤¿¤È¤­¤Ë¤Ï0¤ò¡¢¼ºÇÔ¤·¤¿¤È¤­¤Ë¤Ï-1¤òÊÖ¤µ¤Ê¤¯¤Æ¤Ï¤Ê + ¤é¤Ê¤¤¡£ + + $ENCODER ¤Ï M-text ¤ò¤³¤Î¥³¡¼¥É·Ï¤Ë½¾¤Ã¤Æ¥¨¥ó¥³¡¼¥É¤¹ + ¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï°Ê²¼¤Î6°ú¿ô¤ò¤È¤ë¡£ + + @li M-text ¤Ø¤Î¥Ý¥¤¥ó¥¿ + @li M-text ¤Î¥¨¥ó¥³¡¼¥É³«»Ï°ÌÃÖ + @li M-text ¤Î¥¨¥ó¥³¡¼¥É½ªÎ»°ÌÃÖ + @li À¸À®¤·¤¿¥Ð¥¤¥È¤òÊÝ»ý¤¹¤ë¥á¥â¥êÎΰè¤Ø¤Î¥Ý¥¤¥ó¥¿ + @li ¥á¥â¥êÎΰè¤Î¥µ¥¤¥º + @li ¥³¥ó¥Ð¡¼¥¿¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿ + + $ENCODER ¤ÏÀ®¸ù¤·¤¿¤È¤­¤Ë¤Ï0¤ò¡¢¼ºÇÔ¤·¤¿¤È¤­¤Ë¤Ï-1¤òÊÖ¤µ¤Ê¤¯¤Æ¤Ï¤Ê + ¤é¤Ê¤¤¡£ + + $EXTRA_INFO ¤Ï¥³¡¼¥Ç¥£¥°¥·¥¹¥Æ¥à¤Ë´Ø¤¹¤ëÄɲþðÊó¤ò´Þ¤à¥Ç¡¼¥¿¹½Â¤¤Ø + ¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î¥Ç¡¼¥¿¹½Â¤¤Î¥¿¥¤¥×¤Ï $TYPE ¤Ë°Í¸¤¹¤ë¡£ + + $TYPE ¤¬ #MCODING_TYPE_ISO_2022 ¤Ç¤¢¤ì¤Ð¡¢$EXTRA_INFO ¤Ï @c + MCodingInfoISO2022 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + $TYPE ¤¬ #MCODING_TYPE_UTF ¤Ç¤¢¤ì¤Ð¡¢$EXTRA_INFO ¤Ï @c + MCodingInfoUTF ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + $TYPE ¤¬ #MCODING_TYPE_CHARSET, #MCODING_TYPE_MISC ¤Î¤É¤ì¤«¤Ç + ¤¢¤ì¤Ð¡¢$EXTRA_INFO ¤Ï̵»ë¤µ¤ì¤ë¡£ + + @return + + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mconv_define_coding () ¤Ï $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥· + ¥ó¥Ü¥ë¤òÊÖ¤¹¡£¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥­¡¼¤¬ $Mcoding ¤Ç¡¢ºî¤é¤ì¤¿¥³¡¼¥É·Ï + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£ ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì + ¤¿¾ì¹ç¤Ï Mnil ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + */ + +/*** + @errors + @c MERROR_CODING */ + +MSymbol +mconv_define_coding (char *name, MPlist *plist, + int (*resetter) (MConverter *), + int (*decoder) (unsigned char *, int, MText *, + MConverter *), + int (*encoder) (MText *, int, int, + unsigned char *, int, + MConverter *), + void *extra_info) +{ + MSymbol sym = msymbol (name); + int i; + MCodingSystem *coding; + MPlist *pl; + + MSTRUCT_MALLOC (coding, MERROR_CODING); + coding->name = sym; + if ((coding->type = (MSymbol) mplist_get (plist, Mtype)) == Mnil) + coding->type = Mcharset; + pl = (MPlist *) mplist_get (plist, Mcharsets); + if (! pl) + MERROR (MERROR_CODING, Mnil); + coding->ncharsets = mplist_length (pl); + if (coding->ncharsets > NUM_SUPPORTED_CHARSETS) + coding->ncharsets = NUM_SUPPORTED_CHARSETS; + for (i = 0; i < coding->ncharsets; i++, pl = MPLIST_NEXT (pl)) + { + MSymbol charset_name; + + if (MPLIST_KEY (pl) != Msymbol) + MERROR (MERROR_CODING, Mnil); + charset_name = MPLIST_SYMBOL (pl); + if (! (coding->charsets[i] = MCHARSET (charset_name))) + MERROR (MERROR_CODING, Mnil); + } + + coding->resetter = resetter; + coding->decoder = decoder; + coding->encoder = encoder; + coding->ascii_compatible = 0; + coding->extra_info = extra_info; + coding->extra_spec = NULL; + coding->ready = 0; + + if (coding->type == Mcharset) + { + if (! coding->resetter) + coding->resetter = reset_coding_charset; + if (! coding->decoder) + coding->decoder = decode_coding_charset; + if (! coding->encoder) + coding->encoder = encode_coding_charset; + } + else if (coding->type == Mutf) + { + MCodingInfoUTF *info = malloc (sizeof (MCodingInfoUTF)); + MSymbol val; + + if (! coding->resetter) + coding->resetter = reset_coding_utf; + + info->code_unit_bits = (int) mplist_get (plist, Mcode_unit); + if (info->code_unit_bits == 8) + { + if (! coding->decoder) + coding->decoder = decode_coding_utf_8; + if (! coding->encoder) + coding->encoder = encode_coding_utf_8; + } + else if (info->code_unit_bits == 16) + { + if (! coding->decoder) + coding->decoder = decode_coding_utf_16; + if (! coding->encoder) + coding->encoder = encode_coding_utf_16; + } + else if (info->code_unit_bits == 32) + { + if (! coding->decoder) + coding->decoder = decode_coding_utf_32; + if (! coding->encoder) + coding->encoder = encode_coding_utf_32; + } + else + MERROR (MERROR_CODING, Mnil); + val = (MSymbol) mplist_get (plist, Mbom); + if (val == Mnil) + info->bom = 1; + else if (val == Mmaybe) + info->bom = 0; + else + info->bom = 2; + + info->endian = (mplist_get (plist, Mlittle_endian) ? 1 : 0); + coding->extra_info = info; + } + else if (coding->type == Miso_2022) + { + MCodingInfoISO2022 *info = malloc (sizeof (MCodingInfoISO2022)); + + if (! coding->resetter) + coding->resetter = reset_coding_iso_2022; + if (! coding->decoder) + coding->decoder = decode_coding_iso_2022; + if (! coding->encoder) + coding->encoder = encode_coding_iso_2022; + + info->initial_invocation[0] = 0; + info->initial_invocation[1] = -1; + pl = (MPlist *) mplist_get (plist, Minvocation); + if (pl) + { + if (MPLIST_KEY (pl) != Minteger) + MERROR (MERROR_CODING, Mnil); + info->initial_invocation[0] = MPLIST_INTEGER (pl); + if (! MPLIST_TAIL_P (pl)) + { + pl = MPLIST_NEXT (pl); + if (MPLIST_KEY (pl) != Minteger) + MERROR (MERROR_CODING, Mnil); + info->initial_invocation[1] = MPLIST_INTEGER (pl); + } + } + memset (info->designations, 0, sizeof (info->designations)); + for (i = 0, pl = (MPlist *) mplist_get (plist, Mdesignation); + i < 32 && pl && MPLIST_KEY (pl) == Minteger; + i++, pl = MPLIST_NEXT (pl)) + info->designations[i] = MPLIST_INTEGER (pl); + + info->flags = 0; + MPLIST_DO (pl, (MPlist *) mplist_get (plist, Mflags)) + { + MSymbol val; + + if (MPLIST_KEY (pl) != Msymbol) + MERROR (MERROR_CODING, Mnil); + val = MPLIST_SYMBOL (pl); + if (val == Mreset_at_eol) + info->flags |= MCODING_ISO_RESET_AT_EOL; + else if (val == Mreset_at_cntl) + info->flags |= MCODING_ISO_RESET_AT_CNTL; + else if (val == Meight_bit) + info->flags |= MCODING_ISO_EIGHT_BIT; + else if (val == Mlong_form) + info->flags |= MCODING_ISO_LOCKING_SHIFT; + else if (val == Mdesignation_g0) + info->flags |= MCODING_ISO_DESIGNATION_G0; + else if (val == Mdesignation_g1) + info->flags |= MCODING_ISO_DESIGNATION_G1; + else if (val == Mdesignation_ctext) + info->flags |= MCODING_ISO_DESIGNATION_CTEXT; + else if (val == Mdesignation_ctext_ext) + info->flags |= MCODING_ISO_DESIGNATION_CTEXT_EXT; + else if (val == Mlocking_shift) + info->flags |= MCODING_ISO_LOCKING_SHIFT; + else if (val == Msingle_shift) + info->flags |= MCODING_ISO_SINGLE_SHIFT; + else if (val == Msingle_shift_7) + info->flags |= MCODING_ISO_SINGLE_SHIFT_7; + else if (val == Meuc_tw_shift) + info->flags |= MCODING_ISO_EUC_TW_SHIFT; + else if (val == Miso_6429) + info->flags |= MCODING_ISO_ISO6429; + else if (val == Mrevision_number) + info->flags |= MCODING_ISO_REVISION_NUMBER; + else if (val == Mfull_support) + info->flags |= MCODING_ISO_FULL_SUPPORT; + } + + coding->extra_info = info; + } + else + { + if (! coding->decoder || ! coding->encoder) + MERROR (MERROR_CODING, Mnil); + if (! coding->resetter) + coding->ready = 1; + } + + msymbol_put (sym, Mcoding, coding); + msymbol_put (msymbol__canonicalize (sym), Mcoding, coding); + plist = (MPlist *) mplist_get (plist, Maliases); + if (plist) + { + MPLIST_DO (pl, plist) + { + MSymbol alias; + + if (MPLIST_KEY (pl) != Msymbol) + continue; + alias = MPLIST_SYMBOL (pl); + msymbol_put (alias, Mcoding, coding); + msymbol_put (msymbol__canonicalize (alias), Mcoding, coding); + } + } + + MLIST_APPEND1 (&coding_list, codings, coding, MERROR_CODING); + + return sym; +} + +/*=*/ + +/***en + @brief Resolve coding system name. + + The mconv_resolve_coding () function returns $SYMBOL if it + represents a coding system. Otherwise, canonicalize $SYMBOL as to + a coding system name, and if the canonicalized name represents a + coding system, return it. Otherwise, return Mnil. */ + + +MSymbol +mconv_resolve_coding (MSymbol symbol) +{ + MCodingSystem *coding = find_coding (symbol); + + if (! coding) + { + symbol = msymbol__canonicalize (symbol); + coding = find_coding (symbol); + } + return (coding ? coding->name : Mnil); +} + +/*=*/ + + +/***en + @brief List symbols representing a coding system. + + The mconv_list_codings () function makes an array of symbols + representing a coding system, stores the pointer to the array in a + place pointed to by $SYMBOLS, and returns the length of the array. */ + +int +mconv_list_codings (MSymbol **symbols) +{ + int i = coding_list.used + mplist_length (coding_definition_list); + int j; + MPlist *plist; + + MTABLE_MALLOC ((*symbols), i, MERROR_CODING); + i = 0; + MPLIST_DO (plist, coding_definition_list) + (*symbols)[i++] = MPLIST_KEY (plist); + for (j = 0; j < coding_list.used; j++) + if (! mplist_find_by_key (coding_definition_list, + coding_list.codings[j]->name)) + (*symbols)[i++] = coding_list.codings[j]->name; + return i; +} + +/*=*/ + +/***en + @brief Create a code converter bound to a buffer. + + The mconv_buffer_converter () function creates a pointer to a code + converter for coding system $CODING. The code converter is bound + to buffer area of $N bytes pointed to by $BUF. Subsequent + decodings and encodings are done to/from this buffer area. + + $CODING can be #Mnil. In this case, a coding system associated + with the current locale (LC_CTYPE) is used. + + @return + If the operation was successful, mconv_buffer_converter () returns + the created code converter. Otherwise it returns @c NULL and + assigns an error code to the external variable #merror_code. */ + +/***ja + @brief ¥Ð¥Ã¥Õ¥¡¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤òºî¤ë + + ´Ø¿ô mconv_buffer_converter () ¤Ï¡¢¥³¡¼¥É·Ï $CODING ÍѤΥ³¡¼¥É¥³¥ó + ¥Ð¡¼¥¿¤òºî¤ë¡£¤³¤Î¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤Ï¡¢$BUF ¤Ç¼¨¤µ¤ì¤ëÂ礭¤µ $N ¥Ð + ¥¤¥È¤Î¥Ð¥Ã¥Õ¥¡Îΰè¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤ë¡£¤³¤ì°Ê¹ß¤Î¥Ç¥³¡¼¥É¤ª¤è¤Ó + ¥¨¥ó¥³¡¼¥É¤Ï¡¢¤³¤Î¥Ð¥Ã¥Õ¥¡Îΰè¤ËÂФ·¤Æ¹Ô¤Ê¤ï¤ì¤ë¡£ + + $CODING ¤Ï #Mnil ¤Ç¤¢¤Ã¤Æ¤â¤è¤¤¡£¤³¤Î¾ì¹ç¤Ï¸½ºß¤Î¥í¥±¡¼¥ë + (LC_CTYPE) ¤Ë´ØÏ¢ÉÕ¤±¤é¤ì¤¿¥³¡¼¥É·Ï¤¬»È¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð mconv_buffer_converter () ¤Ï ºî¤é¤ì¤¿¥³¡¼¥É¥³ + ¥ó¥Ð¡¼¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code + ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mconverter} @endlatexonly */ + +/*** + @errors + @c MERROR_SYMBOL, @c MERROR_CODING + + @seealso + mconv_stream_converter () */ + +MConverter * +mconv_buffer_converter (MSymbol name, unsigned char *buf, int n) +{ + MCodingSystem *coding; + MConverter *converter; + MConverterStatus *internal; + + if (name == Mnil) + name = mlocale_get_prop (mlocale__ctype, Mcoding); + coding = find_coding (name); + if (! coding) + MERROR (MERROR_CODING, NULL); + MSTRUCT_CALLOC (converter, MERROR_CODING); + MSTRUCT_CALLOC (internal, MERROR_CODING); + converter->internal_info = internal; + internal->coding = coding; + if (coding->resetter + && (*coding->resetter) (converter) < 0) + { + free (internal); + free (converter); + MERROR (MERROR_CODING, NULL); + } + + internal->unread = mtext (); + internal->work_mt = mtext (); + mtext__enlarge (internal->work_mt, MAX_UTF8_CHAR_BYTES); + internal->buf = buf; + internal->used = 0; + internal->bufsize = n; + internal->binding = BINDING_BUFFER; + + return converter; +} + +/*=*/ + +/***en + @brief Create a code converter bound to a stream. + + The mconv_stream_converter () function create a pointer to a code + converter for coding system $CODING. The code converter is bound + to stream $FP. Subsequent decodings and encodings are done + to/from this stream. + + $CODING can be #Mnil. In this case, a coding system associated + with the current locale (LC_CTYPE) is used. + + @return If the operation was successful, mconv_stream_converter () + returns the created code converter. Otherwise it returns @c NULL + and assigns an error code to the external variable @c + merror_code. */ + +/***ja + @brief ¥¹¥È¥ê¡¼¥à¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤òºî¤ë + + ´Ø¿ô mconv_stream_converter () ¤Ï¡¢¥³¡¼¥É·Ï $CODING ÍѤΥ³¡¼¥É¥³¥ó + ¥Ð¡¼¥¿¤òºî¤ë¡£¤³¤Î¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤Ï¡¢¥¹¥È¥ê¡¼¥à $FP ¤Ë·ë¤ÓÉÕ¤±¤é + ¤ì¤ë¡£¤³¤ì°Ê¹ß¤Î¥Ç¥³¡¼¥É¤ª¤è¤Ó¥¨¥ó¥³¡¼¥É¤Ï¡¢¤³¤Î¥¹¥È¥ê¡¼¥à¤ËÂФ·¤Æ + ¹Ô¤Ê¤ï¤ì¤ë¡£ + + $CODING ¤Ï #Mnil ¤Ç¤¢¤Ã¤Æ¤â¤è¤¤¡£¤³¤Î¾ì¹ç¤Ï¸½ºß¤Î¥í¥±¡¼¥ë + (LC_CTYPE) ¤Ë´ØÏ¢ÉÕ¤±¤é¤ì¤¿¥³¡¼¥É·Ï¤¬»È¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_stream_converter () ¤Ïºî¤é¤ì¤¿¥³¡¼¥É¥³ + ¥ó¥Ð¡¼¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code + ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mconverter} @endlatexonly */ + +/*** + @errors + @c MERROR_SYMBOL, @c MERROR_CODING + + @seealso + mconv_buffer_converter () */ + +MConverter * +mconv_stream_converter (MSymbol name, FILE *fp) +{ + MCodingSystem *coding; + MConverter *converter; + MConverterStatus *internal; + + if (name == Mnil) + name = mlocale_get_prop (mlocale__ctype, Mcoding); + coding = find_coding (name); + if (! coding) + MERROR (MERROR_CODING, NULL); + MSTRUCT_CALLOC (converter, MERROR_CODING); + MSTRUCT_CALLOC (internal, MERROR_CODING); + converter->internal_info = internal; + internal->coding = coding; + if (coding->resetter + && (*coding->resetter) (converter) < 0) + { + free (internal); + free (converter); + MERROR (MERROR_CODING, NULL); + } + + if (fseek (fp, 0, SEEK_CUR) < 0) + { + if (errno == EBADF) + { + free (internal); + free (converter); + return NULL; + } + internal->seekable = 0; + } + else + internal->seekable = 1; + internal->unread = mtext (); + internal->work_mt = mtext (); + mtext__enlarge (internal->work_mt, MAX_UTF8_CHAR_BYTES); + internal->fp = fp; + internal->binding = BINDING_STREAM; + + return converter; +} + +/*=*/ + +/***en + @brief Reset a code converter. + + The mconv_reset_converter () function resets code converter + $CONVERTER to the initial state. + + @return + If $CONVERTER->coding has its own reseter function, + mconv_reset_converter () returns the result of that function + applied to $CONVERTER. Otherwise it returns 0. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ò¥ê¥»¥Ã¥È¤¹¤ë + + ´Ø¿ô mconv_reset_converter () ¤Ï¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤ò½é´ü + ¾õÂÖ¤ËÌ᤹¡£ + + @return + ¤â¤· $CONVERTER->coding ¤Ë¥ê¥»¥Ã¥ÈÍѤδؿô¤¬ÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤Ê¤é¤Ð¡¢ + mconv_reset_converter () ¤Ï¤½¤Î´Ø¿ô¤Ë $CONVERTER ¤òŬÍѤ·¤¿·ë²Ì¤ò + ÊÖ¤·¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð0¤òÊÖ¤¹¡£ */ + +int +mconv_reset_converter (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + converter->nchars = converter->nbytes = 0; + converter->result = MCONVERSION_RESULT_SUCCESS; + internal->carryover_bytes = 0; + mtext_reset (internal->unread); + if (internal->coding->resetter) + return (*internal->coding->resetter) (converter); + return 0; +} + +/*=*/ + +/***en + @brief Free a code converter. + + The mconv_free_converter () function frees the code converter + $CONVERTER. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ò²òÊü¤¹¤ë + + ´Ø¿ô mconv_free_converter () ¤Ï¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤ò²òÊü + ¤¹¤ë¡£ */ + +void +mconv_free_converter (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + M17N_OBJECT_UNREF (internal->work_mt); + M17N_OBJECT_UNREF (internal->unread); + free (internal); + free (converter); +} + +/*=*/ + +/***en + @brief Bind a buffer to a code converter. + + The mconv_rebind_buffer () function binds buffer area of $N bytes + pointed to by $BUF to code converter $CONVERTER. Subsequent + decodings and encodings are done to/from this newly bound buffer + area. + + @return + This function always returns $CONVERTER. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤Ë¥Ð¥Ã¥Õ¥¡Îΰè¤ò·ë¤ÓÉÕ¤±¤ë + + ´Ø¿ô mconv_rebind_buffer () ¤Ï¡¢$BUF ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤¿Â礭¤µ $N ¥Ð + ¥¤¥È¤Î¥Ð¥Ã¥Õ¥¡Îΰè¤ò¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤Ë·ë¤ÓÉÕ¤±¤ë¡£¤³¤ì + °Ê¹ß¤Î¥Ç¥³¡¼¥É¤ª¤è¤Ó¥¨¥ó¥³¡¼¥É¤Ï¡¢¤³¤Î¿·¤¿¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥Ð¥Ã¥Õ¥¡ + Îΰè¤ËÂФ·¤Æ¹Ô¤Ê¤ï¤ì¤ë¤è¤¦¤Ë¤Ê¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë $CONVERTER ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mconv_rebind_buffer} @endlatexonly */ + +/*** + @seealso + mconv_rebind_stream () */ + +MConverter * +mconv_rebind_buffer (MConverter *converter, unsigned char *buf, int n) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + internal->buf = buf; + internal->used = 0; + internal->bufsize = n; + internal->binding = BINDING_BUFFER; + return converter; +} + +/*=*/ + +/***en + @brief Bind a stream to a code converter. + + The mconv_rebind_stream () function binds stream $FP to code + converter $CONVERTER. Following decodings and encodings are done + to/from this newly bound stream. + + @return + This function always returns $CONVERTER. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤Ë¥¹¥È¥ê¡¼¥à¤ò·ë¤ÓÉÕ¤±¤ë + + ´Ø¿ô mconv_rebind_stream () ¤Ï¡¢¥¹¥È¥ê¡¼¥à $FP ¤ò¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ + $CONVERTER ¤Ë·ë¤ÓÉÕ¤±¤ë¡£¤³¤ì°Ê¹ß¤Î¥Ç¥³¡¼¥É¤ª¤è¤Ó¥¨¥ó¥³¡¼¥É¤Ï¡¢ + ¤³¤Î¿·¤¿¤Ë·ë¤ÓÉÕ¤±¤é¤ì¤¿¥¹¥È¥ê¡¼¥à¤ËÂФ·¤Æ¹Ô¤Ê¤ï¤ì¤ë¤è¤¦¤Ë¤Ê¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë $CONVERTER ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mconv_rebind_stream} @endlatexonly */ + +/*** + @seealso + mconv_rebind_buffer () */ + +MConverter * +mconv_rebind_stream (MConverter *converter, FILE *fp) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + if (fseek (fp, 0, SEEK_CUR) < 0) + { + if (errno == EBADF) + return NULL; + internal->seekable = 0; + } + else + internal->seekable = 1; + internal->fp = fp; + internal->binding = BINDING_STREAM; + return converter; +} + +/*=*/ + +/***en + @brief Decode a byte sequence into an M-text. + + The mconv_decode () function decodes a byte sequence and appends + the result at the end of M-text $MT. The source byte sequence is + taken from currently bound the buffer area or the stream. + + @return + If the operation was successful, mconv_decode () returns updated + $MT. Otherwise it returns @c NULL and assigns an error code to + the external variable #merror_code. */ + +/***ja + @brief ¥Ð¥¤¥ÈÎó¤ò M-text ¤Ë¥Ç¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mconv_decode () ¤Ï¡¢¥Ð¥¤¥ÈÎó¤ò¥Ç¥³¡¼¥É¤·¤Æ¤½¤Î·ë²Ì¤ò M-text + $MT ¤ÎËöÈø¤ËÄɲ乤롣¥Ç¥³¡¼¥É¸µ¤Î¥Ð¥¤¥ÈÎó¤Ï¡¢¸½ºß·ë¤ÓÉÕ¤±¤é¤ì¤Æ¤¤¤ë + ¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼¥à¤«¤é¼è¤é¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_decode () ¤Ï¹¹¿·¤µ¤ì¤¿ $MT ¤òÊÖ¤¹¡£¤½ + ¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_rebind_buffer (), mconv_rebind_stream (), + mconv_encode (), mconv_encode_range (), + mconv_decode_buffer (), mconv_decode_stream () */ + +MText * +mconv_decode (MConverter *converter, MText *mt) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + int at_most = converter->at_most > 0 ? converter->at_most : -1; + int n; + + M_CHECK_READONLY (mt, NULL); + + if (! mt->data) + mtext__enlarge (mt, MAX_UTF8_CHAR_BYTES); + + converter->nchars = converter->nbytes = 0; + converter->result = MCONVERSION_RESULT_SUCCESS; + + n = mtext_nchars (internal->unread); + if (n > 0) + { + int limit = n; + int i; + + if (at_most > 0 && at_most < limit) + limit = at_most; + + for (i = 0, n -= 1; i < limit; i++, converter->nchars++, n--) + mtext_cat_char (mt, mtext_ref_char (internal->unread, n)); + mtext_del (internal->unread, n + 1, internal->unread->nchars); + if (at_most > 0) + { + if (at_most == limit) + return mt; + converter->at_most -= converter->nchars; + } + } + + if (internal->binding == BINDING_BUFFER) + { + (*internal->coding->decoder) (internal->buf + internal->used, + internal->bufsize - internal->used, + mt, converter); + internal->used += converter->nbytes; + } + else if (internal->binding == BINDING_STREAM) + { + unsigned char work[CONVERT_WORKSIZE]; + int last_block = converter->last_block; + int use_fread = at_most < 0 && internal->seekable; + + converter->last_block = 0; + while (1) + { + int nbytes, prev_nbytes; + + if (feof (internal->fp)) + nbytes = 0; + else if (use_fread) + nbytes = fread (work, sizeof (unsigned char), CONVERT_WORKSIZE, + internal->fp); + else + { + int c = getc (internal->fp); + + if (c != EOF) + work[0] = c, nbytes = 1; + else + nbytes = 0; + } + + if (ferror (internal->fp)) + { + converter->result = MCONVERSION_RESULT_IO_ERROR; + break; + } + + if (nbytes == 0) + converter->last_block = last_block; + prev_nbytes = converter->nbytes; + (*internal->coding->decoder) (work, nbytes, mt, converter); + if (converter->nbytes - prev_nbytes < nbytes) + { + if (use_fread) + fseek (internal->fp, converter->nbytes - prev_nbytes - nbytes, + SEEK_CUR); + else + ungetc (work[0], internal->fp); + break; + } + if (nbytes == 0 + || (converter->at_most > 0 + && converter->nchars == converter->at_most)) + break; + } + converter->last_block = last_block; + } + else /* internal->binding == BINDING_NONE */ + MERROR (MERROR_CODING, NULL); + + converter->at_most = at_most; + return ((converter->result == MCONVERSION_RESULT_SUCCESS + || converter->result == MCONVERSION_RESULT_INSUFFICIENT_SRC) + ? mt : NULL); +} + +/*=*/ + +/***en + @brief Decode a buffer area based on a coding system. + + The mconv_decode_buffer () function decodes $N bytes of buffer + area pointed to by $BUF based on the coding system $NAME. A + temporary code converter for decoding is automatically created + and freed. + + @return + If the operation was successful, mconv_decode_buffer () returns + the resulting M-text. Otherwise it returns NULL and assigns an + error code to the external variable #merror_code. */ + +/***ja + @brief ¥³¡¼¥É·Ï¤Ë´ð¤Å¤¤¤Æ¥Ð¥Ã¥Õ¥¡Îΰè¤ò¥Ç¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mconv_decode_buffer () ¤Ï¡¢$BUF ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤¿ $N ¥Ð¥¤¥È¤Î + ¥Ð¥Ã¥Õ¥¡Îΰè¤ò¡¢¥³¡¼¥É·Ï $NAME ¤Ë´ð¤Å¤¤¤Æ¥Ç¥³¡¼¥É¤¹¤ë¡£¥Ç¥³¡¼¥É¤Ë + ɬÍפʥ³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ÎºîÀ®¤È²òÊü¤Ï¼«Æ°Åª¤Ë¹Ô¤Ê¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_decode_buffer () ¤ÏÆÀ¤é¤ì¤¿ M-text ¤ò + ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼ + ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_decode (), mconv_decode_stream () */ + +MText * +mconv_decode_buffer (MSymbol name, unsigned char *buf, int n) +{ + MConverter *converter = mconv_buffer_converter (name, buf, n); + MText *mt; + + if (! converter) + return NULL; + mt = mtext (); + if (! mconv_decode (converter, mt)) + { + M17N_OBJECT_UNREF (mt); + mt = NULL; + } + mconv_free_converter (converter); + return mt; +} + +/*=*/ + +/***en + @brief Decode a stream input based on a coding system. + + The mconv_decode_stream () function decodes the entire byte + sequence read in from stream $FP based on the coding system $NAME. + A code converter for decoding is automatically created and freed. + + @return + If the operation was successful, mconv_decode_stream () returns + the resulting M-text. Otherwise it returns NULL and assigns an + error code to the external variable #merror_code. */ + +/***ja + @brief ¥³¡¼¥É·Ï¤Ë´ð¤Å¤¤¤Æ¥¹¥È¥ê¡¼¥àÆþÎϤò¥Ç¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mconv_decode_stream () ¤Ï¡¢¥¹¥È¥ê¡¼¥à $FP ¤«¤éÆɤ߹þ¤Þ¤ì¤ë¥Ð + ¥¤¥ÈÎóÁ´ÂΤò¡¢¥³¡¼¥É·Ï $NAME ¤Ë´ð¤Å¤¤¤Æ¥Ç¥³¡¼¥É¤¹¤ë¡£¥Ç¥³¡¼¥É¤Ëɬ + Íפʥ³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ÎºîÀ®¤È²òÊü¤Ï¼«Æ°Åª¤Ë¹Ô¤Ê¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_decode_stream () ¤ÏÆÀ¤é¤ì¤¿ M-text ¤òÊÖ + ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ + ¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_decode (), mconv_decode_buffer () */ + +MText * +mconv_decode_stream (MSymbol name, FILE *fp) +{ + MConverter *converter = mconv_stream_converter (name, fp); + MText *mt; + + if (! converter) + return NULL; + mt = mtext (); + if (! mconv_decode (converter, mt)) + { + M17N_OBJECT_UNREF (mt); + mt = NULL; + } + mconv_free_converter (converter); + return mt; +} + +/*=*/ + +/***en @brief Encode an M-text into a byte sequence. + + The mconv_encode () function encodes M-text $MT and writes the + resulting byte sequence into the buffer area or the stream that is + currently bound to code converter $CONVERTER. + + @return + If the operation was successful, mconv_encode () returns the + number of written bytes. Otherwise it returns -1 and assigns an + error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤ò¥Ð¥¤¥ÈÎó¤Ë¥¨¥ó¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mconv_encode () ¤Ï¡¢M-text $MT ¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼ + ¥¿ $CONVERTER ¤Ë¸½ºß·ë¤ÓÉÕ¤±¤é¤ì¤Æ¤¤¤ë¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼ + ¥à¤Ë½ñ¤­¹þ¤à¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_encode () ¤Ï½ñ¤­¹þ¤Þ¤ì¤¿¥Ð¥¤¥È¿ô¤òÊÖ¤¹¡£ + ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄê + ¤¹¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_rebind_buffer (), mconv_rebind_stream(), + mconv_decode (), mconv_encode_range () */ + +int +mconv_encode (MConverter *converter, MText *mt) +{ + return mconv_encode_range (converter, mt, 0, mtext_nchars (mt)); +} + +/*=*/ + +/***en + @brief Encode a part of an M-text + + The mconv_encode_range () function encodes the text between $FROM + (inclusive) and $TO (exclusive) in M-text $MT and writes the + resulting byte sequence into the buffer area or the stream that is + currently bound to code converter $CONVERTER. + + @return + If the operation was successful, mconv_encode_range () returns the + number of written bytes. Otherwise it returns -1 and assigns an + error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤Î°ìÉô¤ò¤ò¥Ð¥¤¥ÈÎó¤Ë¥¨¥ó¥³¡¼¥É¤¹¤ë + + ´Ø¿ô mconv_encode_range () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é + $TO ¡Ê´Þ¤Þ¤Ê¤¤¡Ë¤Þ¤Ç¤ÎÈϰϤΥƥ­¥¹¥È¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼ + ¥¿ $CONVERTER ¤Ë¸½ºß·ë¤ÓÉÕ¤±¤é¤ì¤Æ¤¤¤ë¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼ + ¥à¤Ë½ñ¤­¹þ¤à¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_encode_range () ¤Ï½ñ¤­¹þ¤Þ¤ì¤¿¥Ð¥¤¥È¿ô + ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ + ¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_rebind_buffer (), mconv_rebind_stream(), + mconv_decode (), mconv_encode () */ + +int +mconv_encode_range (MConverter *converter, MText *mt, int from, int to) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + M_CHECK_POS_X (mt, from, -1); + M_CHECK_POS_X (mt, to, -1); + if (to < from) + to = from; + + if (converter->at_most > 0 && from + converter->at_most < to) + to = from + converter->at_most; + + converter->nchars = converter->nbytes = 0; + converter->result = MCONVERSION_RESULT_SUCCESS; + + if (internal->binding == BINDING_BUFFER) + { + (*internal->coding->encoder) (mt, from, to, + internal->buf + internal->used, + internal->bufsize - internal->used, + converter); + internal->used += converter->nbytes; + } + else if (internal->binding == BINDING_STREAM) + { + unsigned char work[CONVERT_WORKSIZE]; + + while (from < to) + { + int written = 0; + int prev_nbytes = converter->nbytes; + int this_nbytes; + + (*internal->coding->encoder) (mt, from, to, work, + CONVERT_WORKSIZE, converter); + this_nbytes = converter->nbytes - prev_nbytes; + while (written < this_nbytes) + { + int wrtn = fwrite (work + written, sizeof (unsigned char), + this_nbytes - written, internal->fp); + + if (ferror (internal->fp)) + break; + written += wrtn; + } + if (written < this_nbytes) + { + converter->result = MCONVERSION_RESULT_IO_ERROR; + break; + } + from += converter->nchars; + } + } + else /* fail safe */ + MERROR (MERROR_CODING, -1); + + return ((converter->result == MCONVERSION_RESULT_SUCCESS + || converter->result == MCONVERSION_RESULT_INSUFFICIENT_DST) + ? converter->nbytes : -1); +} + +/*=*/ + +/***en + @brief Encode an M-text into a buffer area. + + The mconv_encode_buffer () function encodes M-text $MT based on + coding system $NAME and writes the resulting byte sequence into the + buffer area pointed to by $BUF. At most $N bytes are written. A + temporary code converter for encoding is automatically created + and freed. + + @return + If the operation was successful, mconv_encode_buffer () returns + the number of written bytes. Otherwise it returns -1 and assigns + an error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¥Ð¥Ã¥Õ¥¡Îΰè¤Ë½ñ¤­¹þ¤à + + ´Ø¿ô mconv_encode_buffer () ¤ÏM-text $MT ¤ò¥³¡¼¥É·Ï $NAME ¤Ë´ð¤Å¤¤ + ¤Æ¥¨¥ó¥³¡¼¥É¤·¡¢ÆÀ¤é¤ì¤¿¥Ð¥¤¥ÈÎó¤ò $BUF ¤Î»Ø¤¹¥Ð¥Ã¥Õ¥¡Îΰè¤Ë½ñ¤­¹þ + ¤à¡£$N ¤Ï½ñ¤­¹þ¤àºÇÂç¥Ð¥¤¥È¿ô¤Ç¤¢¤ë¡£¥¨¥ó¥³¡¼¥É¤ËɬÍפʥ³¡¼¥É¥³¥ó + ¥Ð¡¼¥¿¤ÎºîÀ®¤È²òÊü¤Ï¼«Æ°Åª¤Ë¹Ô¤Ê¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_encode_buffer () ¤Ï½ñ¤­¹þ¤Þ¤ì¤¿¥Ð¥¤¥È + ¿ô¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ + ¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_encode (), mconv_encode_stream () */ + +int +mconv_encode_buffer (MSymbol name, MText *mt, unsigned char *buf, int n) +{ + MConverter *converter = mconv_buffer_converter (name, buf, n); + int ret; + + if (! converter) + return -1; + ret = mconv_encode (converter, mt); + mconv_free_converter (converter); + return ret; +} + +/*=*/ + +/***en + @brief Encode an M-text to write to a stream. + + The mconv_encode_stream () function encodes M-text $MT based on + coding system $NAME and writes the resulting byte sequence to + stream $FP. A temporary code converter for encoding is + automatically created and freed. + + @return + If the operation was successful, mconv_encode_stream () returns + the number of written bytes. Otherwise it returns -1 and assigns + an error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¥¹¥È¥ê¡¼¥à¤Ë½ñ¤­¹þ¤à + + ´Ø¿ô mconv_encode_stream () ¤ÏM-text $MT ¤ò¥³¡¼¥É·Ï $NAME ¤Ë´ð¤Å¤¤ + ¤Æ¥¨¥ó¥³¡¼¥É¤·¡¢ÆÀ¤é¤ì¤¿¥Ð¥¤¥ÈÎó¤ò¥¹¥È¥ê¡¼¥à $FP ¤Ë½ñ¤­½Ð¤¹¡£¥¨¥ó + ¥³¡¼¥É¤ËɬÍפʥ³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ÎºîÀ®¤È²òÊü¤Ï¼«Æ°Åª¤Ë¹Ô¤Ê¤ï¤ì¤ë¡£ + + @return + ¤â¤·½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_encode_stream () ¤Ï½ñ¤­¹þ¤Þ¤ì¤¿¥Ð¥¤¥È¿ô + ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É + ¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_IO, @c MERROR_CODING + + @seealso + mconv_encode (), mconv_encode_buffer (), mconv_encode_file () */ + +int +mconv_encode_stream (MSymbol name, MText *mt, FILE *fp) +{ + MConverter *converter = mconv_stream_converter (name, fp); + int ret; + + if (! converter) + return -1; + ret = mconv_encode (converter, mt); + mconv_free_converter (converter); + return ret; +} + +/*=*/ + +/***en + @brief Read a character via a code converter. + + The mconv_getc () function reads one character from the buffer + area or the stream that is currently bound to code converter + $CONVERTER. The decoder of $CONVERTER is used to decode the byte + sequence. The internal status of $CONVERTER is updated + appropriately. + + @return + If the operation was successful, mconv_getc () returns the + character read in. If the input source reaches EOF, it returns @c + EOF without changing the external variable #merror_code. If an + error is detected, it returns @c EOF and assigns an error code to + #merror_code. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿·Ðͳ¤Ç1ʸ»úÆɤà + + ´Ø¿ô mconv_getc () ¤Ï¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤Ë¸½ºß·ë¤ÓÉÕ¤± + ¤é¤ì¤Æ¤¤¤ë¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼¥à¤«¤é1ʸ»ú¤òÆɤ߹þ¤à¡£¥Ð¥¤ + ¥ÈÎó¤Î¥Ç¥³¡¼¥É¤Ë¤Ï $CONVERTER ¤Î¥Ç¥³¡¼¥À¤¬ÍѤ¤¤é¤ì¤ë¡£$CONVERTER + ¤ÎÆâÉô¾õÂÖ¤ÏɬÍפ˱þ¤¸¤Æ¹¹¿·¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_getc () ¤ÏÆɤ߹þ¤Þ¤ì¤¿Ê¸»ú¤òÊÖ¤¹¡£ÆþÎϸ»¤¬ + EOF ¤Ë㤷¤¿¾ì¹ç¤Ï¡¢³°ÉôÊÑ¿ô #merror_code ¤òÊѤ¨¤º¤Ë @c EOF ¤òÊÖ¤¹¡£ + ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c EOF ¤òÊÖ¤·¡¢#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É + ¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CODING + + @seealso + mconv_ungetc (), mconv_putc (), mconv_gets () */ + +int +mconv_getc (MConverter *converter) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + int at_most = converter->at_most; + + mtext_reset (internal->work_mt); + converter->at_most = 1; + mconv_decode (converter, internal->work_mt); + converter->at_most = at_most; + return (converter->nchars == 1 + ? STRING_CHAR (internal->work_mt->data) + : EOF); +} + +/*=*/ + +/***en + @brief Push a character back to a code converter. + + The mconv_ungetc () function pushes character $C back to code + converter $CONVERTER. Any number of characters can be pushed + back. The lastly pushed back character is firstly read by the + subsequent mconv_getc () call. The characters pushed back are + registered only in $CONVERTER; they are not written to the input + source. The internal status of $CONVERTER is updated + appropriately. + + @return + If the operation was successful, mconv_ungetc () returns $C. + Otherwise it returns @c EOF and assigns an error code to the + external variable #merror_code. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤Ë1ʸ»úÌ᤹ + + ´Ø¿ô mconv_ungetc () ¤Ï¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤Ëʸ»ú $C ¤ò + ²¡¤·Ì᤹¡£²¡¤·Ì᤻¤ëʸ»ú¿ô¤ËÀ©¸Â¤Ï¤Ê¤¤¡£¤³¤Î¸å¤Ë mconv_getc () ¤ò + ¸Æ¤Ó½Ð¤¹¤È¡¢ºÇ¸å¤ËÌᤵ¤ì¤¿Ê¸»ú¤¬ºÇ½é¤ËÆɤޤì¤ë¡£²¡¤·Ìᤵ¤ì¤¿Ê¸»ú¤Ï + $CONVERTER ¤ÎÆâÉô¤ËÃߤ¨¤é¤ì¤ë¤À¤±¤Ç¤¢¤ê¡¢¼ÂºÝ¤ËÆþÎϸ»¤Ë½ñ¤­¹þ¤Þ¤ì + ¤ë¤ï¤±¤Ç¤Ï¤Ê¤¤¡£$CONVERTER ¤ÎÆâÉô¾õÂÖ¤ÏɬÍפ˱þ¤¸¤Æ¹¹¿·¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_ungetc () ¤Ï $C ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c + EOF ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CODING, @c MERROR_CHAR + + @seealso + mconv_getc (), mconv_putc (), mconv_gets () */ + +int +mconv_ungetc (MConverter *converter, int c) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + M_CHECK_CHAR (c, EOF); + + converter->result = MCONVERSION_RESULT_SUCCESS; + mtext_cat_char (internal->unread, c); + return c; +} + +/*=*/ + +/***en + @brief Write a character via a code converter. + + The mconv_putc () function writes character $C to the buffer area + or the stream that is currently bound to code converter + $CONVERTER. The encoder of $CONVERTER is used to encode the + character. The number of bytes actually written is set to the @c + nbytes member of $CONVERTER. The internal status of $CONVERTER + is updated appropriately. + + @return + If the operation was successful, mconv_putc () returns $C. + If an error is detected, it returns @c EOF and assigns + an error code to the external variable #merror_code. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ò·Ðͳ¤Ç1ʸ»ú½ñ¤¯ + + ´Ø¿ô mconv_putc () ¤Ï¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤Ë¸½ºß·ë¤ÓÉÕ¤± + ¤é¤ì¤Æ¤¤¤ë¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼¥à¤Ëʸ»ú $C ¤ò½ñ¤­½Ð¤¹¡£Ê¸»ú + ¤Î¥¨¥ó¥³¡¼¥É¤Ë¤Ï $CONVERTER ¤Î¥¨¥ó¥³¡¼¥À¤¬ÍѤ¤¤é¤ì¤ë¡£¼ÂºÝ¤Ë½ñ¤­½Ð + ¤µ¤ì¤¿¥Ð¥¤¥È¿ô¤Ï¡¢$CONVERTER ¤Î ¥á¥ó¥Ð¡¼ @c nbytes ¤Ë¥»¥Ã¥È¤µ¤ì¤ë¡£ + $CONVERTER ¤ÎÆâÉô¾õÂÖ¤ÏɬÍפ˱þ¤¸¤Æ¹¹¿·¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_putc () ¤Ï $C ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç + ¤Ï @c EOF ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CODING, @c MERROR_IO, @c MERROR_CHAR + + @seealso + mconv_getc (), mconv_ungetc (), mconv_gets () */ + +int +mconv_putc (MConverter *converter, int c) +{ + MConverterStatus *internal = (MConverterStatus *) converter->internal_info; + + M_CHECK_CHAR (c, EOF); + mtext_reset (internal->work_mt); + mtext_cat_char (internal->work_mt, c); + if (mconv_encode_range (converter, internal->work_mt, 0, 1) < 0) + return EOF; + return c; +} + +/*=*/ + +/***en + @brief Read a line using a code converter. + + The mconv_gets () function reads one line from the buffer area or + the stream that is currently bound to code converter $CONVERTER. + The decoder of $CONVERTER is used for decoding. The decoded + character sequence is appended at the end of M-text $MT. The + final newline character in the original byte sequence is not + appended. The internal status of $CONVERTER is updated + appropriately. + + @return + If the operation was successful, mconv_gets () returns the + modified $MT. If it encounters EOF without reading a single + character, it returns $MT without changing it. If an error is + detected, it returns @c NULL and assigns an error code to @c + merror_code. */ + +/***ja + @brief ¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿¤ò»È¤Ã¤Æ1¹ÔÆɤà + + ´Ø¿ô mconv_gets () ¤Ï¡¢¥³¡¼¥É¥³¥ó¥Ð¡¼¥¿ $CONVERTER ¤Ë¸½ºß·ë¤ÓÉÕ¤± + ¤é¤ì¤Æ¤¤¤ë¥Ð¥Ã¥Õ¥¡Îΰ褢¤ë¤¤¤Ï¥¹¥È¥ê¡¼¥à¤«¤é1¹Ô¤òÆɤ߹þ¤à¡£¥Ð¥¤¥È + Îó¤Î¥Ç¥³¡¼¥É¤Ë¤Ï $CONVERTER ¤Î¥Ç¥³¡¼¥À¤¬ÍѤ¤¤é¤ì¤ë¡£¥Ç¥³¡¼¥É¤µ¤ì¤¿ + ʸ»úÎó¤Ï M-text $MT ¤ÎËöÈø¤ËÄɲ䵤ì¤ë¡£¸µ¤Î¥Ð¥¤¥ÈÎó¤Î½ªÃ¼²þ¹Ôʸ»ú + ¤ÏÄɲ䵤ì¤Ê¤¤¡£$CONVERTER ¤ÎÆâÉô¾õÂÖ¤ÏɬÍפ˱þ¤¸¤Æ¹¹¿·¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mconv_gets () ¤ÏÊѹ¹¤µ¤ì¤¿ $MT ¤òÊÖ¤¹¡£¤â¤·1ʸ»ú + ¤âÆɤޤº¤Ë EOF ¤ËÅö¤¿¤Ã¤¿¾ì¹ç¤Ï¡¢$MT ¤òÊѹ¹¤»¤º¤Ë¤½¤Î¤Þ¤ÞÊÖ¤¹¡£¥¨ + ¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_CODING + + @seealso + mconv_getc (), mconv_ungetc (), mconv_putc () */ + +MText * +mconv_gets (MConverter *converter, MText *mt) +{ + int c; + + M_CHECK_READONLY (mt, NULL); + while (1) + { + c = mconv_getc (converter); + if (c == EOF || c == '\n') + break; + mtext_cat_char (mt, c); + } + if (c == EOF && converter->result != MCONVERSION_RESULT_SUCCESS) + /* mconv_getc () sets merror_code */ + return NULL; + return mt; +} + +/*=*/ + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/coding.h b/src/coding.h new file mode 100644 index 0000000..cb256e6 --- /dev/null +++ b/src/coding.h @@ -0,0 +1,31 @@ +/* coding.h -- header file for the code conversion module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_CODING_H_ +#define _M17N_CODING_H_ + +extern void mconv__define_coding_from_charset (MSymbol sym); +extern void mconv__register_charset_coding (MSymbol name); + +extern int mcoding__load_from_database (); + +#endif /* _M17N_CODING_H_ */ diff --git a/src/database.c b/src/database.c new file mode 100644 index 0000000..4f83f8f --- /dev/null +++ b/src/database.c @@ -0,0 +1,852 @@ +/* database.c -- database module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nDatabase + @brief The m17n database and API for it + + The m17n library dynamically acquires various kinds of information + in need from data in the m17n database. Application + programs can also add/load their original data to/from the m17n + database. The m17n database contains multiple heterogeneous data, + and each data is identified by four tags; TAG1, TAG2, TAG3, TAG4. + Each tag must be a symbol. + + TAG1 specifies the type of data stored in the database as below. + +
    + +
  • If TAG1 is #Mchar_table, the data is of the @e chartable @e + type and provides information about each character. In this case, + TAG2 specifies the type of the information and must be #Msymbol, + #Minteger, #Mstring, #Mtext, or #Mplist. TAG3 and TAG4 can be any + symbols. + +
  • If TAG1 is #Mcharset, the data is of the @e charset @e type + and provides a decode/encode mapping table for a charset. In this + case, TAG2 must be a symbol representing a charset. TAG3 and TAG4 + can be any symbols. + +
  • If TAG1 is neither #Mchar_table nor #Mcharset, the data is of + the @e plist @e type. See the documentation of the mdatabase_load + () function for the details. In this case, TAG2, TAG3, and TAG4 + can be any symbols. + +
+ + Below, the notation \ means a data with + those tags. + + Application programs first calls the mdatabase_find () function to + get a pointer to an object of the type #MDatabase. That object + holds information about the specified data. When it is + successfully returned, the mdatabase_load () function loads the + data. The implementation of the structure #MDatabase is + concealed from application programs. +*/ + +/***ja + @addtogroup m17nDatabase + @brief ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿ + ¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò m17n + ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n ¸À + ¸ì¾ðÊó¥Ù¡¼¥¹¤ÏÊ£¿ô¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¤Ê¤ê¡¢³Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ï£´¤Ä¤Î¥¿ + ¥° TAG1, TAG2, TAG3, TAG4¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£ + + TAG1 ¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤ò¼¨¤¹¡£ + + TAG1 ¤¬ #Mchar_table ¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ï @e chartable¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢ + ³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG2 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë + ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢°Ê²¼¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + @li #Msymbol @li #Minteger @li #Mstring @li #Mtext @li #Mplist + + TAG3 ¤È TAG4 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ + + TAG1 ¤¬ #Mcharset ¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ï @e charset¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢Ê¸ + »ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG2 ¤Ï + ʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG3 ¤È TAG4 ¤ÏǤ°Õ¤Î¥·¥ó + ¥Ü¥ë¤Ç¤è¤¤¡£ + + TAG1 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¥Ù¡¼ + ¥¹¤Ï @e plist ¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load + () ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG2¡¢TAG3¡¢TAG4 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë + ¤Ç¤è¤¤¡£ + + °Ê²¼¡¢ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \ ¤È¤¤ + ¤¦·Á¼°¤Çɽ¤ï¤¹¡£ + + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () »È¤Ã¤Æ¥Ç¡¼ + ¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase ·¿¡Ë¤Ø¤Î + ¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () ¤Ë¤è¤Ã¤Æ¼ÂºÝ + ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂÎ #MDatabase ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì + ¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ + + @latexonly \IPAlabel{database} @endlatexonly +*/ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "mtext.h" +#include "character.h" +#include "charset.h" +#include "database.h" +#include "coding.h" +#include "plist.h" + +/** The file containing a list of databases. */ +#define MDB_DIR "mdb.dir" +#define MDB_DIR_LEN 8 + +/** List of database directories. */ +static MPlist *mdb_dir_list; + +/** Structure for a data in the m17n database. */ + +struct MDatabase +{ + /** Tags to identify the data. [0] specifies the type of + database. If it is #Mchar_table, the type is @e chartable, if + it is #Mcharset, the type is @e charset, otherwise the type is + @e plist. */ + MSymbol tag[4]; + + void *(*loader) (MSymbol *tags, void *extra_info); + + /** The meaning of the value is dependent on . If + is load_database (), the value is a string of the file name that + contains the data. */ + void *extra_info; +}; + +/** List of all data. */ +struct MDatabaseList +{ + int size, inc, used; + MDatabase *mdbs; +}; + +static struct MDatabaseList mdb_list; + + +static int +read_number (char *buf, int *i) +{ + int idx = *i; + int c = buf[idx++]; + int n; + + if (!c) + return -1; + + while (c && isspace (c)) c = buf[idx++]; + + if (c == '0') + { + if (buf[idx] == 'x') + { + for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16; + idx++) + c = (c << 4) | n; + *i = idx; + return c; + } + c = 0; + } + else if (c == '\'') + { + c = buf[idx++]; + if (c == '\\') + { + c = buf[idx++]; + n = escape_mnemonic[c]; + if (n != 255) + c = n; + } + while (buf[idx] && buf[idx++] != '\''); + *i = idx; + return c; + } + else if (hex_mnemonic[c] < 10) + c -= '0'; + else + return -1; + + while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10) + c = (c * 10) + n, idx++; + *i = idx; + return c; +} + + +/** Load a data of type @c chartable from the file FD, and return the + newly created chartable. */ + +static void * +load_chartable (FILE *fp, MSymbol type) +{ + int c, from, to; + char buf[1024]; + void *val; + MCharTable *table; + + if (! fp) + MERROR (MERROR_DB, NULL); + + table = mchartable (type, (type == Msymbol ? (void *) Mnil + : type == Minteger ? (void *) -1 + : NULL)); + + while (! feof (fp)) + { + int i, len; + + for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++) + buf[len] = c; + buf[len] = '\0'; + if (hex_mnemonic[(unsigned) buf[0]] >= 10) + /* skip comment/invalid line */ + continue; + i = 0; + from = read_number (buf, &i); + if (buf[i] == '-') + i++, to = read_number (buf, &i); + else + to = from; + if (from < 0 || to < 0) + goto label_error; + + while (buf[i] && isspace ((unsigned) buf[i])) i++; + c = buf[i]; + if (!c) + break; + + if (type == Mstring) + { + /* VAL is a C-string. */ + if (! (val = strdup (buf + i))) + MEMORY_FULL (MERROR_DB); + } + else if (type == Minteger) + { + /* VAL is an integer. */ + int positive = 1; + int n; + + if (c == '-') + i++, positive = -1; + n = read_number (buf, &i); + if (n < 0) + goto label_error; + val = (void *) (n * positive); + } + else if (type == Mtext) + { + /* VAL is an M-text. */ + MText *mt; + if (c == '"') + mt = mconv_decode_buffer (Mcoding_utf_8, + (unsigned char *) (buf + i), + len - i - 1); + else + { + mt = mtext (); + while ((c = read_number (buf, &i)) >= 0) + mt = mtext_cat_char (mt, c); + } + val = (void *) mt; + } + else if (type == Msymbol) + { + if (! strcmp (buf + i, "nil")) + val = (void *) Mnil; + else + val = (void *) msymbol (buf + i); + } + else if (type == Mplist) + { + val = (void *) mplist__from_string ((unsigned char *) buf + i, + strlen (buf + i)); + } + else + val = NULL; + + if (from == to) + mchartable_set (table, from, val); + else + mchartable_set_range (table, from, to, val); + } + return table; + + label_error: + M17N_OBJECT_UNREF (table); + MERROR (MERROR_DB, NULL); +} + + +/** Load a data of type @c charset from the file FD. */ + +static void * +load_charset (FILE *fp, MSymbol charset_name) +{ + MCharset *charset = MCHARSET (charset_name); + int *decoder; + MCharTable *encoder; + int size; + int i, c; + int found = 0; + MPlist *plist; + + if (! charset) + MERROR (MERROR_DB, NULL); + size = (charset->code_range[15] + - (charset->min_code - charset->code_range_min_code)); + MTABLE_MALLOC (decoder, size, MERROR_DB); + for (i = 0; i < size; i++) + decoder[i] = -1; + encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE); + + while ((c = getc (fp)) != EOF) + { + unsigned code1, code2, c1, c2; + int idx1, idx2; + char buf[256]; + + ungetc (c, fp); + fgets (buf, 256, fp); + if (c != '#') + { + if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3) + { + idx1 = CODE_POINT_TO_INDEX (charset, code1); + if (idx1 >= size) + continue; + idx2 = CODE_POINT_TO_INDEX (charset, code2); + if (idx2 >= size) + idx2 = size - 1; + c2 = c1 + (idx2 - idx1); + } + else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2) + { + idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1); + if (idx1 >= size) + continue; + c2 = c1; + } + else + continue; + if (idx1 >= 0 && idx2 >= 0) + { + decoder[idx1] = c1; + mchartable_set (encoder, c1, (void *) code1); + for (idx1++, c1++; idx1 <= idx2; idx1++, c1++) + { + code1 = INDEX_TO_CODE_POINT (charset, idx1); + decoder[idx1] = c1; + mchartable_set (encoder, c1, (void *) code1); + } + found++; + } + } + } + + if (! found) + { + free (decoder); + M17N_OBJECT_UNREF (encoder); + return NULL; + } + plist = mplist (); + mplist_add (plist, Mt, decoder); + mplist_add (plist, Mt, encoder); + return plist; +} + +static char * +gen_database_name (char *buf, MSymbol *tags) +{ + int i; + + strcpy (buf, msymbol_name (tags[0])); + for (i = 1; i < 4; i++) + { + strcat (buf, ", "); + strcat (buf, msymbol_name (tags[i])); + } + return buf; +} + +static void * +load_database (MSymbol *tags, void *extra_info) +{ + FILE *fp; + char *filename = (char *) extra_info; + void *value; + + if (filename[0] == '/') + fp = fopen (filename, "r"); + else + { + MPlist *plist; + char path[PATH_MAX]; + + MPLIST_DO (plist, mdb_dir_list) + { + strcpy (path, (char *) MPLIST_VAL (plist)); + strcat (path, filename); + fp = fopen (path, "r"); + if (fp) + break; + } + } + if (! fp) + MERROR (MERROR_DB, NULL); + + if (tags[0] == Mchar_table) + value = load_chartable (fp, tags[1]); + else if (tags[0] == Mcharset) + value = load_charset (fp, tags[1]); + else + value = mplist__from_file (fp); + fclose (fp); + + if (! value) + MERROR (MERROR_DB, NULL); + return value; +} + + +/** Copy DIRNAME to a newly allocated memory and return it. If + DIRNAME does not end with a slash, append a slash to the new memory. */ + +static char * +duplicate_dirname (char *dirname) +{ + struct stat buf; + int len; + char *str; + + if (! dirname + || stat (dirname, &buf) < 0) + return NULL; + + len = strlen (dirname); + MTABLE_MALLOC (str, len + 2, MERROR_DB); + memcpy (str, dirname, len + 1); + if (str[len - 1] != '/') + { + str[len] = '/'; + str[len + 1] = '\0'; + } + return str; +} + + +/* Internal API */ + +int +mdatabase__init () +{ + char *dir; + int i; + MPlist *plist; + FILE *fp; + + Mchar_table = msymbol ("char-table"); + + mdb_dir_list = mplist (); + /** The macro M17NDIR specifies a directory where the system-wide + MDB_DIR file exists. */ + if ((dir = duplicate_dirname (M17NDIR))) + mplist_set (mdb_dir_list, Mt, dir); + + /* The variable mdatabase_dir specifies a directory where an + application program specific MDB_DIR file exists. */ + if ((dir = duplicate_dirname (mdatabase_dir))) + mplist_push (mdb_dir_list, Mt, dir); + + /* The environment variable M17NDIR (if non-NULL) specifies a + directory where a user specific MDB_DIR file exists. */ + if ((dir = duplicate_dirname (getenv ("M17NDIR")))) + mplist_push (mdb_dir_list, Mt, dir); + + MLIST_INIT1 (&mdb_list, mdbs, 256); + MPLIST_DO (plist, mdb_dir_list) + { + MPlist *pl, *p; + int len; + char path[PATH_MAX]; + + dir = (char *) MPLIST_VAL (plist); + len = strlen (dir); + if (len + MDB_DIR_LEN >= PATH_MAX) + continue; + memcpy (path, dir, len); + memcpy (path + len, MDB_DIR, MDB_DIR_LEN); + if (! (fp = fopen (path, "r"))) + continue; + pl = mplist__from_file (fp); + fclose (fp); + if (! pl) + continue; + MPLIST_DO (p, pl) + { + MDatabase mdb; + MPlist *p1; + int nbytes; + + if (! MPLIST_PLIST_P (p)) + continue; + for (i = 0, p1 = MPLIST_PLIST (p); + i < 4 && MPLIST_KEY (p1) == Msymbol; + i++, p1 = MPLIST_NEXT (p1)) + mdb.tag[i] = MPLIST_SYMBOL (p1); + if (i == 0 + || ! MPLIST_MTEXT_P (p1)) + continue; + for (; i < 4; i++) + mdb.tag[i] = Mnil; + if (mdatabase_find (mdb.tag[0], mdb.tag[1], + mdb.tag[2], mdb.tag[3])) + continue; + + mdb.loader = load_database; + nbytes = mconv_encode_buffer (Mcoding_utf_8, MPLIST_MTEXT (p1), + (unsigned char *) path, PATH_MAX); + if (nbytes < 0 || nbytes >= PATH_MAX) + continue; + path[nbytes++] = '\0'; + mdb.extra_info = (void *) strdup (path); + MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB); + } + M17N_OBJECT_UNREF (pl); + } + + mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol)) + mdatabase_find); + mdatabase__loader = (void *(*) (void *)) mdatabase_load; + + return 0; +} + +void +mdatabase__fini (void) +{ + int i; + MPlist *plist; + + MPLIST_DO (plist, mdb_dir_list) + free (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (mdb_dir_list); + + for (i = 0; i < mdb_list.used; i++) + { + MDatabase *mdb = mdb_list.mdbs + i; + + if (mdb->loader == load_database) + free (mdb->extra_info); + } + MLIST_FREE1 (&mdb_list, mdbs); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nDatabase */ +/*** @{ */ + +/*=*/ +/***en + @brief Directory for application specific data. + + If an application program wants to provide a data specific to the + program or a data overriding what supplied by the m17n database, + it must set this variable to a name of directory that contains the + data files before it calls the macro M17N_INIT (). The directory + may contain a file "mdb.dir" which contains a list of data + definitions in the format described in @ref mdbDir "mdbDir(5)". + + The default value is NULL. */ + +char *mdatabase_dir; + +/*=*/ +/***en + @brief Look for a data in the database. + + The mdatabase_find () function searches the m17n database for a + data who has tags $TAG1 through $TAG4, and returns a pointer to + the data. If such a database is not found, it returns @c + NULL. */ + +/***ja + @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òõ¤¹ + + ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG1 ¤«¤é + $TAG4 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ + ¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mdatabase_find} @endlatexonly */ + +MDatabase * +mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) +{ + int i; + + for (i = 0; i < mdb_list.used; i++) + { + MDatabase *mdb = mdb_list.mdbs + i; + + if (tag0 == mdb->tag[0] + && tag1 == mdb->tag[1] + && tag2 == mdb->tag[2] + && tag3 == mdb->tag[3]) + return mdb; + } + return NULL; +} + +/*=*/ +/***en + @brief Return a data list of the m17n database. + + The mdatabase_list () function searches the m17n database for data + who have tags $TAG1 through $TAG4, and returns their list by a + plist. The value #Mnil in $TAGn means a wild card that matches + any tag. Each element of the plist has key #Mt and value a + pointer to type #MDatabase. */ + +MPlist * +mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) +{ + int i; + MPlist *plist = NULL, *pl; + + for (i = 0; i < mdb_list.used; i++) + { + MDatabase *mdb = mdb_list.mdbs + i; + + if ((tag0 == Mnil || tag0 == mdb->tag[0]) + && (tag1 == Mnil || tag1 == mdb->tag[1]) + && (tag2 == Mnil || tag2 == mdb->tag[2]) + && (tag3 == Mnil || tag3 == mdb->tag[3])) + { + if (! plist) + plist = pl = mplist (); + pl = mplist_add (pl, Mt, mdb); + } + } + return plist; +} + + + +/*=*/ +/***en + @brief Define a data of the m17n database. + + The mdatabase_define () function defines a data that has tags + $TAG1 through $TAG4 and additional information $EXTRA_INFO. + + $LOADER is a pointer to a function that loads the data from the + database. This function is called from the mdatabase_load () + function with the two arguments $TAGS and $EXTRA_INFO. Here, + $TAGS is the array of $TAG1 through $TAG4. + + If $LOADER is @c NULL, the default loader of the m17n library is + used. In this case, $EXTRA_INFO must be a string specifying a + filename that contains the data. + + @return + If the operation was successful, mdatabase_define () returns a + pointer to the defined data, which can be used as an argument to + mdatabase_load (). Otherwise, it returns @c NULL. */ + +/***ja + @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÄêµÁ¤¹¤ë + + ´Ø¿ô mdatabase_define () ¤Ï $TAG1 ¤«¤é $TAG4 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղà + ¾ðÊó $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÄêµÁ¤¹¤ë¡£ + + $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢ + ¤ë¡£¤³¤Î´Ø¿ô¤Ï mdatabase_load () ¤«¤é $MDB ¤È $EXTRA_INFO ¤È¤¤¤¦2 + ¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç $MDB ¤Ï mdatabase_load () ¤ËÅÏ + ¤µ¤ì¤¿°ú¿ô¤Ç¤¢¤ë¡£ + + ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì + ¤ë¡£¤³¤Î¥í¡¼¥À¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò $EXTRA_INFO ¤Ë»ØÄꤵ¤ì¤¿Ì¾Á°¤Î¥Õ¥¡ + ¥¤¥ë¤«¤é¥í¡¼¥É¤¹¤ë¡£ + + @return + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î + ¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mdatabase_define} @endlatexonly */ + +/*** + @seealso + mdatabase_load (), mdatabase_define () */ + +MDatabase * +mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3, + void *(*loader) (MSymbol *, void *), + void *extra_info) +{ + MDatabase *mdb; + + mdb = mdatabase_find (tag0, tag1, tag2, tag3); + if (! mdb) + { + MDatabase template; + + template.tag[0] = tag0, template.tag[1] = tag1; + template.tag[2] = tag2, template.tag[3] = tag3; + MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB); + mdb = mdb_list.mdbs + (mdb_list.used - 1); + } + mdb->loader = loader ? loader : load_database; + mdb->extra_info = extra_info; + if (mdb->loader == load_database) + mdb->extra_info = strdup ((char *) extra_info); + return (&(mdb_list.mdbs[mdb_list.used - 1])); +} + +/*=*/ +/***en + @brief Load a data from the database. + + The mdatabase_load () function loads a data specified in $MDB and + returns the contents. The type of contents depends on the type of + the data. + + If the data is of the @e plist type, this function returns a + pointer to @e plist. + + If the database is of the @e chartable type, it returns a + chartable. The default value of the chartable is set according to + the second tag of the database as below: + +
    + +
  • If the tag is #Msymbol, the default value is #Mnil. +
  • If the tag is #Minteger, the default value is -1. +
  • Otherwise, the default value is @c NULL. + +
+ + If the data is of the charset type, it returns a plist of length 2 + (keys are both #Mt). The value of the first element is an array + of integers that maps code points to the corresponding character + codes. The value of the second element is a chartable of integers + that does the reverse mapping. The charset must be defined in + advance. */ + + +/***ja + @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë + + ´Ø¿ô mdatabase_load () ¤Ï $MDB ¤¬»Ø¤¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤·¡¢¤½¤Î + Ãæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£ + + ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ plist ¥¿¥¤¥×¤Ê¤é¤Ð¡¢ @e plist ¤òÊÖ¤¹¡£ + + ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ chartable ¥¿¥¤¥×¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£Ê¸»ú + ¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢Âè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£ + + @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï #Mnil + @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï -1 + @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï @c NULL + + ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ charset ¥¿¥¤¥×¤Ê¤é¤ÐŤµ 2 ¤Î @c plist ¤òÊÖ¤¹¡Ê¥­¡¼ + ¤Ï¶¦¤Ë #Mt ¡Ë¡£ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã + ¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡££²ÈÖÌܤÎÍ×ÁǤÎÃͤϤ½¤ÎµÕ¤Î¥Þ¥Ã¥×¤ò¤¹¤ëʸ»ú + ¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + @latexonly \IPAlabel{mdatabase_load} @endlatexonly + */ + +/*** + @seealso + mdatabase_load (), mdatabase_define () */ + +void * +mdatabase_load (MDatabase *mdb) +{ + int mdebug_mask = MDEBUG_DATABASE; + char buf[256]; + + MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n", + gen_database_name (buf, mdb->tag)); + return (*mdb->loader) (mdb->tag, mdb->extra_info); +} + +/*=*/ +/***en + @brief Get tags of a data. + + The mdatabase_tag () function returns an array of tags (symbols) + that identify the data in $MDB. The length of the array is + four. */ + +/***ja + @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥¿¥°¤òÆÀ¤ë + + ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛ + Îó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï 4 ¤Ç¤¢¤ë¡£ + + @latexonly \IPAlabel{mdatabase_tag} @endlatexonly */ + +MSymbol * +mdatabase_tag (MDatabase *mdb) +{ + return mdb->tag; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/database.h b/src/database.h new file mode 100644 index 0000000..a6d948d --- /dev/null +++ b/src/database.h @@ -0,0 +1,30 @@ +/* database.h -- header file for the database module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_DATABASE_H_ +#define _M17N_DATABASE_H_ + +#ifndef M17NDIR +#define M17NDIR "/usr/local/share/m17n" +#endif + +#endif /* not _M17N_DATABASE_H_ */ diff --git a/src/draw.c b/src/draw.c new file mode 100644 index 0000000..28b4981 --- /dev/null +++ b/src/draw.c @@ -0,0 +1,2635 @@ +/* draw.c -- drawing module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nDraw + @brief Drawing M-text on a window. + + The m17n GUI API provides functions to draw M-texts. + + The fonts used for drawing are selected automatically based on the + fontset and the properties of a face. A face also specifies the + appearance of M-texts, i.e. font size, color, underline, etc. + + The drawing format of M-texts can be controlled in a variety of + ways, which provides powerful 2-dimensional layouting + facility. */ + +/***ja + @addtogroup m17nDraw + @brief M-text ɽ¼¨¤Î¤¿¤á¤Î m17n-gui API + + m17n-gui API ¤Ë¤Ï¡¢M-text ¤òɽ¼¨¤¹¤ë¤¿¤á¤Î´Ø¿ô¤¬ÍÑ°Õ¤µ¤ì¤Æ¤¤¤ë¡£É½ + ¼¨¤ËÍѤ¤¤é¤ì¤ë¥Õ¥©¥ó¥È¤Ï¡¢¥Õ¥ì¡¼¥à¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤È¥×¥í¥Ñ¥Æ¥£¤Ë´ð + ¤Å¤¤¤Æ¼«Æ°Åª¤Ë·èÄꤵ¤ì¤ë¡£¤Þ¤¿¡¢¿§¤ä²¼Àþ¤Ê¤É¤Î°À­¤â¥Õ¥ì¡¼¥à¤Ë¤è¤Ã + ¤Æ·èÄꤵ¤ì¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "textprop.h" +#include "internal-gui.h" +#include "face.h" +#include "font.h" + +#ifdef HAVE_FRIBIDI +#include +#endif /* HAVE_FRIBIDI */ + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +static MSymbol M_glyph_string; + +/* Special scripts */ +static MSymbol Mlatin, Minherited; +/* Special categories */ +static MSymbol McatCc, McatCf; + + +/* Glyph-string composer. */ + +static MSymbol MbidiR; +static MSymbol MbidiAL; +static MSymbol MbidiRLE; +static MSymbol MbidiRLO; +static MSymbol MbidiBN; +static MSymbol MbidiS; + +static void +visual_order (MGlyphString *gstring) +{ + int len = gstring->used - 2; + MGlyph *glyphs; + int *idx = alloca (sizeof (int) * len); + int gidx; + int bidi_sensitive = gstring->control.orientation_reversed; + int size = 0; + MGlyph *g = MGLYPH (1); + int i; +#ifdef HAVE_FRIBIDI + FriBidiCharType base = (gstring->control.orientation_reversed + ? FRIBIDI_TYPE_RTL : FRIBIDI_TYPE_LTR); + FriBidiChar *logical = alloca (sizeof (FriBidiChar) * len); + FriBidiChar *visual; + FriBidiStrIndex *indices; + FriBidiLevel *levels = alloca (sizeof (FriBidiLevel) * len); +#else /* not HAVE_FRIBIDI */ + int *logical = alloca (sizeof (int) * len); + int *indices; + char *levels = alloca (len); +#endif /* not HAVE_FRIBIDI */ + + while (g->type != GLYPH_ANCHOR) + { + MSymbol bidi = (MSymbol) mchar_get_prop (g->c, Mbidi_category); + + if (bidi == MbidiR || bidi == MbidiAL + || bidi == MbidiRLE || bidi == MbidiRLO) + { + bidi_sensitive = 1; + levels[size] = 1; + } + else + levels[size] = 0; + idx[size] = GLYPH_INDEX (g); + logical[size++] = g++->c; + while (g->type != GLYPH_ANCHOR + && (g[-1].pos == g->pos || g->combining_code)) + g++; + } + + if (! bidi_sensitive) + return; + + glyphs = alloca (sizeof (MGlyph) * gstring->used); + memcpy (glyphs, gstring->glyphs, (sizeof (MGlyph) * gstring->used)); +#ifdef HAVE_FRIBIDI + visual = alloca (sizeof (FriBidiChar) * size); + indices = alloca (sizeof (FriBidiStrIndex) * size); + + fribidi_log2vis (logical, size, &base, visual, NULL, indices, levels); +#else /* not HAVE_FRIBIDI */ + indices = alloca (sizeof (int) * size); + for (i = 0; i < size; i++) + { + if (levels[i]) + { + int j, k; + + for (j = i + 1; j < size && levels[j]; j++); + for (k = j--; i < k; i++, j--) + indices[i] = j; + i--; + } + else + indices[i] = i; + } +#endif /* not HAVE_FRIBIDI */ + + /* IDX are indices to gstring->glyphs[]. The glyphs for LOGICAL[N] + starts from gstring->glyphs[IDX[N]]. + + INDICES are indices to LOGICAL[]. The glyph for VISUAL[N] is + originally at LOGICAL[INDICES[N]]. */ + + for (i = 0, gidx = 1; i < size; i++) + { + int j = indices[i]; + int k = idx[j]; + int pos = glyphs[k].pos; + + glyphs[k].bidi_level = levels[j]; +#ifdef HAVE_FRIBIDI + if (visual[i] != logical[j]) + { + /* Mirrored. */ + glyphs[k].c = visual[i]; + if (glyphs[k].rface->rfont) + glyphs[k].code = mfont__encode_char (glyphs[k].rface->rfont, + glyphs[k].c); + } +#endif /* not HAVE_FRIBIDI */ + *(MGLYPH (gidx)) = glyphs[k]; + for (gidx++, k++; + (k < gstring->used - 1 + && (glyphs[k].pos == pos || glyphs[k].combining_code)); + gidx++, k++) + { + glyphs[k].bidi_level = levels[j]; + *(MGLYPH (gidx)) = glyphs[k]; + } + } +} + +static void +reorder_combining_chars (MGlyphString *gstring, int from, int to) +{ + MGlyph *g, *gbeg = MGLYPH (from + 1), *gend = MGLYPH (to), temp; + int reordered = 1; + + while (reordered) + { + reordered = 0; + for (g = gbeg; g != gend; g++) + if (COMBINING_CODE_CLASS (g->combining_code) > 0 + && (COMBINING_CODE_CLASS (g[-1].combining_code) + > COMBINING_CODE_CLASS (g->combining_code))) + { + reordered = 1; + temp = *g; + *g = g[-1]; + g[-1] = temp; + } + } +} + + +/** Scan M-text MT from FROM to TO, and compose glyphs in GSTRING for + displaying them on FRAME. + + This function fills members , , , , , + of glyphs. The other members are filled by + layout_glyph_string. */ + +static void +compose_glyph_string (MFrame *frame, MText *mt, int from, int to, + MGlyphString *gstring) +{ + MRealizedFace *default_rface = frame->rface; + int stop, face_change, language_change, charset_change; + MGlyph g_tmp, *g; + int pos; + MSymbol language = Mnil, script = Mnil, charset = Mnil; + MRealizedFace *rface = default_rface; + int non_ascii_found; + int size = gstring->control.fixed_width; + int ignore_formatting_char = gstring->control.ignore_formatting_char; + int i, limit; + + MLIST_RESET (gstring); + gstring->from = from; + + /* At first generate glyphs while using the member as a + flag for rface re-checking. */ + INIT_GLYPH (g_tmp); + + /** Put anchor glyphs at the head and tail. */ + g_tmp.type = GLYPH_ANCHOR; + g_tmp.pos = g_tmp.to = from; + g_tmp.c = 0; + APPEND_GLYPH (gstring, g_tmp); + + stop = face_change = charset_change = language_change = pos = from; + g = gstring->glyphs + gstring->used; + non_ascii_found = 0; + while (1) + { + int c; + MSymbol this_script; + + if (pos < mtext_nchars (mt)) + c = mtext_ref_char (mt, pos); + else + c = '\n'; + g_tmp.category = Mnil; + if (c < 0x100) + { + if (c == ' ' || c == '\n' || c == '\t') + g_tmp.type = GLYPH_SPACE, this_script = Mnil; + else + g_tmp.type = GLYPH_CHAR, this_script = Mlatin; + } + else + { + g_tmp.category = mchar_get_prop (c, Mcategory); + if (ignore_formatting_char && g_tmp.category == McatCf) + g_tmp.type = GLYPH_SPACE, this_script = Mnil; + else + { + g_tmp.type = GLYPH_CHAR; + this_script = (MSymbol) mchar_get_prop (c, Mscript); + if (this_script == Minherited) + this_script = script; + } + } + + if (pos == stop || script != this_script || g->type != g_tmp.type) + { + if (non_ascii_found && g->type == GLYPH_CHAR) + while (g < gstring->glyphs + gstring->used) + g = mface__for_chars (script, language, charset, + g, gstring->glyphs + gstring->used, size); + g = gstring->glyphs + gstring->used; + non_ascii_found = 0; + script = this_script; + if (pos == to) + break; + if (pos < mtext_nchars (mt) && pos == language_change) + { + language = (MSymbol) mtext_get_prop (mt, pos, Mlanguage); + mtext_prop_range (mt, Mlanguage, pos, NULL, &language_change, 0); + } + if (pos < mtext_nchars (mt) && pos == charset_change) + { + charset = (MSymbol) mtext_get_prop (mt, pos, Mcharset); + mtext_prop_range (mt, Mcharset, pos, NULL, &charset_change, 0); + } + if (pos < mtext_nchars (mt) && pos == face_change) + { + MFace *faces[64]; + int num = mtext_get_prop_values (mt, pos, Mface, + (void **) faces, 64); + + mtext_prop_range (mt, Mface, pos, NULL, &face_change, 1); + rface = (num > 0 + ? mface__realize (frame, faces, num, + language, charset, size) + : default_rface); + } + stop = language_change; + if (stop > charset_change) + stop = charset_change; + if (face_change < stop) + stop = face_change; + } + + g_tmp.c = g_tmp.code = c; + g_tmp.pos = pos++; + g_tmp.to = pos; + g_tmp.rface = rface; + + if (c >= 0x100) + non_ascii_found = 1; + else if (g_tmp.type == GLYPH_CHAR && (c <= 32 || c == 127)) + { + g_tmp.c = '^'; + APPEND_GLYPH (gstring, g_tmp); + if (c < ' ') + g_tmp.c += 0x40; + else + g_tmp.c = '?'; + } + APPEND_GLYPH (gstring, g_tmp); + if (c == '\n' + && gstring->control.two_dimensional) + break; + } + + limit = pos - from; + + /* Append an anchor glyph. */ + g_tmp.type = GLYPH_ANCHOR; + g_tmp.c = 0; + g_tmp.code = MCHAR_INVALID_CODE; + g_tmp.pos = g_tmp.to = pos; + g_tmp.rface = NULL; + APPEND_GLYPH (gstring, g_tmp); + + gstring->to = pos; + + /* Next, run FLT if necessary. */ + for (i = 1, g = MGLYPH (i); g->type != GLYPH_ANCHOR;) + { + MGlyph *this = g; + + if (this->type == GLYPH_CHAR && this->rface->rfont) + { + int start = i++; + MGlyph *tmp = gstring->glyphs + i; + + if (this->rface->rfont->layouter != Mnil) + { + while ((tmp->type == GLYPH_CHAR || tmp->type == GLYPH_SPACE) + && tmp->rface->rfont == this->rface->rfont + && tmp->code != MCHAR_INVALID_CODE) + i++, tmp++; + i = mfont__flt_run (gstring, start, i, + this->rface->rfont->layouter, + this->rface->ascii_rface); + } + else + { + while (this->type == GLYPH_CHAR + && this->c >= 0x100 + && this->category + && MSYMBOL_NAME (this->category)[0] == 'M' + && this->rface->rfont + && this->rface->rfont->layouter == Mnil) + { + int class = (int) mchar_get_prop (this->c, + Mcombining_class); + this->combining_code + = MAKE_COMBINING_CODE_BY_CLASS (class); + i++, this++; + } + if (start + 1 < i) + reorder_combining_chars (gstring, start, i); + } + g = MGLYPH (i); + } + else + i++, g++; + } + + /* At last, reorder glyphs visually if necessary. */ + if (gstring->control.enable_bidi) + visual_order (gstring); +} + + +static int +combining_code_from_class (int class) +{ + int code; + + if (class < 200) + code = MAKE_COMBINING_CODE (3, 1, 3, 1, 128, 128); + else if (class == 200) /* below left attached */ + code = MAKE_COMBINING_CODE (2, 0, 0, 1, 128, 128); + else if (class == 202) /* below attached*/ + code = MAKE_COMBINING_CODE (2, 1, 0, 1, 128, 128); + else if (class == 204) /* below right attached */ + code = MAKE_COMBINING_CODE (2, 2, 0, 1, 128, 128); + else if (class == 208) /* left attached */ + code = MAKE_COMBINING_CODE (3, 0, 3, 2, 128, 128); + else if (class == 210) /* right attached */ + code = MAKE_COMBINING_CODE (3, 2, 3, 0, 128, 128); + else if (class == 212) /* above left attached */ + code = MAKE_COMBINING_CODE (0, 0, 2, 1, 128, 128); + else if (class == 214) /* above attached */ + code = MAKE_COMBINING_CODE (0, 1, 2, 1, 128, 128); + else if (class == 216) /* above right attached */ + code = MAKE_COMBINING_CODE (0, 2, 2, 1, 128, 128); + else if (class == 218) /* below left */ + code = MAKE_COMBINING_CODE (2, 0, 0, 1, 122, 128); + else if (class == 220) /* below */ + code = MAKE_COMBINING_CODE (2, 1, 0, 1, 122, 128); + else if (class == 222) /* below right */ + code = MAKE_COMBINING_CODE (2, 2, 0, 1, 122, 128); + else if (class == 224) /* left */ + code = MAKE_COMBINING_CODE (3, 0, 3, 2, 128, 122); + else if (class == 226) /* right */ + code = MAKE_COMBINING_CODE (3, 2, 3, 0, 128, 133); + else if (class == 228) /* above left */ + code = MAKE_COMBINING_CODE (0, 0, 2, 1, 133, 128); + else if (class == 230) /* above */ + code = MAKE_COMBINING_CODE (0, 1, 2, 1, 133, 128); + else if (class == 232) /* above right */ + code = MAKE_COMBINING_CODE (0, 2, 2, 1, 133, 128); + else if (class == 233) /* double below */ + code = MAKE_COMBINING_CODE (2, 2, 0, 2, 122, 128); + else if (class == 234) /* double above */ + code = MAKE_COMBINING_CODE (0, 2, 2, 2, 133, 128); + else if (class == 240) /* iota subscript */ + code = MAKE_COMBINING_CODE (2, 1, 0, 1, 122, 128); + else /* unknown */ + code = MAKE_COMBINING_CODE (3, 1, 3, 1, 128, 128); + return code; +} + + +static void +layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to) +{ + int g_physical_ascent, g_physical_descent; + int g_width, g_lbearing, g_rbearing; + MGlyph *g = MGLYPH (from); + MGlyph *last_g = MGLYPH (to); + + g_physical_ascent = gstring->physical_ascent; + g_physical_descent = gstring->physical_descent; + g_width = g_lbearing = g_rbearing = 0; + + while (g < last_g) + { + MGlyph *base = g++; + MRealizedFont *rfont = base->rface->rfont; + int size = rfont->font.property[MFONT_SIZE]; + int width, lbearing, rbearing; + + mfont__get_metric (rfont, base); + if (g == last_g || ! g->combining_code) + { + /* No combining. */ + if (base->left_padding && base->lbearing < 0) + { + base->xoff = - base->lbearing; + base->width += base->xoff; + base->rbearing += base->xoff; + base->lbearing = 0; + } + if (base->right_padding && base->rbearing > base->width) + { + base->width = base->rbearing; + } + lbearing = (base->lbearing < 0 ? base->lbearing : 0); + rbearing = base->rbearing; + } + else + { + /* With combining glyphs. */ + int left = -base->width; + int right = 0; + int top = - base->ascent; + int bottom = base->descent; + int height = bottom - top; + int begin = base->pos; + int end = base->to; + int i; + + width = base->width; + lbearing = (base->lbearing < 0 ? base->lbearing : 0); + rbearing = base->rbearing; + + while (g != last_g && g->combining_code) + { + int combining_code, base_x, base_y, add_x, add_y, off_x, off_y; + + combining_code = g->combining_code; + if (COMBINING_BY_CLASS_P (combining_code)) + g->combining_code = combining_code + = combining_code_from_class (COMBINING_CODE_CLASS + (combining_code)); + + rfont = g->rface->rfont; + size = rfont->font.property[MFONT_SIZE]; + off_x = (size * (COMBINING_CODE_OFF_X (combining_code) - 128) + / 1000); + off_y = (size * (COMBINING_CODE_OFF_Y (combining_code) - 128) + / 1000); + base_x = COMBINING_CODE_BASE_X (combining_code); + base_y = COMBINING_CODE_BASE_Y (combining_code); + add_x = COMBINING_CODE_ADD_X (combining_code); + add_y = COMBINING_CODE_ADD_Y (combining_code); + + if (begin > g->pos) + begin = g->pos; + else if (end < g->to) + end = g->to; + + mfont__get_metric (rfont, g); + g->xoff = left + (width * base_x - g->width * add_x) / 2 + off_x; + if (g->xoff < left) + left = g->xoff; + if (g->xoff + g->width > right) + right = g->xoff + g->width; + width = right - left; + if (g->xoff + g->lbearing < left + lbearing) + lbearing = g->xoff + g->lbearing - left; + if (g->xoff + g->rbearing > left + rbearing) + rbearing = g->xoff + g->rbearing - left; + + if (base_y < 3) + g->yoff = top + height * base_y / 2; + else + g->yoff = 0; + if (add_y < 3) + g->yoff -= (g->ascent + g->descent) * add_y / 2 - g->ascent; + g->yoff -= off_y; + if (g->yoff - g->ascent < top) + top = g->yoff - g->ascent; + if (g->yoff + g->descent > bottom) + bottom = g->yoff + g->descent; + height = bottom - top; + + g->width = 0; + g++; + } + + base->ascent = - top; + base->descent = bottom; + if (left < - base->width) + { + base->xoff = - base->width - left; + base->width += base->xoff; + base->rbearing += base->xoff; + base->lbearing += base->xoff; + } + if (right > 0) + { + base->width += right; + base->rbearing += right; + base->right_padding = 1; + for (i = 1; base + i != g; i++) + base[i].xoff -= right; + } + + for (i = 0; base + i != g; i++) + { + base[i].pos = begin; + base[i].to = end; + } + } + + g_physical_ascent = MAX (g_physical_ascent, base->ascent); + g_physical_descent = MAX (g_physical_descent, base->descent); + g_lbearing = MIN (g_lbearing, g_width + lbearing); + g_rbearing = MAX (g_rbearing, g_width + rbearing); + g_width += base->width; + } + + gstring->physical_ascent = g_physical_ascent; + gstring->physical_descent = g_physical_descent; + gstring->sub_width = g_width; + gstring->sub_lbearing = g_lbearing; + gstring->sub_rbearing = g_rbearing; +} + + +/** Decide the layout of glyphs in GSTRING. Space glyphs are handled + by this function directly. Character glyphs are handled by + layouter functions registered in font drivers. + + This function fill-in all the remaining members of glyphs. */ + +static void +layout_glyph_string (MFrame *frame, MGlyphString *gstring) +{ + /* Default width of TAB. */ + int tab_width = frame->space_width * (gstring->control.tab_width + ? gstring->control.tab_width : 8); + int tab_found = 0; + MGlyph *g; + MGlyph pad; + MDrawControl *control = &(gstring->control); + int width; + MFaceBoxProp *box; + int box_line_height = 0; + + gstring->ascent = gstring->descent = 0; + gstring->physical_ascent = gstring->physical_descent = 0; + gstring->width = gstring->lbearing = gstring->rbearing = 0; + + g = MGLYPH (1); + box = NULL; + while (g->type != GLYPH_ANCHOR) + { + if (box != g->rface->box) + { + int gidx = GLYPH_INDEX (g); + + if (box) + { + /* Insert the right side of the box. That glyph belongs + to the previous grapheme cluster. */ + MGlyph box_glyph = g[-1]; + + box_glyph.type = GLYPH_BOX; + box_glyph.width + = (control->fixed_width + ? frame->space_width + : box->inner_hmargin + box->width + box->outer_hmargin); + box_glyph.lbearing = 0; + box_glyph.rbearing = box_glyph.width; + box_glyph.xoff = 0; + box_glyph.right_padding = 1; + gstring->width += box_glyph.width; + gstring->rbearing += box_glyph.width; + INSERT_GLYPH (gstring, gidx, box_glyph); + gidx++; + g = MGLYPH (gidx); + } + box = g->rface->box; + if (box) + { + /* Insert the left side of the box. That glyph belongs + to the following grapheme cluster. */ + MGlyph box_glyph = *g; + int box_height = (box->width + + box->inner_vmargin + box->outer_vmargin); + + if (box_line_height < box_height) + box_line_height = box_height; + box_glyph.type = GLYPH_BOX; + box_glyph.width + = (control->fixed_width + ? frame->space_width + : box->inner_hmargin + box->width + box->outer_hmargin); + box_glyph.lbearing = 0; + box_glyph.rbearing = box_glyph.width; + box_glyph.xoff = 0; + box_glyph.left_padding = 1; + gstring->width += box_glyph.width; + gstring->rbearing += box_glyph.width; + INSERT_GLYPH (gstring, gidx, box_glyph); + gidx++; + g = MGLYPH (gidx); + } + } + + if (g->type == GLYPH_CHAR) + { + MRealizedFace *rface = g->rface; + MRealizedFont *rfont = rface->rfont; + MGlyph *fromg = g; + int from = GLYPH_INDEX (g); + + for (g++; g->type == GLYPH_CHAR; g++) + if (! rfont != ! g->rface->rfont + || box != g->rface->box + || ((fromg->code == MCHAR_INVALID_CODE) + != (g->code == MCHAR_INVALID_CODE))) + break; + if (rfont && fromg->code != MCHAR_INVALID_CODE) + { + int extra_width; + int to = GLYPH_INDEX (g); + + layout_glyphs (frame, gstring, from, to); + extra_width = - gstring->sub_lbearing; + if (extra_width > 0 + && (GLYPH_INDEX (g) > 1 + || control->align_head)) + { + g = MGLYPH (from); + pad = *g; + pad.type = GLYPH_PAD; + pad.xoff = 0; + pad.lbearing = 0; + pad.width = pad.rbearing = extra_width; + INSERT_GLYPH (gstring, from, pad); + to++; + gstring->sub_lbearing = 0; + gstring->sub_width += extra_width; + gstring->sub_rbearing += extra_width; + + g = MGLYPH (from - 1); + if (g->type == GLYPH_SPACE) + { + /* The pad just inserted is absorbed (maybe + partially) by the previous space while + keeping at least some space width. For the + moment, we use the arbitrary width 2-pixel. + Perhaps, it should be decided by the current + face, or a default value of the current + frame, which is, however, not yet + implemented. */ + if (extra_width + 2 < g->width) + { + g->width -= extra_width; + } + else + { + extra_width -= g->width - 2; + g->width = 2; + } + gstring->width -= extra_width; + gstring->rbearing -= extra_width; + } + } + + extra_width = gstring->sub_rbearing - gstring->sub_width; + if (extra_width > 0) + { + g = MGLYPH (to); + if (g->type == GLYPH_SPACE && box == g->rface->box) + { + g--; + pad = *g; + pad.type = GLYPH_PAD; + pad.xoff = 0; + pad.lbearing = 0; + pad.width = pad.rbearing = extra_width; + INSERT_GLYPH (gstring, to, pad); + to++; + } + else + g[-1].width += extra_width; + gstring->sub_width += extra_width; + } + + if (gstring->lbearing > gstring->width + gstring->sub_lbearing) + gstring->lbearing = gstring->width + gstring->sub_lbearing; + if (gstring->rbearing < gstring->width + gstring->sub_rbearing) + gstring->rbearing = gstring->width + gstring->sub_rbearing; + gstring->width += gstring->sub_width; + if (gstring->ascent < rface->ascent) + gstring->ascent = rface->ascent; + if (gstring->descent < rface->descent) + gstring->descent = rface->descent; + g = MGLYPH (to); + } + else + { + for (; fromg < g; fromg++) + { + if ((fromg->c >= 0x200B && fromg->c <= 0x200F) + || (fromg->c >= 0x202A && fromg->c <= 0x202E)) + fromg->width = fromg->rbearing = 1; + else + fromg->width = fromg->rbearing = rface->space_width; + fromg->xoff = fromg->lbearing = 0; + fromg->ascent = fromg->descent = 0; + gstring->width += fromg->width; + gstring->rbearing += fromg->width; + } + if (gstring->ascent < frame->rface->ascent) + gstring->ascent = frame->rface->ascent; + if (gstring->descent < frame->descent) + gstring->descent = frame->rface->descent; + } + } + else if (g->type == GLYPH_SPACE) + { + if (g->c == ' ') + g->width = g->rface->space_width; + else if (g->c == '\n') + { + g->width = control->cursor_width; + if (g->width) + { + if (control->cursor_bidi) + g->width = 3; + else if (g->width < 0) + g->width = g->rface->space_width; + } + } + else if (g->c == '\t') + { + g->width = tab_width - ((gstring->indent + gstring->width) + % tab_width); + tab_found = 1; + } + else + g->width = 1; + if (g[-1].type == GLYPH_PAD) + { + /* This space glyph absorbs (maybe partially) the + previous padding glyph. */ + g->width -= g[-1].width; + if (g->width < 1) + /* But, keep at least some space width. For the + moment, we use the arbitrary width 2-pixel. */ + g->width = 2; + } + g->rbearing = g->width; + gstring->width += g->width; + gstring->rbearing += g->width; + if (g->rface->rfont) + { + if (gstring->ascent < g->rface->ascent) + gstring->ascent = g->rface->ascent; + if (gstring->descent < g->rface->descent) + gstring->descent = g->rface->descent; + } + g++; + } + else + { + gstring->width += g->width; + gstring->rbearing += g->width; + g++; + } + } + + if (box) + { + /* Insert the right side of the box. */ + int gidx = GLYPH_INDEX (g); + MGlyph box_glyph = g[-1]; + + box_glyph.type = GLYPH_BOX; + box_glyph.width + = (control->fixed_width + ? frame->space_width + : box->inner_hmargin + box->width + box->outer_hmargin); + box_glyph.lbearing = 0; + box_glyph.rbearing = box_glyph.width; + box_glyph.xoff = 0; + box_glyph.right_padding = 1; + gstring->width += box_glyph.width; + gstring->rbearing += box_glyph.width; + INSERT_GLYPH (gstring, gidx, box_glyph); + } + + gstring->text_ascent = gstring->ascent; + gstring->text_descent = gstring->descent; + if (gstring->text_ascent < gstring->physical_ascent) + gstring->text_ascent = gstring->physical_ascent; + if (gstring->text_descent < gstring->physical_descent) + gstring->text_descent = gstring->physical_descent; + gstring->line_ascent = gstring->text_ascent; + gstring->line_descent = gstring->text_descent; + if (box_line_height > 0) + { + gstring->line_ascent += box_line_height; + gstring->physical_ascent = gstring->line_ascent; + gstring->line_descent += box_line_height; + gstring->physical_descent = gstring->line_descent; + } + + if (gstring->line_ascent < control->min_line_ascent) + gstring->line_ascent = control->min_line_ascent; + else if (control->max_line_ascent + && control->max_line_ascent > control->min_line_ascent + && gstring->line_ascent > control->max_line_ascent) + gstring->line_ascent = control->max_line_ascent; + + if (gstring->line_descent < control->min_line_descent) + gstring->line_descent = control->min_line_descent; + else if (control->max_line_descent + && control->max_line_descent > control->min_line_descent + && gstring->line_descent > control->max_line_descent) + gstring->line_descent = control->max_line_descent; + gstring->height = gstring->line_ascent + gstring->line_descent; + + if (control->orientation_reversed + && tab_found) + { + /* We must adjust TAB width for RTL orientation. */ + width = gstring->indent; + + for (g = MGLYPH (gstring->used - 2); g->type != GLYPH_ANCHOR; g--) + { + if (g->type == GLYPH_CHAR && g->c == '\t') + { + int this_width = tab_width - (width % tab_width); + + if (g[1].type == GLYPH_PAD) + this_width -= g[1].width; + if (g[-1].type == GLYPH_PAD) + this_width -= g[-1].width; + if (this_width < 2) + this_width = 2; + gstring->width += this_width - g->width; + gstring->rbearing += this_width - g->width; + g->width = this_width; + width += this_width; + } + else + width += g->width; + } + } +} + + +static MDrawRegion +draw_background (MFrame *frame, MDrawWindow win, int x, int y, + MGlyphString *gstring, int from, int to, + int *from_idx, int *to_idx, int *to_x) +{ + MGlyph *g = MGLYPH (1); + MDrawRegion region = (MDrawRegion) NULL; + MDrawControl *control = &gstring->control; + int cursor_pos = -1; + int prev_pos = -1; + int cursor_bidi = control->cursor_bidi; + + if (control->with_cursor && control->cursor_width) + { + if (gstring->from <= control->cursor_pos + && gstring->to > control->cursor_pos) + cursor_pos = control->cursor_pos; + if (cursor_bidi + && gstring->from <= control->cursor_pos - 1 + && gstring->to > control->cursor_pos - 1) + prev_pos = control->cursor_pos - 1; + } + + *from_idx = *to_idx = 0; + while (g->type != GLYPH_ANCHOR) + { + if (g->pos >= from && g->pos < to) + { + MGlyph *fromg = g, *cursor = NULL; + MRealizedFace *rface = g->rface; + int width = 0; + int cursor_width = 0; + int cursor_x; + + if (! *from_idx) + *from_idx = GLYPH_INDEX (g); + while (g->pos >= from && g->pos < to + && g->rface == rface) + { + g->enabled = 1; + if (g->type != GLYPH_BOX + && g->pos <= cursor_pos && g->to > cursor_pos) + { + if (! cursor) + cursor = g, cursor_x = x + width; + cursor_width += g->width; + } + width += g++->width; + } + if (width > 0 + && (control->as_image + || rface->face.property[MFACE_BACKGROUND] != Mnil + || rface->face.property[MFACE_VIDEOMODE] == Mreverse)) + { + int this_x = x, this_width = width; + + if (fromg->type == GLYPH_BOX) + this_x += fromg->width, this_width -= fromg->width; + if (g[-1].type == GLYPH_BOX) + this_width -= g[-1].width; + mwin__fill_space (frame, win, rface, 0, + this_x, y - gstring->text_ascent, this_width, + gstring->text_ascent + gstring->text_descent, + control->clip_region); + } + if (cursor) + { + MDrawMetric rect; + + rect.x = cursor_x; + rect.y = y - gstring->text_ascent; + rect.height = gstring->text_ascent + gstring->text_descent; + if (! cursor_bidi) + { + rect.width = ((control->cursor_width > 0 + && control->cursor_width < cursor_width) + ? control->cursor_width : cursor_width); + } + else + { + if (cursor->bidi_level % 2) + rect.x += cursor_width - 1; + rect.width = 1; + } + mwin__fill_space (frame, win, rface, 1, + rect.x, rect.y, rect.width, rect.height, + control->clip_region); + if (! region) + region = mwin__region_from_rect (&rect); + else + mwin__region_add_rect (region, &rect); + mwin__verify_region (frame, region); + if (cursor_bidi) + { + if (cursor->bidi_level % 2) + rect.x -= 3; + rect.height = 2; + rect.width = cursor_width < 4 ? cursor_width : 4; + mwin__fill_space (frame, win, rface, 1, + rect.x, rect.y, rect.width, rect.height, + control->clip_region); + mwin__region_add_rect (region, &rect); + mwin__verify_region (frame, region); + } + } + + if (prev_pos >= 0) + { + int temp_width = 0; + + cursor_width = 0; + cursor = NULL; + while (fromg < g) + { + if (fromg->type != GLYPH_BOX + && fromg->pos <= prev_pos && fromg->to > prev_pos) + { + if (! cursor) + cursor = fromg, cursor_x = x + temp_width; + cursor_width += fromg->width; + } + temp_width += fromg++->width; + } + if (cursor) + { + MDrawMetric rect; + + rect.x = cursor_x; + if (! (cursor->bidi_level % 2)) + rect.x += cursor_width - 1; + rect.y = y - gstring->text_ascent; + rect.height = gstring->text_ascent + gstring->text_descent; + rect.width = 1; + mwin__fill_space (frame, win, rface, 1, + rect.x, rect.y, rect.width, rect.height, + control->clip_region); + if (! region) + region = mwin__region_from_rect (&rect); + else + mwin__region_add_rect (region, &rect); + mwin__verify_region (frame, region); + rect.y += rect.height - 2; + rect.height = 2; + rect.width = cursor_width < 4 ? cursor_width : 4; + if (! (cursor->bidi_level % 2)) + rect.x -= rect.width - 1; + mwin__fill_space (frame, win, rface, 1, + rect.x, rect.y, rect.width, rect.height, + control->clip_region); + mwin__region_add_rect (region, &rect); + mwin__verify_region (frame, region); + } + } + x += width; + *to_idx = GLYPH_INDEX (g); + *to_x = x; + } + else + g++->enabled = 0; + } + return region; +} + +static void +render_glyphs (MFrame *frame, MDrawWindow win, int x, int y, int width, + MGlyphString *gstring, int from_idx, int to_idx, + int reverse, MDrawRegion region) +{ + MGlyph *g = MGLYPH (from_idx), *gend = MGLYPH (to_idx); + + if (region) + { + MDrawMetric rect; + + mwin__region_to_rect (region, &rect); + if (rect.x > x) + { + while (g != gend && x + g->rbearing <= rect.x) + { + x += g->width; + width -= g++->width; + while (! g->enabled && g != gend) + g++; + } + } + rect.x += rect.width; + if (rect.x < x + width) + { + while (g != gend && x + width - gend[-1].width >= rect.x) + { + width -= (--gend)->width; + while (! gend->enabled && g != gend) + gend--; + } + if (g != gend) + while (gend[-1].to == gend->to) gend++; + } + } + + while (g != gend) + { + if (g->enabled) + { + MRealizedFace *rface = g->rface; + int width = g->width; + MGlyph *from_g = g++; + + /* Handle the glyphs of the same type/face at once. */ + while (g != gend + && g->type == from_g->type + && g->rface == rface + && (g->code < 0) == (from_g->code < 0) + && g->enabled) + width += g++->width; + + if (from_g->type == GLYPH_CHAR) + { + MFontDriver *driver; + + if (rface->rfont && from_g->code >= 0) + driver = rface->rfont->driver; + else + driver = mfont__driver_list[MFONT_TYPE_WIN]; + (driver->render) (win, x, y, gstring, from_g, g, + reverse, region); + } + else if (from_g->type == GLYPH_BOX) + { + /* Draw the left or right side of a box. If + from_g->lbearing is nonzero, this is the left side, + else this is the right side. */ + mwin__draw_box (frame, win, gstring, from_g, x, y, 0, region); + } + + if (from_g->type != GLYPH_BOX) + { + if (rface->hline) + mwin__draw_hline (frame, win, gstring, rface, reverse, + x, y, width, region); + if (rface->box + && ! reverse) + /* Draw the top and bottom side of a box. */ + mwin__draw_box (frame, win, gstring, from_g, + x, y, width, region); + } + x += width; + } + else + g++; + } +} + + +static int +find_overlapping_glyphs (MGlyphString *gstring, int *left, int *right, + int *from_x, int *to_x) +{ + MGlyph *g; + int left_idx = *left, right_idx = *right; + int left_x, right_x, x; + + for (g = MGLYPH (*left) - 1, x = 0; g->type != GLYPH_ANCHOR; g--) + { + x -= g->width; + if (x + g->rbearing > 0) + { + while (g[-1].pos == g->pos && g[-1].type != GLYPH_ANCHOR) + x -= (--g)->width; + left_idx = GLYPH_INDEX (g); + left_x = x; + } + } + + for (g = MGLYPH (*right), x = 0; g->type != GLYPH_ANCHOR; g++) + { + x += g->width; + if (x - g->width + g->lbearing < 0) + { + while (g->pos == g[1].pos && g[1].type != GLYPH_ANCHOR) + x += (++g)->width; + right_idx = GLYPH_INDEX (g) + 1; + right_x = x; + } + } + + if (*left == left_idx && *right == right_idx) + return 0; + + if (*left != left_idx) + { + for (g = MGLYPH (*left) - 1; GLYPH_INDEX (g) >= left_idx; g--) + g->enabled = 1; + *left = left_idx; + *from_x += left_x; + } + if (*right != right_idx) + { + for (g = MGLYPH (*right); GLYPH_INDEX (g) < right_idx; g++) + g->enabled = 1; + *right = right_idx; + *to_x += right_x; + } + return 1; +} + + +static int +gstring_width (MGlyphString *gstring, int from, int to, int *rbearing) +{ + MGlyph *g; + int width; + + if (from <= gstring->from && to >= gstring->to) + { + if (rbearing) + *rbearing = gstring->rbearing; + return gstring->width; + } + + if (rbearing) + *rbearing = 0; + for (g = MGLYPH (1), width = 0; g->type != GLYPH_ANCHOR; g++) + if (g->pos >= from && g->pos < to) + { + if (rbearing && width + g->rbearing > *rbearing) + *rbearing = width + g->rbearing; + width += g->width; + } + return width; +} + + +static void +render_glyph_string (MFrame *frame, MDrawWindow win, int x, int y, + MGlyphString *gstring, int from, int to) +{ + MDrawControl *control = &gstring->control; + MDrawMetric rect; + MDrawRegion clip_region, cursor_region; + int from_idx, to_idx; + int to_x; + + if (control->orientation_reversed) + x -= gstring->indent + gstring_width (gstring, from, to, NULL); + else + x += gstring->indent; + + /* At first, draw all glyphs without cursor. */ + cursor_region = draw_background (frame, win, x, y, gstring, from, to, + &from_idx, &to_idx, &to_x); + + if (control->partial_update) + { + rect.x = x; + rect.width = to_x - x; + if (find_overlapping_glyphs (gstring, &from_idx, &to_idx, &x, &to_x)) + { + rect.y = y - gstring->line_ascent; + rect.height = gstring->height; + clip_region = mwin__region_from_rect (&rect); + if (control->clip_region) + mwin__intersect_region (clip_region, control->clip_region); + } + else + clip_region = control->clip_region; + } + else + clip_region = control->clip_region; + + render_glyphs (frame, win, x, y, to_x - x, gstring, from_idx, to_idx, + 0, clip_region); + if (cursor_region) + { + if (clip_region) + mwin__intersect_region (cursor_region, clip_region); + render_glyphs (frame, win, x, y, to_x - x, gstring, from_idx, to_idx, + 1, cursor_region); + } + if (clip_region != control->clip_region) + mwin__free_region (clip_region); + if (cursor_region) + mwin__free_region (cursor_region); + return; +} + +static int gstring_num; + +static void +free_gstring (void *object) +{ + MGlyphString *gstring = (MGlyphString *) object; + + if (gstring->next) + free_gstring (gstring->next); + if (gstring->size > 0) + free (gstring->glyphs); + free (gstring); + gstring_num--; +} + + +static MGlyphString scratch_gstring; + +static MGlyphString * +alloc_gstring (MText *mt, int pos, MDrawControl *control, int line, int y) +{ + MGlyphString *gstring; + + if (pos == mt->nchars) + { + gstring = &scratch_gstring; + } + else + { + M17N_OBJECT (gstring, free_gstring, MERROR_DRAW); + MLIST_INIT1 (gstring, glyphs, 128); + gstring_num++; + } + + gstring->top = gstring; + gstring->mt = mt; + gstring->control = *control; + gstring->indent = gstring->width_limit = 0; + if (control->format) + (*control->format) (line, y, &(gstring->indent), &(gstring->width_limit)); + else + gstring->width_limit = control->max_line_width; + return gstring; +} + +/* Truncate the line width of GSTRING to GSTRING->width_limit. */ + +static void +truncate_gstring (MFrame *frame, MText *mt, MGlyphString *gstring) +{ + int width; + int i; + int *pos_width; + MGlyph *g; + int pos; + + /* Setup the array POS_WIDTH so that POS_WIDTH[I - GSTRING->from] is + a width of glyphs for the character at I of GSTRING->mt. If I is + not a beginning of a grapheme cluster, the corresponding element + is 0. */ + MTABLE_ALLOCA (pos_width, gstring->to - gstring->from, MERROR_DRAW); + memset (pos_width, 0, sizeof (int) * (gstring->to - gstring->from)); + for (g = MGLYPH (1); g->type != GLYPH_ANCHOR; g++) + pos_width[g->pos - gstring->from] += g->width; + for (i = 0, width = 0; i < gstring->to - gstring->from; i++) + { + if (pos_width[i] > 0) + { + if (width + pos_width[i] > gstring->width_limit) + break; + } + width += pos_width[i]; + } + + pos = gstring->from + i; + if (gstring->control.line_break) + { + pos = (*gstring->control.line_break) (gstring->mt, gstring->from + i, + gstring->from, gstring->to, 0, 0); + if (pos <= gstring->from || pos >= gstring->to) + return; + } + compose_glyph_string (frame, mt, gstring->from, pos, gstring); + layout_glyph_string (frame, gstring); +} + + +/* Return a gstring that covers a character at POS. */ + +static MGlyphString * +get_gstring (MFrame *frame, MText *mt, int pos, int to, MDrawControl *control) +{ + MGlyphString *gstring = NULL; + + if (pos < mtext_nchars (mt)) + { + MTextProperty *prop = mtext_get_property (mt, pos, M_glyph_string); + + if (prop + && ((prop->start != 0 + && mtext_ref_char (mt, prop->start - 1) != '\n') + || (prop->end < mtext_nchars (mt) + && mtext_ref_char (mt, prop->end - 1) != '\n'))) + { + mtext_detach_property (prop); + prop = NULL; + } + if (prop) + { + gstring = prop->val; + if (memcmp (control, &gstring->control, + (char *) (&control->with_cursor) + - (char *) (control))) + { + mtext_detach_property (prop); + gstring = NULL; + } + } + } + else if (! control->cursor_width) + return NULL; + + if (gstring) + { + MGlyphString *gst; + int offset; + + offset = mtext_character (mt, pos, 0, '\n'); + if (offset < 0) + offset = 0; + else + offset++; + offset -= gstring->from; + if (offset) + for (gst = gstring; gst; gst = gst->next) + { + int i; + + gst->from += offset; + gst->to += offset; + for (i = 0; i < gst->used; i++) + { + gst->glyphs[i].pos += offset; + gst->glyphs[i].to += offset; + } + } + M17N_OBJECT_REF (gstring); + } + else + { + int beg, end; + int line = 0, y = 0; + + if (control->two_dimensional) + { + beg = mtext_character (mt, pos, 0, '\n'); + if (beg < 0) + beg = 0; + else + beg++; + end = mtext_nchars (mt) + (control->cursor_width != 0); + } + else + { + beg = pos; + end = to; + } + gstring = alloc_gstring (mt, beg, control, line, y); + compose_glyph_string (frame, mt, beg, end, gstring); + layout_glyph_string (frame, gstring); + end = gstring->to; + if (control->two_dimensional + && gstring->width_limit + && gstring->width > gstring->width_limit) + { + MGlyphString *gst = gstring; + + truncate_gstring (frame, mt, gst); + while (gst->to < end) + { + line++, y += gst->height; + gst->next = alloc_gstring (mt, gst->from, control, line, y); + gst->next->top = gstring; + compose_glyph_string (frame, mt, gst->to, end, gst->next); + gst = gst->next; + layout_glyph_string (frame, gst); + if (gst->width <= gst->width_limit) + break; + truncate_gstring (frame, mt, gst); + } + } + + if (! control->disable_caching && pos < mtext_nchars (mt)) + { + MTextProperty *prop = mtext_property (M_glyph_string, gstring, + MTEXTPROP_VOLATILE_STRONG); + + if (end > mtext_nchars (mt)) + end = mtext_nchars (mt); + mtext_attach_property (mt, beg, end, prop); + M17N_OBJECT_UNREF (prop); + } + } + + while (gstring->to <= pos) + { + if (! gstring->next) + mdebug_hook (); + gstring = gstring->next; + } + gstring->control = *control; + + return gstring; +} + + +static MDrawControl control_noop; + +#define ASSURE_CONTROL(control) \ + if (! control) \ + control = &control_noop; \ + else + + +static int +draw_text (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to, + MDrawControl *control) +{ + MGlyphString *gstring; + + M_CHECK_POS_X (mt, from, -1); + ASSURE_CONTROL (control); + if (to > mtext_nchars (mt) + (control->cursor_width != 0)) + to = mtext_nchars (mt) + (control->cursor_width != 0); + else if (to < from) + to = from; + + gstring = get_gstring (frame, mt, from, to, control); + if (! gstring) + MERROR (MERROR_DRAW, -1); + render_glyph_string (frame, win, x, y, gstring, from, to); + from = gstring->to; + while (from < to) + { + y += gstring->line_descent; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, from, to, control); + y += gstring->line_ascent; + render_glyph_string (frame, win, x, y, gstring, from, to); + from = gstring->to; + } + M17N_OBJECT_UNREF (gstring->top); + + return 0; +} + + +static MGlyph * +find_glyph_in_gstring (MGlyphString *gstring, int pos, int forwardp) +{ + MGlyph *g; + + if (forwardp) + { + for (g = MGLYPH (1); g->type != GLYPH_ANCHOR; g++) + if (g->pos <= pos && g->to > pos) + break; + } + else + { + for (g = MGLYPH (gstring->used - 2); g->type != GLYPH_ANCHOR; g--) + if (g->pos <= pos && g->to > pos) + break; + } + return g; +} + + +/* for debugging... */ +char work[16]; + +char * +dump_combining_code (int code) +{ + char *vallign = "tcbB"; + char *hallign = "lcr"; + char *p; + int off_x, off_y; + + if (! code) + return "none"; + if (COMBINING_BY_CLASS_P (code)) + code = combining_code_from_class (COMBINING_CODE_CLASS (code)); + work[0] = vallign[COMBINING_CODE_BASE_Y (code)]; + work[1] = hallign[COMBINING_CODE_BASE_X (code)]; + off_y = COMBINING_CODE_OFF_Y (code) - 128; + off_x = COMBINING_CODE_OFF_X (code) - 128; + if (off_y > 0) + sprintf (work + 2, "+%d", off_y); + else if (off_y < 0) + sprintf (work + 2, "%d", off_y); + else if (off_x == 0) + sprintf (work + 2, "-"); + p = work + strlen (work); + if (off_x > 0) + sprintf (p, ">%d", off_x); + else if (off_x < 0) + sprintf (p, "<%d", -off_x); + p += strlen (p); + p[0] = vallign[COMBINING_CODE_ADD_Y (code)]; + p[1] = hallign[COMBINING_CODE_ADD_X (code)]; + p[2] = '\0'; + return work; +} + +void +dump_gstring (MGlyphString *gstring, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MGlyph *g, *last_g = gstring->glyphs + gstring->used; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "(glyph-string"); + + for (g = MGLYPH (0); g < last_g; g++) + fprintf (stderr, + "\n%s (%02d %s pos:%d-%d c:%04X code:%04X face:%x cmb:%s w:%02d bidi:%d)", + prefix, + g - gstring->glyphs, + (g->type == GLYPH_SPACE ? "SPC": g->type == GLYPH_PAD ? "PAD" + : g->type == GLYPH_ANCHOR ? "ANC" + : g->type == GLYPH_BOX ? "BOX" : "CHR"), + g->pos, g->to, g->c, g->code, (unsigned) g->rface, + dump_combining_code (g->combining_code), + g->width, g->bidi_level); + fprintf (stderr, ")"); +} + + +/* m17n-X internal APIs */ + +int +mdraw__init () +{ + M_glyph_string = msymbol_as_managing_key (" glyph-string"); + + memset (&scratch_gstring, 0, sizeof (scratch_gstring)); + MLIST_INIT1 (&scratch_gstring, glyphs, 3); + + Mlatin = msymbol ("latin"); + Minherited = msymbol ("inherited"); + + McatCc = msymbol ("Cc"); + McatCf = msymbol ("Cf"); + + MbidiR = msymbol ("R"); + MbidiAL = msymbol ("AL"); + MbidiRLE = msymbol ("RLE"); + MbidiRLO = msymbol ("RLO"); + MbidiBN = msymbol ("BN"); + MbidiS = msymbol ("S"); +#ifdef HAVE_FRIBIDI + fribidi_set_mirroring (TRUE); +#endif + + return 0; +} + +void +mdraw__fini () +{ + MLIST_FREE1 (&scratch_gstring, glyphs); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ +/*** @addtogroup m17nDraw */ +/*** @{ */ + +/*=*/ +/***en + @brief Draw an M-text on a window. + + The mdraw_text () function draws the text between $FROM and $TO of + M-text $MT on window $WIN of frame $FRAME at coordinate ($X, $Y). + + The appearance of the text (size, style, color, etc) is specified + by the value of the text property whose key is @c Mface. If the + M-text or a part of the M-text does not have such a text property, + the default face of $FRAME is used. + + The font used to draw a character in the M-text is selected from + the value of the fontset property of a face by the following + algorithm: + +
    + +
  1. Search the text properties given to the character for the one + whose key is @c Mcharset; its value should be either a symbol + specifying a charset or Mnil. If the value is Mnil, proceed + to the next step. + + Otherwise, search the mapping table of the fontset for the + charset. If no entry is found proceed to the next step. If + an entry is found, use one of the fonts in the entry that has + a glyph for the character and that matches best with the face + properties. If no such font exists, proceed to the next + step. + +
  2. Get the character-property script of the character. If it is + inherited, get the script property from the previous + characters. If there is no previous character, or none of + them has the script property other than inherited, proceed to + the next step. + + Search the text properties given to the character for the one + whose key is @c Mlanguage; its value should be either a + symbol specifying a language or @c Mnil. + + Search the mapping table of the fontset for the combination + of the script and language. If no entry is found, proceed to + the next step. If an entry is found, use one of the fonts in + the entry that has a glyph for the character and that matches + best with the face properties. If no such font exists, + proceed to the next step. + +
  3. Search the fall-back table of the fontset for a font that has + a glyph of the character. If such a font is found, use that + font. + +
+ + If no font is found by the algorithm above, this function draws an + empty box for the character. + + This function draws only the glyph foreground. To specify the + background color, use mdraw_image_text () or + mdraw_text_with_control (). + + This function is the counterpart of XDrawString (), + XmbDrawString (), and XwcDrawString () functions + in the X Window System. + + @return + If the operation was successful, mdraw_text () returns 0. If an + error is detected, it returns -1 and assigns an error code to the + external variable @c merror_code. */ + +/***ja + @brief ¥¦¥£¥ó¥É¥¦¤Ë M-text ¤òɽ¼¨¤¹¤ë + + ´Ø¿ô mdraw_text () ¤Ï¡¢¥Õ¥ì¡¼¥à $FRAME ¤Î¥¦¥£¥ó¥É¥¦ $WIN ¤ÎºÂɸ + ($X, $Y) ¤Ë¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î¥Æ¥­¥¹¥È¤ò + ɽ¼¨¤¹¤ë¡£ + + ¥Ý¥¤¥ó¥¿ $RET_DESCENT ¤¬ @c NULL ¤Ç¤Ê¤±¤ì¤Ð¡¢É½¼¨¤·¤¿¥Æ¥­¥¹¥È¤Î¥Ç¥£¥» + ¥ó¥È¤¬¤½¤³¤Ë³ÊǼ¤µ¤ì¤ë¡£ + + ¥Æ¥­¥¹¥È¤Î¸«±É¤¨¡Ê¥Õ¥©¥ó¥È¡¢¥¹¥¿¥¤¥ë¡¢¿§¤Ê¤É¡Ë¤Ï¡¢¥­¡¼¤¬ @c Mface + ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤˤè¤Ã¤Æ·è¤Þ¤ë¡£M-text ¤Î°ìÉô¤¢¤ë¤¤¤Ï + Á´Éô¤Ë¤½¤Î¤è¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬ÉÕ¤¤¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤Ï¡¢$FRAME + ¤Î¥Ç¥Õ¥©¥ë¥È¥Õ¥§¡¼¥¹¤¬ÍѤ¤¤é¤ì¤ë¡£ + + M-text ¤Î³Æʸ»ú¤òɽ¼¨¤¹¤ë¥Õ¥©¥ó¥È¤Ï¡¢$FACE ¤Î fontset ¥×¥í¥Ñ¥Æ¥£¤Î + Ãͤ«¤é°Ê²¼¤Î¥¢¥ë¥´¥ê¥º¥à¤ÇÁª¤Ð¤ì¤ë¡£ + +
    + +
  1. ¤½¤Îʸ»ú¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ @c Mlanguage ¤Ç¤¢ + ¤ë¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£¤³¤ÎÃͤϸÀ¸ì¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil ¤Î¤¤ + ¤º¤ì¤«¤Ç¤¢¤ë¡£¼¡¤Ë¥­¡¼¤¬ @c Mscript ¤Ç¤¢¤ë¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£ + ¤³¤ÎÃͤϥ¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil ¤Î¤É¤Á¤é¤«¤Ç¤¢¤ë¡£ + + ¤É¤Á¤é¤â @c Mnil ¤Ê¤é¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£ + + ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤Î¸À¸ì¤È¥¹¥¯¥ê¥×¥È¤ÎÁȤ߹ç¤ï¤»¤ò»È¤Ã + ¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬ + ¤ß¤Ä¤«¤ê¡¢¤«¤Ä¤½¤Î¥Õ¥©¥ó¥È¤Ç¸½ºß¤Îʸ»ú¤¬É½¼¨¤Ç¤­¤ì¤Ð¡¢¤½¤Î¥Õ¥© + ¥ó¥È¤ò»È¤¦¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£ + +
  2. ¤½¤Îʸ»ú¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ @c Mcharset ¤Ç¤¢¤ë + ¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£¤³¤ÎÃͤÏʸ»ú¥»¥Ã¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil + ¤Î¤É¤Á¤é¤«¤Ç¤¢¤ë¡£@c Mnil ¤Ê¤é¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£ + + ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¸«¤Ä¤«¤Ã¤¿Ê¸»ú¥»¥Ã¥È»È¤Ã¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã + ¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬¤ß¤Ä¤«¤ê¡¢¤«¤Ä¤½¤Î¥Õ¥© + ¥ó¥È¤Ç¸½ºß¤Îʸ»ú¤¬É½¼¨¤Ç¤­¤ì¤Ð¡¢¤½¤Î¥Õ¥©¥ó¥È¤ò»È¤¦¡£¤½¤¦¤Ç¤Ê + ¤±¤ì¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£ + +
  3. ¸½ºß¤Îʸ»ú¼«¿È¤ò»È¤Ã¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö + ¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬¸«¤Ä¤«¤ì¤Ð¤½¤ì¤ò»È¤¦¡£ + +
+ + °Ê¾å¤Î¥¢¥ë¥´¥ê¥º¥à¤Ç¥Õ¥©¥ó¥È¤¬¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï¤½¤Îʸ»ú + ¤È¤·¤Æ¶õ¤Î»Í³Ñ·Á¤òɽ¼¨¤¹¤ë¡£ + + ¤³¤Î´Ø¿ô¤¬ÉÁ²è¤¹¤ë¤Î¤Ï¥°¥ê¥Õ¤ÎÁ°·Ê¿§¤À¤±¤Ç¤¢¤ë¡£ÇØ·Ê¿§¤ò»ØÄꤹ¤ë¤Ë + ¤Ï¡¢´Ø¿ô mdraw_image_text () ¤ò»È¤¦¤³¤È¡£ + + ¤³¤Î´Ø¿ô¤Ï¡¢X ¥¦¥£¥ó¥É¥¦¤Ë¤ª¤±¤ë XDrawString (), + XmbDrawString (), XwcDrawString () ¤ËÁêÅö¤¹¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mdraw_text () ¤Ï 0 ÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì + ¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mdraw_text} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mdraw_image_text () */ + +int +mdraw_text (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to) +{ + MDrawControl control; + + memset (&control, 0, sizeof control); + control.as_image = 0; + return draw_text (frame, win, x, y, mt, from, to, &control); +} + +/*=*/ + + +/***en + @brief Draw an M-text on a window as an image + + The mdraw_image_text () function draws the text between $FROM and + $TO of M-text $MT as image on window $WIN of frame $FRAME at + coordinate ($X, $Y). + + The way to draw a text is the same as in mdraw_text () except that + this function also draws the background with the color specified + by faces. + + This function is the counterpart of XDrawImageString (), + XmbDrawImageString (), and XwcDrawImageString () + functions in the X Window System. + + @return + If the operation was successful, mdraw_image_text () returns 0. + If an error is detected, it returns -1 and assigns an error code + to the external variable @c merror_code. */ + +/***ja + @brief ¥Ç¥£¥¹¥×¥ì¥¤¤ËM-text ¤ò²èÁü¤È¤·¤ÆÉÁ¤¯ + + ´Ø¿ô mdraw_image_text () ¤Ï¡¢¥Õ¥ì¡¼¥à $FRAME ¤Î¥¦¥£¥ó¥É¥¦ $WIN ¤Î + ºÂɸ ($X, $Y) ¤Ë¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î¥Æ¥­¥¹¥È¤ò²è + Áü¤È¤·¤ÆÉÁ¤¯¡£ + + ¥Æ¥­¥¹¥È¤ÎÉÁ²èÊýË¡¤Ï mdraw_text () ¤È¤Û¤ÜƱ¤¸¤Ç¤¢¤ë¤¬¡¢¤³¤Î´Ø¿ô¤Ç + ¤Ï $FACE ¤Ç»ØÄꤵ¤ì¤¿¿§¤ÇÇطʤâÉÁ¤¯ÅÀ¤¬°Û¤Ê¤Ã¤Æ¤¤¤ë¡£ + + ¤³¤Î´Ø¿ô¤Ï¡¢X ¥¦¥£¥ó¥É¥¦¤Ë¤ª¤±¤ë XDrawImageString (), + XmbDrawImageString (), XwcDrawImageString () ¤Ë + ÁêÅö¤¹¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mdraw_image_text () ¤Ï 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð + ¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c m_errro ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ + ¤ë¡£ + + @latexonly \IPAlabel{mdraw_image_text} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mdraw_text () */ + +int +mdraw_image_text (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to) +{ + MDrawControl control; + + memset (&control, 0, sizeof control); + control.as_image = 1; + return draw_text (frame, win, x, y, mt, from, to, &control); +} + +/*=*/ + +/***en + @brief Draw an M-text on a window with fine control. + + The mdraw_text_with_control () function draws the text between + $FROM and $TO of M-text $MT on windows $WIN of frame $FRAME at + coordinate ($X, $Y). + + The way to draw a text is the same as in mdraw_text () except that + this function also follows what specified in the drawing control + object $CONTROL. + + For instance, if of $CONTROL is nonzero, this + function draw an M-text 2-dimensionally, i.e., newlines in M-text + breaks lines and the following characters are drawn in the next + line. See the documentation of the structure @ MDrawControl for + more detail. */ + +int +mdraw_text_with_control (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to, MDrawControl *control) +{ + return draw_text (frame, win, x, y, mt, from, to, control); +} + +/*=*/ + +/***en + @brief Compute text pixel width. + + The mdraw_text_extents () function computes the width of text + between $FROM and $TO of M-text $MT when it is drawn on a window + of frame $FRAME using the mdraw_text_with_control () function with + the drawing control object $CONTROL. + + If $OVERALL_INK_RETURN is not @c NULL, this function also computes + the bounding box of character ink of the M-text, and stores the + results in the members of the structure pointed to by + $OVERALL_INK_RETURN. If the M-text has a face specifying a + surrounding box, the box is included in the bounding box. + + If $OVERALL_LOGICAL_RETURN is not @c NULL, this function also + computes the bounding box that provides mininum spacing to other + graphical features (such as surrounding box) for the M-text, and + stores the results in the members of the structure pointed to by + $OVERALL_LOGICAL_RETURN. + + If $OVERALL_LINE_RETURN is not @c NULL, this function also + computes the bounding box that provides mininum spacing to the + other M-text drawn, and stores the results in the members of the + structure pointed to by $OVERALL_LINE_RETURN. This is a union of + $OVERALL_INK_RETURN and $OVERALL_LOGICAL_RETURN if the members + min_line_ascent, min_line_descent, max_line_ascent, and + max_line_descent of $CONTROL are all zero. + + @return + + This function returns the width of the text to be drawn in the + unit of pixels. If $CONTROL->two_dimensional is nonzero and the + text is drawn in multiple physical lines, it returns the width of + the widest line. If an error occurs, it returns -1 and assigns an + error code to the external variable @c merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¤ÎÉý¤ò·×»»¤¹¤ë + + ´Ø¿ô mdraw_text_extents () ¤Ï¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤ò + ¥Õ¥ì¡¼¥à $FRAME ¤Ëɽ¼¨¤¹¤ëºÝ¤ËɬÍפȤʤëÉý¤òÊÖ¤¹¡£ + + ¥Ý¥¤¥ó¥¿ $OVERALL_RETURN ¤¬ @c NULL °Ê³°¤Î¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¥Æ¥­¥¹¥È + Á´ÂΤÎɽ¼¨ÈϰϾðÊó¤â·×»»¤·¡¢$OVERALL_RETURN ¤¬»Ø¤¹¹½Â¤ÂΤΥá¥ó¥Ð¤Ë + ¤½¤Î·ë²Ì¤òÀßÄꤹ¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ïɽ¼¨¤ËɬÍפȤʤë¥Æ¥­¥¹¥È¤ÎÉý¤ò¥Ô¥¯¥»¥ëñ°Ì¤ÇÊÖ¤¹¡£¥¨¥é¡¼ + ¤¬À¸¤¸¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mdraw_text_extents} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE */ + +int +mdraw_text_extents (MFrame *frame, + MText *mt, int from, int to, MDrawControl *control, + MDrawMetric *overall_ink_return, + MDrawMetric *overall_logical_return, + MDrawMetric *overall_line_return) +{ + MGlyphString *gstring; + int y = 0; + int width, rbearing; + + ASSURE_CONTROL (control); + M_CHECK_POS_X (mt, from, -1); + if (to > mtext_nchars (mt) + (control->cursor_width != 0)) + to = mtext_nchars (mt) + (control->cursor_width != 0); + else if (to < from) + to = from; + + gstring = get_gstring (frame, mt, from, to, control); + if (! gstring) + MERROR (MERROR_DRAW, -1); + width = gstring_width (gstring, from, to, &rbearing); + if (overall_ink_return) + { + overall_ink_return->y = - gstring->physical_ascent; + overall_ink_return->x = gstring->lbearing; + } + if (overall_logical_return) + { + overall_logical_return->y = - gstring->ascent; + overall_logical_return->x = 0; + } + if (overall_line_return) + { + overall_line_return->y = - gstring->line_ascent; + overall_line_return->x = gstring->lbearing; + } + + for (from = gstring->to; from < to; from = gstring->to) + { + int this_width, this_rbearing; + + y += gstring->line_descent; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, from, to, control); + this_width = gstring_width (gstring, from, to, &this_rbearing); + y += gstring->line_ascent; + if (width < this_width) + width = this_width; + if (rbearing < this_rbearing) + rbearing = this_rbearing; + } + if (overall_ink_return) + { + overall_ink_return->width = rbearing; + overall_ink_return->height + = y + gstring->physical_descent - overall_ink_return->y; + } + if (overall_logical_return) + { + overall_logical_return->width = width; + overall_logical_return->height + = y + gstring->descent - overall_logical_return->y; + } + if (overall_line_return) + { + overall_line_return->width = MAX (width, rbearing); + overall_line_return->height + = y + gstring->line_descent - overall_line_return->y; + } + + M17N_OBJECT_UNREF (gstring->top); + return width; +} + +/*=*/ + +/***en + @brief Compute the text dimensions of each character of M-text. + + The mdraw_text_per_char_extents () function computes the drawn + metric of each character between $FROM and $TO of M-text $MT + assuming that they are drawn on a window of frame $FRAME using the + mdraw_text_with_control () function with the drawing control + object $CONTROL. + + $ARRAY_SIZE specifies the size of $INK_ARRAY_RETURN and + $LOGICAL_ARRAY_RETURN. Each successive element of + $INK_ARRAY_RETURN and $LOGICAL_ARRAY_RETURN are set to the drawn + ink and logical metrics of successive characters respectively, + relative to the drawing origin of the M-text. The number of + elements of $INK_ARRAY_RETURN and $LOGICAL_ARRAY_RETURN that have + been set is returned to $NUM_CHARS_RETURN. + + If $ARRAY_SIZE is too small to return all metrics, the function + returns -1 and store the requested size in $NUM_CHARS_RETURN. + Otherwise, it returns zero. + + If pointer $OVERALL_INK_RETURN an $OVERALL_LOGICAL_RETURN are not + @c NULL, this function also computes the metrics of the overall + text and stores the results in the members of the structure + pointed to by $OVERALL_INK_RETURN and $OVERALL_LOGICAL_RETURN. + + If $CONTROL->two_dimensional is nonzero, this function computes + only the metrics of characters in the first line. */ + +int +mdraw_text_per_char_extents (MFrame *frame, + MText *mt, int from, int to, + MDrawControl *control, + MDrawMetric *ink_array_return, + MDrawMetric *logical_array_return, + int array_size, + int *num_chars_return, + MDrawMetric *overall_ink_return, + MDrawMetric *overall_logical_return) +{ + MGlyphString *gstring; + MGlyph *g; + int x; + + ASSURE_CONTROL (control); + *num_chars_return = to - from; + if (array_size < *num_chars_return) + return 0; + if (overall_logical_return) + memset (overall_logical_return, 0, sizeof (MDrawMetric)); + if (overall_ink_return) + memset (overall_ink_return, 0, sizeof (MDrawMetric)); + + M_CHECK_RANGE (mt, from, to, -1, 0); + gstring = get_gstring (frame, mt, from, to, control); + if (! gstring) + { + *num_chars_return = 0; + return 0; + } + + for (g = MGLYPH (1), x = 0; g->type != GLYPH_ANCHOR;) + if (g->pos >= from && g->pos < to) + { + int start = g->pos; + int end = g->to; + int width = g->width; + int lbearing = g->lbearing; + int rbearing = g->rbearing; + int ascent = g->ascent; + int descent = g->descent; + int logical_ascent = g->rface->rfont->ascent; + int logical_descent = g->rface->rfont->descent; + + for (g++; g->type != GLYPH_ANCHOR && g->pos == start; g++) + { + if (lbearing < width + g->lbearing) + lbearing = width + g->lbearing; + if (rbearing < width + g->rbearing) + rbearing = width + g->rbearing; + width += g->width; + if (ascent < g->ascent) + ascent = g->ascent; + if (descent < g->descent) + descent = g->descent; + } + + if (end > to) + end = to; + while (start < end) + { + ink_array_return[start - from].x = x + lbearing; + ink_array_return[start - from].y = - ascent; + ink_array_return[start - from].width = rbearing - lbearing; + ink_array_return[start - from].height = ascent + descent; + logical_array_return[start - from].x = x; + logical_array_return[start - from].y = - logical_descent; + logical_array_return[start - from].height + = logical_ascent + logical_descent; + logical_array_return[start - from].width = width; + start++; + } + x += width; + } + + if (overall_ink_return) + { + overall_ink_return->y = - gstring->line_ascent; + overall_ink_return->x = gstring->lbearing; + overall_ink_return->width = x - gstring->lbearing; + overall_ink_return->height = gstring->height; + } + if (overall_logical_return) + { + overall_logical_return->y = - gstring->ascent; + overall_logical_return->x = 0; + overall_logical_return->width = x; + overall_logical_return->height = gstring->ascent + gstring->descent; + } + + M17N_OBJECT_UNREF (gstring->top); + return 1; +} + +/*=*/ + +/***en + @brief Return the character position nearest to the coordinates. + + The mdraw_coordinates_position () function checks which character + is to be drawn at coordinate ($X, $Y) when the text between $FROM + and $TO of M-text $MT is drawn at the coordinate (0, 0) using the + mdraw_text_with_control () function with the drawing control + object $CONTROL. Here, the character position means the number of + characters that precede the character in question in $MT. + + $FRAME is used only to get the default face information. + + @return + If the glyph image of a character covers coordinate ($X, $Y), + mdraw_coordinates_position () returns the character position of + that character.\n\n + If $Y is less than the minimum Y-coordinate of the drawn area, it + returns $FROM.\n\n\n + If $Y is greater than the maximum Y-coordinate of the drawn area, + it returns $TO.\n\n\n + If $Y fits in with the drawn area but $X is less than the minimum + X-coordinate, it returns the character position of the first + character drawn on the line $Y.\n\n\n + If $Y fits in with the drawn area but $X is greater than the + maximum X-coordinate, it returns the character position of the + last character drawn on the line $Y. */ + +/***ja + @brief »ØÄꤷ¤¿ºÂɸ¤Ë¤¢¤ëʸ»ú¤Î°ÌÃÖ¤òÆÀ¤ë + + ´Ø¿ô mdraw_coordinates_position () ¤Ï¡¢ + + @li ´Ø¿ô mdraw_text () ¤ò»È¤Ã¤Æ + @li M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤ò + @li ºÂɸ (0, 0) ¤òµ¯ÅÀ¤È¤·¤Æ²¾¤ËÉÁ²è¤·¤¿¾ì¹ç + + ºÂɸ ($X, $Y) ¤ËÉÁ²è¤µ¤ì¤ë¥Ù¤­Ê¸»ú¤Îʸ»ú°ÌÃÖ¤òÊÖ¤¹¡£¤³¤³¤Çʸ»ú°ÌÃÖ + ¤È¤Ï¡¢Åö³º M-text Ãæ¤Ë¤ª¤¤¤Æ¤½¤Îʸ»ú¤¬ºÇ½é¤«¤é²¿ÈÖÌܤ«¤ò¼¨¤¹À°¿ô¤Ç + ¤¢¤ë¡£¤¿¤À¤·ºÇ½é¤Îʸ»ú¤Îʸ»ú°ÌÃÖ¤Ï0¤È¤¹¤ë¡£ + + $FRAME ¤Ï¥Ç¥Õ¥©¥ë¥È¥Õ¥§¡¼¥¹¤Î¾ðÊó¤òÆÀ¤ë¤¿¤á¤À¤±¤ËÍѤ¤¤é¤ì¤ë¡£ + + @return + ºÂɸ ($X, $Y) ¤¬¤¢¤ëʸ»ú¤Î¥°¥ê¥Õ¤Çʤ¤ï¤ì¤ë¾ì¹ç¡¢ ´Ø¿ô + mdraw_coordinates_position () ¤Ï¤½¤Îʸ»ú¤Îʸ»ú°ÌÃÖ¤òÊÖ¤¹¡£ + + ¤â¤· $Y ¤¬ÉÁ²èÎΰè¤ÎºÇ¾®YºÂɸ¤è¤ê¤â¾®¤µ¤¤¤Ê¤é¤Ð $FROM ¤òÊÖ¤¹¡£ + + ¤â¤· $Y ¤¬ÉÁ²èÎΰè¤ÎºÇÂçYºÂɸ¤è¤ê¤âÂ礭¤¤¤Ê¤é¤Ð $TO ¤òÊÖ¤¹¡£ + + ¤â¤· $Y ¤¬ÉÁ²èÎΰè¤Ë¾è¤Ã¤Æ¤¤¤Æ¤«¤Ä $X ¤¬ÉÁ²èÎΰè¤ÎºÇ¾®XºÂɸ¤è¤ê¤â + ¾®¤µ¤¤¾ì¹ç¤Ï¡¢Ä¾Àþ y = $Y ¾å¤ËÉÁ²è¤µ¤ì¤ëºÇ½é¤Îʸ»ú¤Îʸ»ú°ÌÃÖ¤òÊÖ¤¹¡£ + + ¤â¤· $Y ¤¬ÉÁ²èÎΰè¤Ë¾è¤Ã¤Æ¤¤¤Æ¤«¤Ä $X ¤¬ÉÁ²èÎΰè¤ÎºÇÂçXºÂɸ¤è¤ê¤â + Â礭¤¤¾ì¹ç¤Ï¡¢Ä¾Àþ y = $Y ¾å¤ËÉÁ²è¤µ¤ì¤ëºÇ¸å¤Îʸ»ú¤Îʸ»ú°ÌÃÖ¤òÊÖ¤¹¡£ */ + +int +mdraw_coordinates_position (MFrame *frame, MText *mt, int from, int to, + int x_offset, int y_offset, MDrawControl *control) +{ + MGlyphString *gstring; + int y = 0; + int width; + MGlyph *g; + + M_CHECK_POS_X (mt, from, -1); + if (to > mtext_nchars (mt) + (control->cursor_width != 0)) + to = mtext_nchars (mt) + (control->cursor_width != 0); + else if (to < from) + to = from; + + if (from == to) + return from; + ASSURE_CONTROL (control); + gstring = get_gstring (frame, mt, from, to, control); + while (y + gstring->line_descent <= y_offset + && gstring->to < to) + { + from = gstring->to; + y += gstring->line_descent; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, from, to, control); + y += gstring->line_ascent; + } + + /* Accumulate width of glyphs in WIDTH until it exceeds X. */ + if (! control->orientation_reversed) + { + width = gstring->indent; + for (g = MGLYPH (1); g[1].type != GLYPH_ANCHOR; g++) + if (g->pos >= from && g->pos < to) + { + width += g->width; + if (width > x_offset) + break; + } + } + else + { + width = - gstring->indent; + for (g = MGLYPH (gstring->used - 2); g->type != GLYPH_ANCHOR; g--) + if (g->pos >= from && g->pos < to) + { + width -= g->width; + if (width < x_offset) + break; + } + } + from = g->pos; + M17N_OBJECT_UNREF (gstring->top); + + return from; +} + +/*=*/ + +/***en + @brief Compute information about a glyph. + + The @c mdraw_glyph_info () function computes information about a + glyph that covers a character at position $POS of the M-text $MT + assuming that the text is drawn from the character at $FROM of $MT + on a window of frame $FRAME using the mdraw_text_with_control () + function with the drawing control object $CONTROL. + + The information is stored in the members of $INFO. */ + +/*** + @seealso + MDrawGlyphInfo +*/ + +int +mdraw_glyph_info (MFrame *frame, MText *mt, int from, int pos, + MDrawControl *control, MDrawGlyphInfo *info) +{ + MGlyphString *gstring; + MGlyph *g; + int y = 0; + + M_CHECK_RANGE_X (mt, from, pos, -1); + + ASSURE_CONTROL (control); + gstring = get_gstring (frame, mt, from, pos + 1, control); + if (! gstring) + MERROR (MERROR_DRAW, -1); + while (gstring->to <= pos) + { + y += gstring->line_descent; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, gstring->to, pos + 1, control); + y += gstring->line_ascent; + } + info->line_from = gstring->from; + if (info->line_from < from) + info->line_from = from; + info->line_to = gstring->to; + + info->y = y; + if (! control->orientation_reversed) + { + info->x = gstring->indent; + for (g = MGLYPH (1); g->pos > pos || g->to <= pos; g++) + info->x += g->width; + } + else + { + info->x = - gstring->indent; + for (g = MGLYPH (gstring->used - 2); g->pos > pos || g->to <= pos; g--) + info->x -= g->width; + while (g[-1].to == g->to) + g--; + } + info->from = g->pos; + info->to = g->to; + info->this.x = g->lbearing; + info->this.y = - gstring->line_ascent; + info->this.height = gstring->height; + if (g->rface->rfont) + info->font = &g->rface->rfont->font; + else + info->font = NULL; + /* info->this.width is calculated later. */ + + if (info->from > info->line_from) + { + /* The logically previous glyph is on this line. */ + MGlyph *g_tmp = find_glyph_in_gstring (gstring, info->from - 1, 1); + + info->prev_from = g_tmp->pos; + } + else if (info->line_from > 0) + { + /* The logically previous glyph is on the previous line. */ + MGlyphString *gst = get_gstring (frame, mt, gstring->from - 1, + gstring->from, control); + MGlyph *g_tmp = find_glyph_in_gstring (gst, info->from - 1, 1); + + info->prev_from = g_tmp->pos; + M17N_OBJECT_UNREF (gst->top); + } + else + info->prev_from = -1; + + if (GLYPH_INDEX (g) > 1) + info->left_from = g[-1].pos, info->left_to = g[-1].to; + else if (! control->orientation_reversed) + { + if (info->line_from > 0) + { + MGlyph *g_tmp; + MGlyphString *gst; + int p = gstring->from - 1; + + gst = get_gstring (frame, mt, p, gstring->from, control); + g_tmp = gst->glyphs + (gst->used - 2); + info->left_from = g_tmp->pos, info->left_to = g_tmp->to; + M17N_OBJECT_UNREF (gst->top); + } + else + info->left_from = info->left_to = -1; + } + else + { + if (gstring->to + (control->cursor_width == 0) <= mtext_nchars (mt)) + { + MGlyph *g_tmp; + MGlyphString *gst; + int p = gstring->to; + + gst = get_gstring (frame, mt, p, p + 1, control); + g_tmp = gst->glyphs + (gst->used - 2); + info->left_from = g_tmp->pos, info->left_to = g_tmp->to; + M17N_OBJECT_UNREF (gst->top); + } + else + info->left_from = info->left_to = -1; + } + + if (info->to < gstring->to) + { + /* The logically next glyph is on this line. */ + MGlyph *g_tmp = find_glyph_in_gstring (gstring, info->to, 0); + + info->next_to = g_tmp->to; + } + else if (info->to + (control->cursor_width == 0) <= mtext_nchars (mt)) + { + /* The logically next glyph is on the next line. */ + int p = info->to; + MGlyphString *gst = get_gstring (frame, mt, p, p + 1, control); + MGlyph *g_tmp = find_glyph_in_gstring (gst, p, 0); + + info->next_to = g_tmp->to; + M17N_OBJECT_UNREF (gst->top); + } + else + info->next_to = -1; + + for (info->this.width = (g++)->width; + g->pos == pos && g->type != GLYPH_ANCHOR; + info->this.width += (g++)->width); + + if (g->type != GLYPH_ANCHOR) + info->right_from = g->pos, info->right_to = g->to; + else if (! control->orientation_reversed) + { + if (gstring->to + (control->cursor_width == 0) <= mtext_nchars (mt)) + { + pos = gstring->to; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, pos, pos + 1, control); + g = MGLYPH (1); + info->right_from = g->pos, info->right_to = g->to; + } + else + info->right_from = info->right_to = -1; + } + else + { + if (info->line_from > 0) + { + pos = gstring->from - 1; + M17N_OBJECT_UNREF (gstring->top); + gstring = get_gstring (frame, mt, pos, pos + 1, control); + g = MGLYPH (1); + info->right_from = g->pos, info->right_to = g->to; + } + else + info->right_from = info->right_to = -1; + } + + M17N_OBJECT_UNREF (gstring->top); + return 0; +} + +/*=*/ + +/***en + @brief Draw one or more textitems. + + The mdraw_text_items () function draws one or more M-texts on + window $WIN of $FRAME at coordinate ($X, $Y). $ITEMS is an array + of the textitems to be drawn and $NITEMS is the number of + textimtems in the array. */ + +/***ja + @brief textitem ¤òɽ¼¨¤¹¤ë + + ´Ø¿ô mdraw_text_items () ¤Ï¡¢°ì¸Ä°Ê¾å¤Î¥Æ¥­¥¹¥È¥¢¥¤¥Æ¥à¤ò¡¢¥Õ¥ì¡¼ + ¥à $FRAME ¤Î¥¦¥£¥ó¥É¥¦ $WIN ¤ÎºÂɸ ($X, $Y) ¤Ëɽ¼¨¤¹¤ë¡£$ITEMS ¤Ï + ɽ¼¨¤¹¤Ù¤­¥Æ¥­¥¹¥È¥¢¥¤¥Æ¥à¤ÎÇÛÎó¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï¤½¤Î + ¸Ä¿ô¤Ç¤¢¤ë¡£ + + @latexonly \IPAlabel{mdraw_text_items} @endlatexonly */ + +/*** + @seealso + MTextItem, mdraw_text (). */ + +void +mdraw_text_items (MFrame *frame, MDrawWindow win, int x, int y, + MDrawTextItem *items, int nitems) +{ + while (nitems-- > 0) + { + if (items->face) + mtext_push_prop (items->mt, 0, mtext_nchars (items->mt), Mface, + items->face); + mdraw_text_with_control (frame, win, x, y, + items->mt, 0, mtext_nchars (items->mt), + items->control); + x += mdraw_text_extents (frame, items->mt, 0, mtext_nchars (items->mt), + items->control, NULL, NULL, NULL); + x += items->delta; + if (items->face) + mtext_pop_prop (items->mt, 0, mtext_nchars (items->mt), Mface); + } +} + +/*=*/ + +int +mdraw_default_line_break (MText *mt, int pos, + int from, int to, int line, int y) +{ + int c = mtext_ref_char (mt, pos); + int orig_pos = pos; + + if (c == ' ' || c == '\t') + { + pos++; + while (pos < to + && ((c = mtext_ref_char (mt, pos)) == ' ' || c == '\t')) + pos++; + } + else + { + while (pos > from) + { + if (c == ' ' || c == '\t') + break; + pos--; + c = mtext_ref_char (mt, pos); + } + if (pos == from) + pos = orig_pos; + else + pos++; + } + return pos; +} + +/*=*/ + +/***en + @brief Obtain per character dimension information. + + The mdraw_per_char_extents () function computes the text dimension + of each character in M-text $MT. The faces given as text + properties in $MT and the default face of frame $FRAME determine + the fonts to draw the text. Each successive element in + $ARRAY_RETURN is set to the drawn metrics of successive + characters, which is relative to the origin of the drawing, and a + rectangle for each character in $MT. The number of elements of + $ARRAY_RETURN must be equal to or greater than the number of + characters in $MT. + + If pointer $OVERALL_RETURN is not @c NULL, this function also + computes the extents of the overall text and stores the results in + the members of the structure pointed to by $OVERALL_RETURN */ + +/***ja + @brief M-text ¤Îʸ»úËè¤Î¾ðÊó¤òÆÀ¤ë + + ´Ø¿ô mdraw_per_char_extents () ¤Ï¡¢M-text $MT Ãæ¤Î³Æʸ»ú¤Îɽ¼¨ÈÏ°Ï + ¤ò·×»»¤¹¤ë¡£¤³¤Î·×»»¤ËÍѤ¤¤ë¥Õ¥©¥ó¥È¤Ï¡¢$MT ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ç + »ØÄꤵ¤ì¤¿¥Õ¥§¡¼¥¹¤È¡¢¥Õ¥ì¡¼¥à $FRAME ¤Î¥Ç¥Õ¥©¥ë¥È¥Õ¥§¡¼¥¹¤«¤é·èÄê + ¤µ¤ì¤ë¡£$ARRAY_RETURN ¤Î³ÆÍ×ÁǤϡ¢Åö³º M-text Ãæ¤Î³Æʸ»ú¤Îɽ¼¨ÈÏ°Ï + ¾ðÊó¤Ë¤è¤Ã¤Æ½ç¤ËËä¤á¤é¤ì¤ë¡£¤³¤Îɽ¼¨ÈϰϾðÊó¤Ï¡¢M-text ¤Îɽ¼¨¸¶ÅÀ + ¤«¤é¤ÎÁêÂаÌÃ֤Ǥ¢¤ë¡£$ARRAY_RETURN ¤ÎÍ×ÁÇ¿ô¤Ï¡¢M-text ¤Îʸ»ú¿ô°Ê + ¾å¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + ¥Ý¥¤¥ó¥¿ $OVERALL_RETURN ¤¬ @c NULL ¤Ç¤Ê¤¤¾ì¹ç¤Ï¡¢¥Æ¥­¥¹¥ÈÁ´ÂΤÎɽ¼¨ + ÈϰϾðÊó¤â·×»»¤·¡¢¤½¤Î·×»»¤ò $OVERALL_RETURN ¤Î»Ø¤¹Àè¤Ë³ÊǼ¤¹¤ë¡£ + + @latexonly \IPAlabel{mdraw_per_char_extents} @endlatexonly */ + +void +mdraw_per_char_extents (MFrame *frame, MText *mt, + MDrawMetric *array_return, + MDrawMetric *overall_return) +{ +} + +void +mdraw_clear_cache (MText *mt) +{ + mtext_pop_prop (mt, 0, mtext_nchars (mt), M_glyph_string); +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/face.c b/src/face.c new file mode 100644 index 0000000..e1a2bc5 --- /dev/null +++ b/src/face.c @@ -0,0 +1,1685 @@ +/* face.c -- face module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nFace + @brief A face is an object to control appearance of M-text. + + A @e face is an object of the type #MFace and controls how to + draw M-texts. A face has a fixed number of @e face @e properties. + Like other types of properties, a face property consists of a key + and a value. A key is one of the following symbols: + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg + + "The face property that belongs to face F and whose key is @c xxx" + may be shortened to "the xxx property of F". + + The M-text drawing functions first search an M-text for the text + property whose key is the symbol #Mface, then draw the M-text + using the value of that text property. This value must be a + pointer to a face object. + + If there are multiple text properties whose key is @c Mface, and + they are not conflicting one another, properties of those faces + are merged and used. + + If no faces specify a certain property, the value of the default + face is used. */ + +/***ja + @addtogroup m17nFace + @brief ¥Õ¥§¡¼¥¹¤È¤Ï¡¢M-text ¤Îɽ¼¨¤òÀ©¸æ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ë + + @e ¥Õ¥§¡¼¥¹ ¤Ï #MFace ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ê¡¢M-text ¤Îɽ¼¨ÊýË¡ + ¤òÀ©¸æ¤¹¤ë¡£¥Õ¥§¡¼¥¹¤Ï¸ÇÄê¸Ä¿ô¤Î @e ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£ ¤ò»ý¤Ä¡£ + ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Ï¥­¡¼¤ÈÃͤ«¤é¤Ê¤ë¡£¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢ + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg + + ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£¥­¡¼¤¬ #Mfontset ¤Ê¤é¤ÐÃͤϥե©¥ó¥È¥»¥Ã¥È¤Ø¤Î + ¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¥­¡¼¤¬ #Msize ¤Ê¤é¤ÐÃͤÏÀ°¿ô¤Ç¤¢¤ë¡£¥­¡¼¤¬¤½¤ì°Ê + ³°¤Ê¤é¤ÐÃͤϥ·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£¡Ö¥Õ¥§¡¼¥¹ F ¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¤¦ + ¤Á¥­¡¼¤¬ @c Mxxx ¤Ç¤¢¤ë¤â¤Î¡×¤Î¤³¤È¤ò´Êñ¤Ë¡ÖF ¤Î xxx ¥×¥í¥Ñ¥Æ¥£¡× + ¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ + + Foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Á°·Ê¿§¤òɽ¤ï¤¹¡£ + + Background ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢ÇØ·Ê¿§¤òɽ¤ï¤¹¡£ + + Reverse ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Á°·Ê¿§¤ÈÇØ·Ê¿§¤òÆþ¤ìÂؤ¨¤ë¤³¤È¤òɽ¤ï¤¹¡£ + + Underline ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢²¼Àþ¤ò°ú¤¯¤«Èݤ«¤òɽ¤ï¤¹¡£ + + Box ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢°Ï¤ßÏȤòɽ¼¨¤¹¤ë¤«Èݤ«¤òɽ¤ï¤¹¡£ + + Fontset ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¥»¥Ã¥È¤òɽ¤ï¤¹¡£ + + Family ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î family ¥×¥í¥Ñ¥Æ¥£¤Î¥Ç + ¥Õ¥©¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + Weight ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î weight ¥×¥í¥Ñ¥Æ¥£¤Î¥Ç + ¥Õ¥©¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + Style ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î style ¥×¥í¥Ñ¥Æ¥£¤Î¥Ç¥Õ¥© + ¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + Stretch ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î stretch ¥×¥í¥Ñ¥Æ¥£¤Î + ¥Ç¥Õ¥©¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + Adstyle ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î adstyle ¥×¥í¥Ñ¥Æ¥£¤Î + ¥Ç¥Õ¥©¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + Size ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢»ÈÍѤ¹¤ë¥Õ¥©¥ó¥È¤Î size ¥×¥í¥Ñ¥Æ¥£¤Î¥Ç¥Õ¥© + ¥ë¥ÈÃͤòɽ¤ï¤¹¡£ + + M-text ¤Îɽ¼¨¤Ë´Ø¤¹¤ë m17n-win API ¤Î´Ø¿ô¤Ï¡¢¤Þ¤ººÇ½é¤Ë¤½¤Î M-text + ¤«¤é¥­¡¼¤¬¥·¥ó¥Ü¥ë #Mface ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òµá¤á¡¢ + ¼¡¤Ë¤½¤ÎÃͤ˽¾¤Ã¤Æ M-text ¤òɽ¼¨¤¹¤ë¡£¤³¤ÎÃͤϥե§¡¼¥¹¥ª¥Ö¥¸¥§¥¯¥È + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + ¤â¤· M-text ¤¬¡¢#Mface ¤ò¥­¡¼¤È¤¹¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ô»ý¤Ã + ¤Æ¤ª¤ê¡¢¤«¤Ä¤½¤ì¤é¤ÎÃͤδ֤˾×Æͤ¬¤Ê¤¤¤Ê¤é¤Ð¡¢¥Õ¥§¡¼¥¹¾ðÊó¤ÏÁȤ߹ç + ¤ï¤µ¤ì¤ÆÍѤ¤¤é¤ì¤ë¡£¾×Æͤ¬¤¢¤ë¾ì¹ç¤Ï¡¢ºÇ¾åÁؤΥե§¡¼¥¹¤¬ÍѤ¤¤é¤ì¤ë¡£ + + ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎºÇ¸å¤Î6¸Ä¡¢¤¹¤Ê¤ï¤Á family, weight, sytle, + stretch, adstyle ¤ª¤è¤Ó size ¤ÎÃÍ¤Ï fontset Ãæ¤Î¥Õ¥©¥ó¥È¤¬¤½¤ì¤é¤ò + »ØÄꤷ¤Æ¤¤¤Ê¤¤¾ì¹ç¤Î¥Ç¥Õ¥©¥ë¥È¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ + + ¤¢¤ë¥Æ¥­¥¹¥È°À­¤¬¤É¤Î¥Õ¥§¡¼¥¹¤Ë¤è¤Ã¤Æ¤â»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ï¡¢¥Ç + ¥Õ¥©¥ë¥È¥Õ¥§¡¼¥¹¤ÎÃͤ¬ÍѤ¤¤é¤ì¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "charset.h" +#include "symbol.h" +#include "plist.h" +#include "mtext.h" +#include "textprop.h" +#include "internal-gui.h" +#include "face.h" +#include "font.h" +#include "fontset.h" + +static M17NObjectArray face_table; + +static MSymbol Mlatin; + +static MSymbol M_face_prop_index; + +/** Find a realized face registered on FRAME that is realized from + FACE and using font RFONT. If RFONT is NULL, find any that + matches FACE. */ + +static MRealizedFace * +find_realized_face (MFrame *frame, MFace *face, MRealizedFont *rfont) +{ + MPlist *rface_list; + MRealizedFace *rface; + int i; + + MPLIST_DO (rface_list, frame->realized_face_list) + { + rface = MPLIST_VAL (rface_list); + if (! rfont + || rface->rfont == rfont) + { + for (i = 0; i < MFACE_RATIO; i++) + if (rface->face.property[i] != face->property[i]) + break; + if (i == MFACE_RATIO) + return rface; + } + } + return NULL; +} + +static void +free_face (void *object) +{ + MFace *face = (MFace *) object; + + if (face->property[MFACE_FONTSET]) + M17N_OBJECT_UNREF (face->property[MFACE_FONTSET]); + if (face->property[MFACE_HLINE]) + free (face->property[MFACE_HLINE]); + if (face->property[MFACE_BOX]) + free (face->property[MFACE_BOX]); + M17N_OBJECT_UNREGISTER (face_table, face); + free (object); +} + + +static MPlist * +serialize_hline (MPlist *plist, MFaceHLineProp *hline) +{ + MPlist *pl = mplist (); + + mplist_add (pl, Minteger, (void *) hline->type); + mplist_add (pl, Minteger, (void *) hline->width); + mplist_add (pl, Msymbol, hline->color); + plist = mplist_add (plist, Mplist, pl); + M17N_OBJECT_UNREF (pl); + return plist; +} + +static MPlist * +serialize_box (MPlist *plist, MFaceBoxProp *box) +{ + MPlist *pl = mplist (); + + mplist_add (pl, Minteger, (void *) box->width); + mplist_add (pl, Minteger, (void *) box->inner_hmargin); + mplist_add (pl, Minteger, (void *) box->inner_vmargin); + mplist_add (pl, Minteger, (void *) box->outer_hmargin); + mplist_add (pl, Minteger, (void *) box->outer_vmargin); + mplist_add (pl, Msymbol, box->color_top); + mplist_add (pl, Msymbol, box->color_bottom); + mplist_add (pl, Msymbol, box->color_left); + mplist_add (pl, Msymbol, box->color_right); + plist = mplist_add (plist, Mplist, pl); + M17N_OBJECT_UNREF (pl); + return plist; +} + +static MPlist * +serialize_face (void *val) +{ + MFace *face = val; + MPlist *plist = mplist (), *pl = plist; + int i; + struct { + MSymbol *key; + MSymbol *type; + MPlist *(*func) (MPlist *plist, void *val); + } serializer[MFACE_PROPERTY_MAX] + = { { &Mfoundry, &Msymbol }, + { &Mfamily, &Msymbol }, + { &Mweight, &Msymbol }, + { &Mstyle, &Msymbol }, + { &Mstretch, &Msymbol }, + { &Madstyle, &Msymbol }, + { &Msize, &Minteger }, + { &Mfontset, NULL }, + { &Mforeground, &Msymbol }, + { &Mbackground, &Msymbol }, + { &Mhline, NULL }, + { &Mbox, NULL }, + { &Mvideomode, &Msymbol }, + { NULL, NULL}, /* MFACE_HOOK_FUNC */ + { NULL, NULL}, /* MFACE_HOOK_ARG */ + { &Mratio, &Minteger } }; + + for (i = 0; i < MFACE_PROPERTY_MAX; i++) + if (face->property[i] && serializer[i].key) + { + pl = mplist_add (pl, Msymbol, *serializer[i].key); + if (serializer[i].type) + pl = mplist_add (pl, *serializer[i].type, face->property[i]); + else if (i == MFACE_FONTSET) + pl = mplist_add (pl, Msymbol, mfontset_name ((MFontset *) + face->property[i])); + else if (i == MFACE_HLINE) + pl = serialize_hline (pl, (MFaceHLineProp *) face->property[i]); + else if (i == MFACE_BOX) + pl = serialize_box (pl, (MFaceBoxProp *) face->property[i]); + } + + return plist; +} + +static void * +deserialize_hline (MPlist *plist) +{ + MFaceHLineProp hline, *hline_ret; + + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + hline.type = MPLIST_INTEGER_P (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + hline.width = MPLIST_INTEGER_P (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FACE, NULL); + hline.color = MPLIST_SYMBOL (plist); + MSTRUCT_MALLOC (hline_ret, MERROR_FACE); + *hline_ret = hline; + return hline_ret; +} + +static void * +deserialize_box (MPlist *plist) +{ + MFaceBoxProp box, *box_ret; + + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + box.width = MPLIST_INTEGER (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + box.inner_hmargin = MPLIST_INTEGER (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + box.inner_vmargin = MPLIST_INTEGER (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + box.outer_hmargin = MPLIST_INTEGER (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_INTEGER_P (plist)) + MERROR (MERROR_FACE, NULL); + box.outer_vmargin = MPLIST_INTEGER (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FACE, NULL); + box.color_top = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FACE, NULL); + box.color_bottom = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FACE, NULL); + box.color_left = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FACE, NULL); + box.color_right = MPLIST_SYMBOL (plist); + MSTRUCT_MALLOC (box_ret, MERROR_FACE); + *box_ret = box; + return box_ret; +} + +static void * +deserialize_face (MPlist *plist) +{ + MFace *face = mface (); + + MPLIST_DO (plist, plist) + { + MSymbol key; + int index; + void *val; + + if (! MPLIST_SYMBOL_P (plist)) + break; + key = MPLIST_SYMBOL (plist); + index = (int) msymbol_get (key, M_face_prop_index) - 1; + plist = MPLIST_NEXT (plist); + if (MPLIST_TAIL_P (plist)) + break; + if (index < 0 || index >= MFACE_PROPERTY_MAX) + continue; + if (key == Mfoundry || key == Mfamily || key == Mweight || key == Mstyle + || key == Mstretch || key == Madstyle + || key == Mforeground || key == Mbackground || key == Mvideomode) + { + if (! MPLIST_SYMBOL_P (plist)) + continue; + val = MPLIST_VAL (plist); + } + else if (key == Msize || key == Mratio) + { + if (! MPLIST_INTEGER_P (plist)) + continue; + val = MPLIST_VAL (plist); + } + else if (key == Mfontset) + { + if (! MPLIST_SYMBOL_P (plist)) + continue; + val = mfontset (MSYMBOL_NAME (MPLIST_SYMBOL (plist))); + } + else if (key == Mhline) + { + if (! MPLIST_PLIST_P (plist)) + continue; + val = deserialize_hline (MPLIST_PLIST (plist)); + } + else if (key == Mbox) + { + if (! MPLIST_PLIST_P (plist)) + continue; + val = deserialize_box (MPLIST_PLIST (plist)); + } + face->property[index] = val; + } + return face; +} + + + +/* Internal API */ + +MFace *mface__default; + +int +mface__init () +{ + int i; + + face_table.count = 0; + Mface = msymbol_as_managing_key ("face"); + msymbol_put (Mface, Mtext_prop_serializer, (void *) serialize_face); + msymbol_put (Mface, Mtext_prop_deserializer, (void *) deserialize_face); + + Mforeground = msymbol ("foreground"); + Mbackground = msymbol ("background"); + Mvideomode = msymbol ("videomode"); + Mnormal = msymbol ("normal"); + Mreverse = msymbol ("reverse"); + Mratio = msymbol ("ratio"); + Mhline = msymbol ("hline"); + Mbox = msymbol ("box"); + Mhook_func = msymbol ("hook-func"); + Mhook_arg = msymbol ("hook-arg"); + + Mlatin = msymbol ("latin"); + M_face_prop_index = msymbol (" face-prop-index"); + + { + struct { + /* Pointer to the key symbol of the face property. */ + MSymbol *key; + /* Index (enum face_property) of the face property. */ + int index; + } mface_prop_data[MFACE_PROPERTY_MAX] = + { { &Mfoundry, MFACE_FOUNDRY }, + { &Mfamily, MFACE_FAMILY }, + { &Mweight, MFACE_WEIGHT }, + { &Mstyle, MFACE_STYLE }, + { &Mstretch, MFACE_STRETCH }, + { &Madstyle, MFACE_ADSTYLE }, + { &Msize, MFACE_SIZE }, + { &Mfontset, MFACE_FONTSET }, + { &Mforeground, MFACE_FOREGROUND }, + { &Mbackground, MFACE_BACKGROUND }, + { &Mhline, MFACE_HLINE }, + { &Mbox, MFACE_BOX }, + { &Mvideomode, MFACE_VIDEOMODE }, + { &Mhook_func, MFACE_HOOK_FUNC }, + { &Mhook_arg, MFACE_HOOK_ARG }, + { &Mratio, MFACE_RATIO }, + }; + + for (i = 0; i < MFACE_PROPERTY_MAX; i++) + /* We add one to distinguish it from no-property. */ + msymbol_put (*mface_prop_data[i].key, M_face_prop_index, + (void *) (mface_prop_data[i].index + 1)); + } + + mface__default = mface (); + mface__default->property[MFACE_WEIGHT] = msymbol ("medium"); + mface__default->property[MFACE_STYLE] = msymbol ("r"); + mface__default->property[MFACE_STRETCH] = msymbol ("normal"); + mface__default->property[MFACE_SIZE] = (void *) 120; + mface__default->property[MFACE_FONTSET] = mfontset (NULL); + M17N_OBJECT_REF (mface__default->property[MFACE_FONTSET]); + /* mface__default->property[MFACE_FOREGROUND] =msymbol ("black"); */ + /* mface__default->property[MFACE_BACKGROUND] =msymbol ("white"); */ + + mface_normal_video = mface (); + mface_normal_video->property[MFACE_VIDEOMODE] = (void *) Mnormal; + + mface_reverse_video = mface (); + mface_reverse_video->property[MFACE_VIDEOMODE] = (void *) Mreverse; + + { + MFaceHLineProp *hline_prop; + + MSTRUCT_MALLOC (hline_prop, MERROR_FACE); + hline_prop->type = MFACE_HLINE_UNDER; + hline_prop->width = 1; + hline_prop->color = Mnil; + mface_underline = mface (); + mface_underline->property[MFACE_HLINE] = (void *) hline_prop; + } + + mface_medium = mface (); + mface_medium->property[MFACE_WEIGHT] = (void *) msymbol ("medium"); + mface_bold = mface (); + mface_bold->property[MFACE_WEIGHT] = (void *) msymbol ("bold"); + mface_italic = mface (); + mface_italic->property[MFACE_STYLE] = (void *) msymbol ("i"); + mface_bold_italic = mface_copy (mface_bold); + mface_bold_italic->property[MFACE_STYLE] + = mface_italic->property[MFACE_STYLE]; + + mface_xx_small = mface (); + mface_xx_small->property[MFACE_RATIO] = (void *) 50; + mface_x_small = mface (); + mface_x_small->property[MFACE_RATIO] = (void *) 67; + mface_small = mface (); + mface_small->property[MFACE_RATIO] = (void *) 75; + mface_normalsize = mface (); + mface_normalsize->property[MFACE_RATIO] = (void *) 100; + mface_large = mface (); + mface_large->property[MFACE_RATIO] = (void *) 120; + mface_x_large = mface (); + mface_x_large->property[MFACE_RATIO] = (void *) 150; + mface_xx_large = mface (); + mface_xx_large->property[MFACE_RATIO] = (void *) 200; + + mface_black = mface (); + mface_black->property[MFACE_FOREGROUND] = (void *) msymbol ("black"); + mface_white = mface (); + mface_white->property[MFACE_FOREGROUND] = (void *) msymbol ("white"); + mface_red = mface (); + mface_red->property[MFACE_FOREGROUND] = (void *) msymbol ("red"); + mface_green = mface (); + mface_green->property[MFACE_FOREGROUND] = (void *) msymbol ("green"); + mface_blue = mface (); + mface_blue->property[MFACE_FOREGROUND] = (void *) msymbol ("blue"); + mface_cyan = mface (); + mface_cyan->property[MFACE_FOREGROUND] = (void *) msymbol ("cyan"); + mface_yellow = mface (); + mface_yellow->property[MFACE_FOREGROUND] = (void *) msymbol ("yellow"); + mface_magenta = mface (); + mface_magenta->property[MFACE_FOREGROUND] = (void *) msymbol ("magenta"); + return 0; +} + +void +mface__fini () +{ + M17N_OBJECT_UNREF (mface__default); + M17N_OBJECT_UNREF (mface_normal_video); + M17N_OBJECT_UNREF (mface_reverse_video); + M17N_OBJECT_UNREF (mface_underline); + M17N_OBJECT_UNREF (mface_medium); + M17N_OBJECT_UNREF (mface_bold); + M17N_OBJECT_UNREF (mface_italic); + M17N_OBJECT_UNREF (mface_bold_italic); + M17N_OBJECT_UNREF (mface_xx_small); + M17N_OBJECT_UNREF (mface_x_small); + M17N_OBJECT_UNREF (mface_small); + M17N_OBJECT_UNREF (mface_normalsize); + M17N_OBJECT_UNREF (mface_large); + M17N_OBJECT_UNREF (mface_x_large); + M17N_OBJECT_UNREF (mface_xx_large); + M17N_OBJECT_UNREF (mface_black); + M17N_OBJECT_UNREF (mface_white); + M17N_OBJECT_UNREF (mface_red); + M17N_OBJECT_UNREF (mface_green); + M17N_OBJECT_UNREF (mface_blue); + M17N_OBJECT_UNREF (mface_cyan); + M17N_OBJECT_UNREF (mface_yellow); + M17N_OBJECT_UNREF (mface_magenta); + mdebug__report_object ("Face", &face_table); +} + +/** Return a realized face for ASCII characters from NUM number of + base faces pointed by FACES on the frame FRAME. */ + +MRealizedFace * +mface__realize (MFrame *frame, MFace **faces, int num, + MSymbol language, MSymbol charset, int size) +{ + MRealizedFace *rface; + MRealizedFont *rfont; + MFace merged_face = *(frame->face); + void **props; + int i, j; + unsigned tick; + MGlyph g; + + if (num == 0 && language == Mnil && charset == Mnil && frame->rface) + return frame->rface; + + for (i = 0; i < MFACE_PROPERTY_MAX; i++) + for (j = num - 1; j >= 0; j--) + if (faces[j]->property[i]) + { + merged_face.property[i] = faces[j]->property[i]; + break; + } + + for (i = 0, tick = 0; i < num; i++) + tick += faces[i]->tick; + + if (merged_face.property[MFACE_RATIO]) + { + int font_size = (int) merged_face.property[MFACE_SIZE]; + + font_size *= (int) merged_face.property[MFACE_RATIO]; + font_size /= 100; + merged_face.property[MFACE_SIZE] = (void *) font_size; + } + + if ((MSymbol) merged_face.property[MFACE_VIDEOMODE] == Mreverse) + { + MSymbol foreground = (MSymbol) merged_face.property[MFACE_FOREGROUND]; + MSymbol background = (MSymbol) merged_face.property[MFACE_BACKGROUND]; + + merged_face.property[MFACE_FOREGROUND] = background; + merged_face.property[MFACE_BACKGROUND] = foreground; + } + + rface = find_realized_face (frame, &merged_face, NULL); + if (rface && rface->tick == tick) + return rface->ascii_rface; + + MSTRUCT_CALLOC (rface, MERROR_FACE); + rface->frame = frame; + rface->face = merged_face; + rface->tick = tick; + props = rface->face.property; + + rface->rfontset = mfont__realize_fontset (frame, + (MFontset *) props[MFACE_FONTSET], + &merged_face); + g.c = ' '; + num = 1; + rfont = mfont__lookup_fontset (rface->rfontset, &g, &num, + msymbol ("latin"), language, Mnil, + size); + + if (rfont) + { + rface->rfont = rfont; + g.otf_encoded = 0; + mfont__get_metric (rfont, &g); + rface->space_width = g.width; + g.code = MCHAR_INVALID_CODE; + mfont__get_metric (rface->rfont, &g); + rface->ascent = g.ascent; + rface->descent = g.descent; + } + else + { + rface->rfont = NULL; + rface->space_width = frame->space_width; + } + + rface->hline = (MFaceHLineProp *) props[MFACE_HLINE]; + rface->box = (MFaceBoxProp *) props[MFACE_BOX]; + rface->ascii_rface = rface; + mwin__realize_face (rface); + + mplist_add (frame->realized_face_list, Mt, rface); + + if (rface->rfont) + { + MSTRUCT_CALLOC (rface->nofont_rface, MERROR_FACE); + *rface->nofont_rface = *rface; + rface->nofont_rface->rfont = NULL; + } + else + rface->nofont_rface = rface; + + return rface; +} + + +MGlyph * +mface__for_chars (MSymbol script, MSymbol language, MSymbol charset, + MGlyph *from_g, MGlyph *to_g, int size) +{ + MRealizedFace *rface; + MRealizedFont *rfont; + int num = to_g - from_g, i; + + rfont = mfont__lookup_fontset (from_g->rface->rfontset, from_g, &num, + script, language, charset, size); + if (! rfont) + { + from_g->rface = from_g->rface->nofont_rface; + return (from_g + 1); + } + rface = find_realized_face (from_g->rface->frame, &(from_g->rface->face), + rfont); + if (! rface) + { + MSTRUCT_MALLOC (rface, MERROR_FACE); + *rface = *from_g->rface->ascii_rface; + rface->rfont = rfont; + { + MGlyph tmp; + + tmp.code = MCHAR_INVALID_CODE; + mfont__get_metric (rfont, &tmp); + rface->ascent = tmp.ascent; + rface->descent = tmp.descent; + } + mwin__realize_face (rface); + mplist_add (from_g->rface->frame->realized_face_list, Mt, rface); + } + + for (i = 0; i < num; i++, from_g++) + from_g->rface = rface; + return from_g; +} + + +void +mface__free_realized (MRealizedFace *rface) +{ + mwin__free_realized_face (rface); + if (rface == rface->ascii_rface) + { + if (! rface->nofont_rface) + mdebug_hook (); + else + free (rface->nofont_rface); + rface->nofont_rface = NULL; + } + free (rface); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ +/*** @addtogroup m17nFace */ +/*** @{ */ +/*=*/ + +/***en @name Variables: Keys of face property */ +/***ja @name ÊÑ¿ô: ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë */ +/*** @{ */ +/*=*/ + +/***en + @brief Key of a face property specifying foreground color. + + The variable #Mforeground is used as a key of face property. The + property value must be a symbol whose name is a color name, or + #Mnil. + + #Mnil means that the face does not specify a foreground color. + + Otherwise, the foreground of an M-text is drawn by the specified + color. */ + +/***ja + @brief Á°·Ê¿§¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Mforeground ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥·¥ó¥Ü¥ë @c + Mnil ¤Ç¤¢¤ë¤«¡¢¤¢¤ë¤¤¤Ï¿§Ì¾¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é + ¤Ê¤¤¡£Á°¼Ô¤Î¾ì¹ç¡¢Á°·Ê¿§¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£¸å¼Ô¤Î¾ì¹ç¤Ï¤½¤Î¥·¥ó¥Ü¥ë¤Î + ̾Á°¤Î¿§¤¬Á°·Ê¿§¤È¤Ê¤ë¡£ */ + +MSymbol Mforeground; + +/***en + @brief Key of a face property specifying background color. + + The variable #Mbackground is used as a key of face property. The + property value must be a symbol whose name is a color name, or + #Mnil. + + #Mnil means that the face does not specify a background color. + + Otherwise, the background of an M-text is drawn by the specified + color. */ + +/***ja + @brief ÇØ·Ê¿§¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Mbackground ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥·¥ó¥Ü¥ë @c + Mnil ¤Ç¤¢¤ë¤«¡¢¤¢¤ë¤¤¤Ï¿§Ì¾¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é + ¤Ê¤¤¡£Á°¼Ô¤Î¾ì¹ç¡¢ÇØ·Ê¿§¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£¸å¼Ô¤Î¾ì¹ç¤Ï¤½¤Î¥·¥ó¥Ü¥ë¤Î + ̾Á°¤Î¿§¤¬ÇØ·Ê¿§¤È¤Ê¤ë¡£ */ + +MSymbol Mbackground; + +/***en + @brief Key of a face property specifying video mode. + + The variable #Mvideomode is used as a key of face property. The + property value must be #Mnormal, #Mreverse, or #Mnil. + + #Mnormal means that an M-text is drawn in normal video mode + (i.e. the foreground is drawn by foreground color, the background + is drawn by background color). + + #Mreverse means that an M-text is drawn in reverse video mode + (i.e. the foreground is drawn by background color, the background + is drawn by foreground color). + + #Mnil means that the face does not specify a video mode. */ + +/***ja + @brief ¥Ó¥Ç¥ª¥â¡¼¥É¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Mvideomode ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϥ·¥ó¥Ü¥ë¤Ç¤Ê¤± + ¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤â¤· #Mreverse ¤Î¾ì¹ç¤ÏÁ°·Ê¿§¤ÈÇطʼ￧¤òÆþ¤ìÂؤ¨ + ¤ÆÍѤ¤¤ë¡£#Mnil ¤Î¾ì¹ç¤Ï¥Ó¥Ç¥ª¥â¡¼¥É¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£¤½¤ì°Ê³°¤Î¥· + ¥ó¥Ü¥ë¤Î¾ì¹çÁ°·Ê¿§¤ÈÇطʼ￧¤òÆþ¤ìÂؤ¨¤ò¹Ô¤Ê¤ï¤Ê¤¤¡£ */ + +MSymbol Mvideomode; + +/***en + @brief Key of a face property specifying font size ratio. + + The variable #Mratio is used as a key of face property. The value + RATIO must be an integer. + + The value @c NULL means that the face does not specify a + horizontal line. Otherwise, an M-text is drawn by a font of size + (FONTSIZE * RATIO / 100) where FONTSIZE is a font size specified + by the face property #Msize. */ + +MSymbol Mratio; + +/***en + @brief Key of a face property specifying horizontal line. + + The variable #Mhline is used as a key of face property. The value + must be a pointer to an object of type #MFaceHLineProp, or @c + NULL. + + The value @c NULL means that the face does not specify this + property. Otherwise, an M-text is drawn with a horizontal line by + a way specified by the object that the value points to. */ + +/***ja + @brief ²¼Àþ¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Munderline ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥·¥ó¥Ü¥ë + #Mt, #Mnil, #Munspecified ¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£#Mt + ¤Î¾ì¹ç¤Ï²¼ÀþÉÕ¤­¤Çɽ¼¨¤¹¤ë¡£#Mnil ¤Î¾ì¹ç¤Ï²¼Àþ¤òÉÕ¤±¤Ê¤¤¡£ + #Munspecified ¤Î¾ì¹ç¤Ï¤É¤Á¤é¤È¤â»ØÄꤵ¤ì¤Ê¤¤¡£ */ + +MSymbol Mhline; + +/***en + @brief Key of a face property specifying box. + + The variable #Mbox is used as a key of face property. The value + must be a pointer to an object of type #MFaceBoxProp, or @c NULL. + + The value @c NULL means that the face does not specify a box. + Otherwise, an M-text is drawn with a surrounding box by a way + specified by the object that the value points to. */ + +/***ja + @brief °Ï¤ßÏȤò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Mbox ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥·¥ó¥Ü¥ë #Mt, @c + Mnil, #Munspecified ¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£#Mt ¤Î¾ì¹ç + ¤Ï°Ï¤ßÏÈÉÕ¤­¤Çɽ¼¨¤¹¤ë¡£#Mnil ¤Î¾ì¹ç¤Ï°Ï¤ßÏȤòÉÕ¤±¤Ê¤¤¡£@c + Munspecified ¤Î¾ì¹ç¤Ï¤É¤Á¤é¤È¤â»ØÄꤵ¤ì¤Ê¤¤¡£ */ + +MSymbol Mbox; + +/***en + @brief Key of a face property specifying fontset. + + The variable #Mfontset is used as a key of face property. The + value must be a pointer to an object of type #Mfontset, or @c + NULL. + + The value @c NULL means that the face does not specify a fontset. + Otherwise, an M-text is drawn with a font selected from what + specified in the fontset. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥­¡¼¤¬ #Mfontset ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥·¥ó¥Ü¥ë @c + Munspecified ¤Ç¤¢¤ë¤«¡¢¤µ¤â¤Ê¤¯¤Ð¥Õ¥©¥ó¥È¥»¥Ã¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤± + ¤ì¤Ð¤Ê¤é¤Ê¤¤¡£Á°¼Ô¤Î¾ì¹ç¤Ï¥Õ¥©¥ó¥È¥»¥Ã¥È¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¤³¤È¤ò¼¨ + ¤·¡¢¸å¼Ô¤Î¾ì¹ç¤Ï¤½¤Î¥Ý¥¤¥ó¥¿¤Î»Ø¤¹¥Õ¥©¥ó¥È¥»¥Ã¥È¤¬¥Æ¥­¥¹¥Èɽ¼¨¤ËÍÑ + ¤¤¤é¤ì¤ë¡£ */ + +MSymbol Mfontset; + +/***en + @brief Key of a face property specifying hook. + + The variable #Mhook_func is used as a key of face property. The + value must be a function of type #MFaceHookFunc, or @c NULL. + + The value @c NULL means that the face does not specify a hook. + Otherwise, the specified function is called before the face is + realized. */ + +MSymbol Mhook_func; + +/***en + @brief Key of a face property specifying argument of hook. + + The variable #Mhook_arg is used as a key of face property. The + value can be anything that is passed a hook function specified by + the face property #Mhook_func. */ + +MSymbol Mhook_arg; + +/*** @} */ +/*=*/ + +/*** @ingroup m17nFace */ +/***en @name Variables: Possible values of #Mvideomode property of face */ +/*** @{ */ +/*=*/ + +/***en + See the documentation of the variable #Mvideomode. */ +MSymbol Mnormal; +MSymbol Mreverse; +/*** @} */ +/*=*/ + +/*** @ingroup m17nFace */ +/***en @name Variables: Predefined faces */ +/***ja @name ÊÑ¿ô: ÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹ */ +/*** @{ */ +/*=*/ + +/***en + @brief Normal video face + + The variable #mface_normal_video points to a face that has the + #Mvideomode property with value #Mnormal. The other properties + are not specified. An M-text drawn with this face appear normal + colors (i.e. the foreground is drawn by foreground color, and + background is drawn by background color). */ + +MFace *mface_normal_video; + +/***en + @brief Reverse video face + + The variable #mface_reverse_video points to a face that has the + #Mvideomode property with value #Mreverse. The other properties + are not specified. An M-text drawn with this face appear in + reversed colors (i.e. the foreground is drawn by background + color, and background is drawn by foreground color). */ + +/***ja + @brief ¥ê¥Ð¡¼¥¹¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #maface_reverse ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì + ¤¿ M-text ¤ÏÁ°·Ê¿§¤ÈÇØ·Ê¿§¤¬Æþ¤ìÂؤï¤ë¡£ */ + +MFace *mface_reverse_video; + +/***en + @brief Underline face. + + The variable #mface_underline points to a face that has the + #Mhline property with value a pointer to an object of type + #MFaceHLineProp. The members of the object are as follows: + +@verbatim + member value + ----- ----- + type MFACE_HLINE_UNDER + width 1 + color Mnil +@endverbatim + + The other properties are not specified. An M-text that has this + face is drawn with an underline. */ + +MFace *mface_underline; + +/***en + @brief Medium face. + + The variable #mface_medium points to a face that has the #Mweight + property with value a symbol of name "medium". The other + properties are not specified. An M-text that has this face is + drawn with a font of medium weight. */ +MFace *mface_medium; + +/***en + @brief Bold face + + The variable #mface_bold points to a face that has the #Mweight + property with value a symbol of name "bold". The other properties + are not specified. An M-text that has this face is drawn with a + font of bold weight. */ + +/***ja + @brief ¥Ü¡¼¥ë¥É¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_bold ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢weight + ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¥Ü¡¼¥ë¥ÉÂΤò°ÕÌ£¤¹¤ë¥·¥¹¥Æ¥à°Í¸¤Î¥·¥ó¥Ü¥ë¤ò»ý + ¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹ + ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤Ï¥Ü¡¼¥ë¥ÉÂΤȤʤ롣 */ + +MFace *mface_bold; + +/***en + @brief Italic face + + The variable #mface_italic points to a face that has the #Mstyle + property with value a symbol of name "italic". The other + properties are not specified. An M-text that has this face is + drawn with a font of italic style. */ + +/***ja + @brief ¥¤¥¿¥ê¥Ã¥¯¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_italic ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢style + ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¥¤¥¿¥ê¥Ã¥¯ÂΤò°ÕÌ£¤¹¤ë¥·¥¹¥Æ¥à°Í¸¤Î¥·¥ó¥Ü¥ë¤ò + »ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹Ä̤ê¤Ç¤¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹ + ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤Ï¥¤¥¿¥ê¥Ã¥¯ÂΤȤʤ롣 */ + +MFace *mface_italic; + +/***en + @brief Bold italic face + + The variable #mface_bold_italic points to a face that has the + #Mweight property with value a symbol of name "bold", and #Mstyle + property with value a symbol of name "italic". The other + properties are not specified. An M-text that has this face is + drawn with a font of bold weight and italic style. */ + +/***ja + @brief ¥Ü¡¼¥ë¥É¥¤¥¿¥ê¥Ã¥¯¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_bold_italic ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î + ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Ï¡¢weight ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¥Ü¡¼¥ë¥ÉÂΤò°ÕÌ£ + ¤¹¤ë¥·¥¹¥Æ¥à°Í¸¤Î¥·¥ó¥Ü¥ë¤ò¡¢style ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¥¤¥¿¥ê¥Ã¥¯ + ÂΤò°ÕÌ£¤¹¤ë¥·¥¹¥Æ¥à°Í¸¤Î¥·¥ó¥Ü¥ë¤ò¡¢¤½¤ì°Ê³°¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤȤ·¤Æ²¼¤Ë¼¨¤¹¤â¤Î¤ò¤½¤ì¤¾¤ì»ý¤Ä¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì + ¤¿ M-text ¤Ï¥Ü¡¼¥ë¥É¥¤¥¿¥ê¥Ã¥¯ÂΤȤʤ롣 */ + +MFace *mface_bold_italic; + +/***en + @brief Smallest face + + The variable #mface_xx_small points to a face that has the #Mratio + property with value 50. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 50% of a normal font. */ + +/***ja + @brief ºÇ¾®¤Î¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_xx_small ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï @c + mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ @c + mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 50% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_xx_small; + +/***en + @brief Smaller face + + The variable #mface_x_small points to a face that has the #Mratio + property with value 66. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 66% of a normal font. */ + +/***ja + @brief ¤è¤ê¾®¤µ¤¤¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_x_small ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï + #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + #mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 66% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_x_small; + +/***en + @brief Small face + + The variable #mface_x_small points to a face that has the #Mratio + property with value 75. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 75% of a normal font. */ + +/***ja + @brief ¾®¤µ¤¤¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_small ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï + #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + #mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 75% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_small; + +/***en + @brief Normalsize face + + The variable #mface_normalsize points to a face that has the + #Mratio property with value 100. The other properties are not + specified. An M-text that has this face is drawn with a font + whose size is the same as a normal font. */ + +/***ja + @brief ɸ½à¤ÎÂ礭¤µ¤Î¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼¤Ë + ¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï¥Ý¥¤¥ó¥È¥µ¥¤¥º¤ò + ɽ¤ï¤¹À°¿ô¤Ç¤¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + ¥Ç¥Õ¥©¥ë¥È¥Õ¥ì¡¼¥à¤Î¥Ç¥Õ¥©¥ë¥È¥Õ¥©¥ó¥È¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤Î + Â礭¤µ¤ËÅù¤·¤¤¡£ */ + +MFace *mface_normalsize; + +/***en + @brief Large face + + The variable #mface_large points to a face that has the #Mratio + property with value 120. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 120% of a normal font. */ + +/***ja + @brief Â礭¤¤¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_large ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï + #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + #mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 120% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_large; + +/***en + @brief Larger face + + The variable #mface_x_large points to a face that has the #Mratio + property with value 150. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 150% of a normal font. */ + +/***ja + @brief ¤è¤êÂ礭¤¤¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_x_large ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï + #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + #mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 150% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_x_large; + +/***en + @brief Largest face + + The variable #mface_xx_large points to a face that has the #Mratio + property with value 200. The other properties are not specified. + An M-text that has this face is drawn with a font whose size is + 200% of a normal font. */ + +/***ja + @brief ºÇÂç¤Î¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_xx_large ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢²¼ + ¤Ë¼¨¤¹¤è¤¦¤Ê¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¤³¤³¤Ç @c N ¤Ï + #mface_normalsize ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Î size ¥×¥í¥Ñ¥Æ¥£ + ¤ÎÃͤǤ¢¤ë¡£¤³¤Î¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Ï¡¢ + #mface_normalsize ¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÂ礭¤µ¤Î 200% ¤Ë¤Ê¤ë¡£ + */ + +MFace *mface_xx_large; + +/***en + @brief Black face + + The variable #mface_black points to a face that has the + #Mforeground property with value a symbol of name "black". The + other properties are not specified. An M-text that has this face + is drawn with black foreground. */ + +/***ja + @brief ¹õ¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_black ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + #Mforeground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"black" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤Ï¹õ¤Ë¤Ê¤ë¡£ */ + +MFace *mface_black; + +/***en + @brief White face + + The variable #mface_white points to a face that has the + #Mforeground property with value a symbol of name "white". The + other properties are not specified. An M-text that has this face + is drawn with white foreground. */ + +/***ja + @brief Çò¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_white ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"white" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤ÏÇò¤Ë¤Ê¤ë¡£ */ + +MFace *mface_white; + +/***en + @brief Red face + + The variable #mface_red points to a face that has the + #Mforeground property with value a symbol of name "red". The + other properties are not specified. An M-text that has this face + is drawn with red foreground. */ + +/***ja + @brief ÀÖ¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_red ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"red" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤ÏÀ֤ˤʤ롣 */ + +MFace *mface_red; + +/***en + @brief Green face + + The variable #mface_green points to a face that has the + #Mforeground property with value a symbol of name "green". The + other properties are not specified. An M-text that has this face + is drawn with green foreground. */ + +/***ja + @brief ÎÐ¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_green ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"green" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤ÏÎФˤʤ롣 */ + +MFace *mface_green; + +/***en + @brief Blue face + + The variable #mface_blue points to a face that has the + #Mforeground property with value a symbol of name "blue". The + other properties are not specified. An M-text that has this face + is drawn with blue foreground. */ + +/***ja + @brief ÀÄ¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_blue ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"blue" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤ÏÀĤˤʤ롣 */ + +MFace *mface_blue; + +/***en + @brief Cyan face + + The variable #mface_cyan points to a face that has the + #Mforeground property with value a symbol of name "cyan". The + other properties are not specified. An M-text that has this face + is drawn with cyan foreground. */ + +/***ja + @brief ¥·¥¢¥ó¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_cyan ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"cyan" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤Ï¥·¥¢¥ó¤Ë¤Ê¤ë¡£ */ + +MFace *mface_cyan; + +/***en + @brief yellow face + + The variable #mface_yellow points to a face that has the + #Mforeground property with value a symbol of name "yellow". The + other properties are not specified. An M-text that has this face + is drawn with yellow foreground. */ + +/***ja + @brief ²«¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_yellow ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"yellow" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤Ï²«¿§¤Ë¤Ê¤ë¡£ */ + +MFace *mface_yellow; + + +/***en + @brief Magenta face + + The variable #mface_magenta points to a face that has the + #Mforeground property with value a symbol of name "magenta". The + other properties are not specified. An M-text that has this face + is drawn with magenta foreground. */ + +/***ja + @brief ¥Þ¥¼¥ó¥¿¥Õ¥§¡¼¥¹ + + ¥Ý¥¤¥ó¥¿ #mface_magenta ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ëÄêµÁºÑ¤ß¥Õ¥§¡¼¥¹¤Ï¡¢ + foreground ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ"magenta" ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü + ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î + ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ M-text ¤ÎÁ°·Ê¿§¤Ï¥Þ¥¼¥ó¥¿¤Ë¤Ê¤ë¡£ */ + +MFace *mface_magenta; + +/*** @} */ +/*=*/ + +/***en @name Variables: The other symbols for face handling. */ +/***ja @name ÊÑ¿ô: ¥Õ¥§¡¼¥¹¤ò¼è¤ê°·¤¦¤¿¤á¤Î¤½¤Î¾¤ÎÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë */ +/*** @{ */ +/*=*/ + +/***en + @brief Key of a text property specifying a face. + + The variable #Mface is a symbol of name "face". A text + property whose key is this symbol must have a pointer to an object + of type #MFace. This is a managing key. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤Ê¤ë¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë #Mface ¤Ï "face" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¡£¥­¡¼¤¬ @c + Mface ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥Õ¥§¡¼¥¹¥ª¥Ö¥¸¥§¥¯¥È¤Ø + ¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ */ + +MSymbol Mface; +/*=*/ +/*** @} */ +/*=*/ + +/***en + @brief Create a new face. + + The mface () function creates a new face object that specifies no + property. + + @return + This function returns a pointer to the created face. */ + +/***ja + @brief ¿·¤·¤¤¥Õ¥§¡¼¥¹¤ò¤Ä¤¯¤ë + + ´Ø¿ô mface () ¤Ï¿·¤·¤¤¥Õ¥§¡¼¥¹¥ª¥Ö¥¸¥§¥¯¥È¤òºî¤ë¡£ + ºî¤é¤ì¤¿¥Õ¥§¡¼¥¹¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£ + + @li ¥­¡¼ #Mfontset ¤ËÂФ¹¤ëÃÍ¤Ï @c NULL + @li ¥­¡¼ #Msize ¤ËÂФ¹¤ëÃÍ¤Ï 0 + @li ¤½¤Î¾¤Î¥­¡¼¤ËÂФ¹¤ëÃÍ¤Ï #Munspecified */ + +MFace * +mface () +{ + MFace *face; + + M17N_OBJECT (face, free_face, MERROR_FACE); + M17N_OBJECT_REGISTER (face_table, face); + return face; +} + +/*=*/ + +/***en + @brief Make a copy of a face. + + The mface_copy () function makes a copy of $FACE and returns a + pointer to the created copy. */ + +/***ja + @brief ¥Õ¥§¡¼¥¹¤Î¥³¥Ô¡¼¤òºî¤ë + + ´Ø¿ô mface_copy () ¤Ï¥Õ¥§¡¼¥¹ $FACE ¤Î¥³¥Ô¡¼¤òºî¤ê¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó + ¥¿¤òÊÖ¤¹¡£ */ + +MFace * +mface_copy (MFace *face) +{ + MFace *copy; + + MSTRUCT_CALLOC (copy, MERROR_FACE); + *copy = *face; + copy->control.ref_count = 1; + M17N_OBJECT_REGISTER (face_table, copy); + if (copy->property[MFACE_FONTSET]) + M17N_OBJECT_REF (copy->property[MFACE_FONTSET]); + if (copy->property[MFACE_HLINE]) + { + MFaceHLineProp *val; + + MSTRUCT_MALLOC (val, MERROR_FACE); + *val = *((MFaceHLineProp *) copy->property[MFACE_HLINE]); + copy->property[MFACE_HLINE] = val; + } + if (copy->property[MFACE_BOX]) + { + MFaceBoxProp *val; + + MSTRUCT_MALLOC (val, MERROR_FACE); + *val = *((MFaceBoxProp *) copy->property[MFACE_BOX]); + copy->property[MFACE_BOX] = val; + } + + return copy; +} + +/*=*/ +/***en + @brief Merge faces. + + The mface_merge () functions merges the properties of face $SRC + into $DST. + + @return + This function returns $DST. */ + +MFace * +mface_merge (MFace *dst, MFace *src) +{ + int i; + + for (i = 0; i < MFACE_PROPERTY_MAX; i++) + if (src->property[i]) + { + dst->property[i] = src->property[i]; + if (i == MFACE_FONTSET) + M17N_OBJECT_REF (dst->property[i]); + else if (i == MFACE_HLINE) + { + MFaceHLineProp *val; + + MSTRUCT_MALLOC (val, MERROR_FACE); + *val = *((MFaceHLineProp *) dst->property[MFACE_HLINE]); + dst->property[MFACE_HLINE] = val; + } + else if (i == MFACE_BOX) + { + MFaceBoxProp *val; + + MSTRUCT_MALLOC (val, MERROR_FACE); + *val = *((MFaceBoxProp *) dst->property[MFACE_BOX]); + dst->property[MFACE_BOX] = val; + } + } + return dst; +} + +/*=*/ + +/***en + @brief Make a face from a font. + + The mface_from_font () function return a newly created face while + reflecting the properties of $FONT in its properties. */ + +MFace * +mface_from_font (MFont *font) +{ + MFace *face = mface (); + + face->property[MFACE_FOUNDRY] = mfont_get_prop (font, Mfoundry); + face->property[MFACE_FAMILY] = mfont_get_prop (font, Mfamily); + face->property[MFACE_WEIGHT] = mfont_get_prop (font, Mweight); + face->property[MFACE_STYLE] = mfont_get_prop (font, Mstyle); + face->property[MFACE_STRETCH] = mfont_get_prop (font, Mstretch); + face->property[MFACE_ADSTYLE] = mfont_get_prop (font, Madstyle); + face->property[MFACE_SIZE] = mfont_get_prop (font, Msize); + return face; +} + +/*=*/ + +/***en + @brief Get the value of a face property. + + The mface_get_prop () function returns the value of the face + property whose key is $KEY in face $FACE. $KEY must be one of the + followings: + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg + + @return + The actual type of the returned value depends of $KEY. See + documentation of the above keys. If an error is detected, it + returns @c NULL and assigns an error code to the external variable + #merror_code. */ + +/***ja + @brief ¥Õ¥§¡¼¥¹¤Î¥×¥í¥Ñ¥Æ¥£ÃͤòÆÀ¤ë + + ´Ø¿ô mface_get_prop () ¤Ï¡¢¥Õ¥§¡¼¥¹ $FACE ¤¬»ý¤Ä¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£ + ¤ÎÆâ¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤ÎÃͤòÊÖ¤¹¡£$KEY ¤Ï²¼µ­¤Î¤¤¤º¤ì¤«¤Ç¤Ê + ¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg + + @return + ¤â¤· $KEY ¤¬ #Mfontset ¤Ê¤é¤Ð¡¢mface_get_prop () ¤Ï¥Õ¥©¥ó¥È¥»¥Ã + ¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤â¤· $KEY ¤¬ #Msize ¤Ê¤é¤ÐÀ°¿ô¤òÊÖ¤¹¡£¤½¤ì + °Ê³°¤Î¾ì¹ç¤Ï¥·¥ó¥Ü¥ë¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ + ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @seealso + mface_put_prop () + + @errors + @c MERROR_FACE */ + +void * +mface_get_prop (MFace *face, MSymbol key) +{ + int index = (int) msymbol_get (key, M_face_prop_index) - 1; + + if (index < 0) + MERROR (MERROR_FACE, NULL); + return face->property[index]; +} + +/*=*/ + +/***en + @brief Set a value of a face face property. + + The mface_put_prop () function assigns $VAL to the property whose + key is $KEY in face $FACE. $KEY must be one the followings: + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg + + Among them, font related properties (#Mfoundry through #Msize) are + used as the default values when a font in the fontset of $FACE + does not specify those values. + + The actual type of the returned value depends of $KEY. See + documentation of the above keys. + + @return + If the operation was successful, mface_put_prop returns () 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief ¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÀßÄꤹ¤ë + + ´Ø¿ô mface_put_prop () ¤Ï¡¢¥Õ¥§¡¼¥¹ $FACE Æâ¤Ç¥­¡¼¤¬$KEY ¤Ç¤¢¤ë¥× + ¥í¥Ñ¥Æ¥£¤ÎÃͤò $VAL ¤ËÀßÄꤹ¤ë¡£$KEY ¤Ï°Ê²¼¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤¯¤Æ¤Ï¤Ê + ¤é¤Ê¤¤¡£ + + #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox, + #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle, + #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg. + + ¤³¤ì¤é¤Î¤¦¤Á¤Î¡¢¥Õ¥©¥ó¥È´ØÏ¢¤Î¥×¥í¥Ñ¥Æ¥£ (#Mfamily ¤«¤é #Msize + ¤Þ¤Ç) ¤Ï¡¢¥Õ¥§¡¼¥¹¤Î¥Õ¥©¥ó¥È¥»¥Ã¥ÈÃæ¤Î¥Õ¥©¥ó¥È¤Ë´Ø¤¹¤ë¥Ç¥Õ¥©¥ë¥ÈÃÍ + ¤È¤Ê¤ë¡£ + + ¤â¤· $KEY ¤¬ #Mfontset ¤Ê¤é $VAL ¤Ï #MFontset ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç + ¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤â¤· $KEY ¤¬ #Msize ¤Ê¤é¤Ð $VAL ¤ÏÀ°¿ô¤Ç¤Ê¤±¤ì + ¤Ð¤Ê¤é¤Ê¤¤¡£$KEY ¤¬¤½¤ì°Ê³°¤Î¾ì¹ç¡¢$VAL ¤Ï¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê + ¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mface_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï + -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @seealso + mface_get_prop () + + @errors + @c MERROR_FACE */ + +int +mface_put_prop (MFace *face, MSymbol key, void *val) +{ + int index = (int) msymbol_get (key, M_face_prop_index) - 1; + + if (index < 0) + MERROR (MERROR_FACE, -1); + if (key == Mfontset) + M17N_OBJECT_REF (val); + else if (key == Mhline) + { + MFaceHLineProp *newval; + + MSTRUCT_MALLOC (newval, MERROR_FACE); + *newval = *((MFaceHLineProp *) val); + val = newval; + } + else if (key == Mbox) + { + MFaceBoxProp *newval; + + MSTRUCT_MALLOC (newval, MERROR_FACE); + *newval = *((MFaceBoxProp *) val); + val = newval; + } + face->property[index] = val; + face->tick++; + + return 0; +} + +/*=*/ + +/***en + @brief Update a face. + + The mface_update () function update $FACE on $FRAME by calling a + hook function of $FACE (if any). */ + +void +mface_update (MFrame *frame, MFace *face) +{ + MFaceHookFunc func = (MFaceHookFunc) face->property[MFACE_HOOK_FUNC]; + MPlist *rface_list; + MRealizedFace *rface; + + if (func) + { + MPLIST_DO (rface_list, frame->realized_face_list) + { + rface = MPLIST_VAL (rface_list); + if ((MFaceHookFunc) rface->face.property[MFACE_HOOK_FUNC] == func) + (func) (&(rface->face), rface->face.property[MFACE_HOOK_ARG], + rface->info); + } + } +} +/*=*/ + +/*** @} */ +/*=*/ + +/*** @addtogroup m17nDebug */ +/*** @{ */ +/*=*/ + +/***en + @brief Dump a face + + The mdebug_dump_face () function prints $FACE in a human readable + way to the stderr. $INDENT specifies how many columns to indent + the lines but the first one. + + @return + This function returns $FACE. */ + +MFace * +mdebug_dump_face (MFace *face, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MFont spec; + + memset (prefix, 32, indent); + prefix[indent] = 0; + mfont__set_spec_from_face (&spec, face); + fprintf (stderr, "(face font:\""); + mdebug_dump_font (&spec); + fprintf (stderr, "\"\n %s fore:%s back:%s", prefix, + msymbol_name ((MSymbol) face->property[MFACE_FOREGROUND]), + msymbol_name ((MSymbol) face->property[MFACE_BACKGROUND])); + if (face->property[MFACE_FONTSET]) + fprintf (stderr, " non-default-fontset"); + fprintf (stderr, " hline:%s", face->property[MFACE_HLINE] ? "yes" : "no"); + fprintf (stderr, " box:%s)", face->property[MFACE_BOX] ? "yes" : "no"); + return face; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/face.h b/src/face.h new file mode 100644 index 0000000..9ab1d16 --- /dev/null +++ b/src/face.h @@ -0,0 +1,141 @@ +/* face.h -- header file for the face module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_FACE_H_ +#define _M17N_FACE_H_ + +enum MFaceProperty + { + /** The font related properties. */ + /* The order of MFACE_FOUNDRY to MFACE_ADSTYLE must be the same as + enum MFontProperty. */ + MFACE_FOUNDRY, + MFACE_FAMILY, + MFACE_WEIGHT, + MFACE_STYLE, + MFACE_STRETCH, + MFACE_ADSTYLE, + MFACE_SIZE, + MFACE_FONTSET, + + /** The color related properties. */ + MFACE_FOREGROUND, + MFACE_BACKGROUND, + + /** The other properties. */ + MFACE_HLINE, + MFACE_BOX, + MFACE_VIDEOMODE, + + /** Extention by applications. */ + MFACE_HOOK_FUNC, + MFACE_HOOK_ARG, + + /* In a realized face, this is already reflected in MFACE_SIZE, + thus is ignored. */ + MFACE_RATIO, + + MFACE_PROPERTY_MAX + }; + +struct MFace +{ + M17NObject control; + /* Initialized to 0, and incremented by one each the face is + modified. */ + unsigned tick; + void *property[MFACE_PROPERTY_MAX]; +}; + + +enum face_gc + { + MFACE_GC_NORMAL, + MFACE_GC_INVERSE, + MFACE_GC_SCRATCH, + MFACE_GC_HLINE, + MFACE_GC_BOX_TOP, + MFACE_GC_BOX_BOTTOM, + MFACE_GC_BOX_LEFT, + MFACE_GC_BOX_RIGHT, + MFACE_GCS + }; + +/** A realized face is registered in MFrame->face_list, thus it does + not have to be a managed object. */ + +struct MRealizedFace +{ + /** Frame on which this realized face is created. */ + MFrame *frame; + + /** Properties of all stacked faces are merged into here. */ + MFace face; + + /** From what faces this is realized. Keys are Mface and values are + (MFace *). */ + MPlist *base_face_list; + + /* Initialized to the sum of ticks of the above faces. */ + unsigned tick; + + /* Realized font, one of ->realized_font_list. */ + MRealizedFont *rfont; + + /* Realized fontset, one of ->realized_fontset_list. */ + MRealizedFontset *rfontset; + + MSymbol layouter; + + MFaceHLineProp *hline; + + MFaceBoxProp *box; + + /** Realized face for ASCII chars that has the same face + properties. */ + MRealizedFace *ascii_rface; + + /** Realized face for undisplayable chars (no font found) that has + the same face properties. */ + MRealizedFace *nofont_rface; + + int ascent, descent; + int space_width; + + /** Pointer to a window system dependent object. */ + void *info; +}; + + +extern MFace *mface__default; + +extern MRealizedFace *mface__realize (MFrame *frame, MFace **faces, int num, + MSymbol language, MSymbol charset, + int limitted_size); + +extern MGlyph *mface__for_chars (MSymbol script, MSymbol language, + MSymbol charset, MGlyph *from_g, MGlyph *to_g, + int size); + +extern void mface__free_realized (MRealizedFace *rface); + +#endif /* _M17N_FACE_H_ */ diff --git a/src/font-flt.c b/src/font-flt.c new file mode 100644 index 0000000..b93c5c4 --- /dev/null +++ b/src/font-flt.c @@ -0,0 +1,1537 @@ +/* font-flt.c -- Font Layout Table sub-module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "mtext.h" +#include "symbol.h" +#include "plist.h" +#include "internal-gui.h" +#include "font.h" +#include "face.h" + +/* Font Layouter */ + +/* Font Layout Table (FLT) + +Predefined terms: SYMBOL, INTEGER, STRING + +FLT ::= '(' STAGE + ')' + +STAGE ::= CATEGORY-TABLE ? FONT-LAYOUT-RULE + +;; Each STAGE consumes a source (code sequence) and produces another +;; code sequence that is given to the next STAGE as a source. The +;; source given to the first stage is a sequence of character codes +;; that are assigned category codes by CATEGORY-TABLE. The output of +;; the last stage is a glyph code sequence given to the renderer. + +CATEGORY-TABLE ::= + '(' 'category' CATEGORY-SPEC + ')' +CATEGORY-SPEC ::= + '(' CODE [ CODE ] CATEGORY ')' +CODE ::= INTEGER +CATEGORY ::= INTEGER +;; ASCII character codes of alphabet ('A' .. 'Z' 'a' .. 'z'). +;; Ex: CATEGORY-TABLE +;; (category +;; (0x0900 0x097F ?E) ; All Devanagari characters +;; (0x093C ?N)) ; DEVANAGARI-LETTER NUKTA +;; Assign the category 'E' to all Devanagari characters but 0x093C, +;; assign the category 'N' to 0x093C. + +FONT-LAYOUT-RULE ::= + '(' 'generator' RULE MACRO-DEF * ')' + +RULE ::= COMMAND | REGEXP-RULE | MATCH-RULE | MAP-RULE + | COND-STRUCT | MACRO-NAME + +COMMAND ::= + DIRECT-CODE | COMBINING | PREDEFIND-COMMAND | OTF-COMMAND + +DIRECT-CODE ::= INTEGER +;; Always succeed. Produce the code. Consume no source. + +PREDEFIND-COMMAND ::= + '=' | '*' | '<' | '>' | '|' + +;; '=': Succeed when the current run contains at least one code. +;; Consume the first code in the current run, and produce it as is. + +;; '*': If the the previous command succeeded, repeat it until it +;; fails. + +;; '<': Produce a special code that indicates the start of grapheme +;; cluster. Succeed always, consume nothing. + +;; '>': Produce a special code that indicates the end of grapheme +;; cluster. Succeed always, consume nothing. + +;; '|': Produce a special code whose category is ' '. Succeed always, +;; consume nothing. + +OTF-COMMAND ::= + 'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]] +;; Run the Open Type Layout Table on the current run. Succeed always, +;; consume nothing. + +SCRIPT ::= OTF-TAG +;; OTF's ScriptTag name (four letters) listed at: +;; +LANGSYS ::= OTF-TAG +;; OTF's Language System name (four letters) listed at: +;; + +GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' ' +GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' ' +FEATURE ::= OTF-TAG +;; OTF's Feature name (four letters) listed at: +;; + +OTF-TAG ::= PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR PRINTABLE-CHAR + +;; Ex. OTF-COMMAND +;; 'otf:deva' +;; Run all features in the default langsys of 'deva' script. +;; 'otf:deva::nukt:haln' +;; Run all GSUB features, run 'nukt' and 'haln' GPOS features. +;; 'otf:deva:: :' +;; Run all GSUB features, run no GPOS features. + +REGEXP-RULE ::= + '(' REGEXP RULE * ')' + +;; Succeed if REGXP matches the head of source. Run RULEs while +;; limiting the source to the matching part. Consume that part. + +REGEXP ::= STRING +;; Must be composed only from ASCII characters. 'A' - 'Z', 'a' - 'z' +;; correspond to CATEGORY. + +;; Ex: REGEXP-RULE +;; ("VA?" +;; < | vowel * | >) + +MATCH-RULE ::= + '(' MATCH-IDX RULE * ')' + +;; Succeed if the previous REGEXP-RULE found a matching part for +;; MATCH-IDX. Run RULEs while limiting the source to the matching +;; part. If MATCH-IDX is zero, consume the whole part, else consume +;; nothing. + +MATCH-IDX ::= INTEGER +;; Must be 0..20. + +;; Ex. MATCH-RULE +;; (2 consonant *) + +MAP-RULE ::= + '(' ( SOURCE-SEQ | SOURCE-RANGE ) RULE * ')' + +;; Succeed if the source matches SOURCE-SEQ or SOURCE-RANGE. Run +;; RULEs while limiting the source to the matching part. Consume that +;; part. + +SOURCE-SEQ ::= + '(' CODE + ')' +SOURCE-RANGE ::= + '(' 'range' CODE CODE ')' +;; Ex. MAP-RULE +;; ((0x0915 0x094D) 0x43) +;; If the source code sequence is 0x0915 0x094D, produce 0x43. +;; ((range 0x0F40 0x0F6A) 0x2221) +;; If the first source code CODE is in the range 0x0F40..0x0F6A, +;; produce (0x2221 + (CODE - 0x0F40)). + +COND-STRUCT ::= + '(' 'cond' RULE + ')' + +;; Try each rule in sequence until one succeeds. Succeed if one +;; succeeds. Consume nothing. + +;; Ex. COND-STRUCT +;; (cond +;; ((0x0915 0x094D) 0x43) +;; ((range 0x0F40 0x0F6A) 0x2221) +;; = ) + +COMBINING ::= 'V''H''O''V''H' +V ::= ( 't' | 'c' | 'b' | 'B' ) +H ::= ( 'l' | 'c' | 'r' ) +O ::= ( '.' | XOFF | YOFF | XOFF YOFF ) +XOFF ::= '<'INTEGER | '>'INTEGER +YOFF ::= '+'INTEGER | '-'INTEGER +;; INTEGER must be integer 0..127 + +;; VH pair indicates 12 reference points of a glyph as below: +;; +;; 0----1----2 <---- ascent 0:tl (top-left) +;; | | 1:tc (top-center) +;; | | 2:tr (top-right) +;; | | 3:Bl (base-left) +;; 9 10 11 <---- center 4:Bc (base-center) +;; | | 5:Br (base-right) +;; --3----4----5-- <-- baseline 6:bl (bottom-left) +;; | | 7:bc (bottom-center) +;; 6----7----8 <---- descent 8:br (bottom-right) +;; 9:cl (center-left) +;; | | | 10:cc (center-center) +;; left center right 11:cr (center-right) +;; +;; Ex. COMBINING +;; 'tc.bc': +;; Align top-left point of the previous glyph and bottom-center +;; point of the current glyph. +;; 'Bl<20-10Br' +;; Align 20% left and 10% below of base-left point of the previous +;; glyph and base-right point of the current glyph. + +MACRO-DEF ::= + '(' MACRO-NAME RULE + ')' +MACRO-NAME ::= SYMBOL + +*/ + +static int mdebug_mask = MDEBUG_FONT_FLT; + +MSymbol Mlayouter; + +static MPlist *flt_list; + +/* Command ID: + 0 ... : direct code + -1 : invalid + -0x0F .. -2 : builtin commands + -0x100000F .. -0x10 : combining code + ... -0x1000010: index to FontLayoutStage->cmds + */ + +#define INVALID_CMD_ID -1 +#define CMD_ID_OFFSET_BUILTIN -2 +#define CMD_ID_OFFSET_COMBINING -0x10 +#define CMD_ID_OFFSET_INDEX -0x1000010 + +/* Builtin commands. */ +#define CMD_ID_COPY -2 /* '=' */ +#define CMD_ID_REPEAT -3 /* '*' */ +#define CMD_ID_CLUSTER_BEGIN -4 /* '<' */ +#define CMD_ID_CLUSTER_END -5 /* '>' */ +#define CMD_ID_SEPARATOR -6 /* '|' */ +#define CMD_ID_LEFT_PADDING -7 /* '[' */ +#define CMD_ID_RIGHT_PADDING -8 /* ']' */ + +#define CMD_ID_TO_COMBINING_CODE(id) (CMD_ID_OFFSET_COMBINING - (id)) +#define COMBINING_CODE_TO_CMD_ID(code) (CMD_ID_OFFSET_COMBINING - (code)) + +#define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id)) +#define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx)) + +static MSymbol Mcond, Mrange; + +#define GLYPH_CODE_P(code) \ + ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX) + +#define GLYPH_CODE_INDEX(code) ((code) - GLYPH_CODE_MIN) + +enum FontLayoutCmdRuleSrcType + { + SRC_REGEX, + SRC_INDEX, + SRC_SEQ, + SRC_RANGE + }; + +typedef struct +{ + enum FontLayoutCmdRuleSrcType src_type; + union { + struct { + char *pattern; + regex_t preg; + } re; + int match_idx; + struct { + int n_codes; + int *codes; + } seq; + struct { + int from, to; + } range; + } src; + + int n_cmds; + int *cmd_ids; +} FontLayoutCmdRule; + +typedef struct +{ + int n_cmds; + int *cmd_ids; +} FontLayoutCmdCond; + +typedef struct +{ + MSymbol script; + MSymbol langsys; + MSymbol gsub_features; + MSymbol gpos_features; +} FontLayoutCmdOTF; + +enum FontLayoutCmdType + { + FontLayoutCmdTypeRule, + FontLayoutCmdTypeCond, + FontLayoutCmdTypeOTF, + FontLayoutCmdTypeMAX + }; + +typedef struct +{ + enum FontLayoutCmdType type; + union { + FontLayoutCmdRule rule; + FontLayoutCmdCond cond; + FontLayoutCmdOTF otf; + } body; +} FontLayoutCmd; + +typedef struct +{ + MCharTable *category; + int size, inc, used; + FontLayoutCmd *cmds; +} FontLayoutStage; + +typedef MPlist MFontLayoutTable; /* t vs FontLayoutStage */ + +/* Font layout table loader */ + +/* Load a category table from PLIST. PLIST has this form: + PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) * +*/ + +static MCharTable * +load_category_table (MPlist *plist) +{ + MCharTable *table; + + table = mchartable (Minteger, (void *) 0); + + MPLIST_DO (plist, plist) + { + MPlist *elt; + int from, to, category_code; + + if (! MPLIST_PLIST (plist)) + MERROR (MERROR_FONT, NULL); + elt = MPLIST_PLIST (plist); + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + from = MPLIST_INTEGER (elt); + elt = MPLIST_NEXT (elt); + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + to = MPLIST_INTEGER (elt); + elt = MPLIST_NEXT (elt); + if (MPLIST_TAIL_P (elt)) + { + category_code = to; + to = from; + } + else + { + if (! MPLIST_INTEGER_P (elt)) + MERROR (MERROR_FONT, NULL); + category_code = MPLIST_INTEGER (elt); + } + if (! isalpha (category_code)) + MERROR (MERROR_FONT, NULL); + + if (from == to) + mchartable_set (table, from, (void *) category_code); + else + mchartable_set_range (table, from, to, (void *) category_code); + } + + return table; +} + + +/* Parse OTF command name NAME and store the result in CMD. + NAME has this form: + :SCRIPT[/[LANGSYS][=[GSUB-FEATURES][+GPOS-FEATURES]]] + where GSUB-FEATURES and GPOS-FEATURES have this form: + [FEATURE[,FEATURE]*] | ' ' */ + +static int +load_otf_command (FontLayoutCmd *cmd, char *name) +{ + char *p = name, *beg; + + cmd->type = FontLayoutCmdTypeOTF; + cmd->body.otf.script = cmd->body.otf.langsys = Mnil; + cmd->body.otf.gsub_features = cmd->body.otf.gpos_features = Mt; + + while (*p) + { + if (*p == ':') + { + for (beg = ++p; *p && *p != '/' && *p != '=' && *p != '+'; p++); + if (beg < p) + cmd->body.otf.script = msymbol__with_len (beg, p - beg); + } + else if (*p == '/') + { + for (beg = ++p; *p && *p != '=' && *p != '+'; p++); + if (beg < p) + cmd->body.otf.langsys = msymbol__with_len (beg, p - beg); + } + else if (*p == '=') + { + for (beg = ++p; *p && *p != '+'; p++); + if (beg < p) + cmd->body.otf.gsub_features = msymbol__with_len (beg, p - beg); + else + cmd->body.otf.gsub_features = Mnil; + } + else if (*p == '+') + { + for (beg = ++p; *p && *p != '+'; p++); + if (beg < p) + cmd->body.otf.gpos_features = msymbol__with_len (beg, p - beg); + else + cmd->body.otf.gpos_features = Mnil; + } + else + p++; + } + + return (cmd->body.otf.script == Mnil ? -1 : 0); +} + + +/* Read a decimal number from STR preceded by one of "+-><". '+' and + '>' means a plus sign, '-' and '<' means a minus sign. If the + number is greater than 127, limit it to 127. */ + +static int +read_decimal_number (char **str) +{ + char *p = *str; + int sign = (*p == '-' || *p == '<') ? -1 : 1; + int n = 0; + + p++; + while (*p >= '0' && *p <= '9') + n = n * 10 + *p++ - '0'; + *str = p; + if (n == 0) + n = 5; + return (n < 127 ? n * sign : 127 * sign); +} + + +/* Read a horizontal and vertical combining positions from STR, and + store them in the place pointed by X and Y. The horizontal + position left, center, and right are represented by 0, 1, and 2 + respectively. The vertical position top, center, bottom, and base + are represented by 0, 1, 2, and 3 respectively. If successfully + read, return 0, else return -1. */ + +static int +read_combining_position (char *str, int *x, int *y) +{ + int c = *str++; + int i; + + /* Vertical position comes first. */ + for (i = 0; i < 4; i++) + if (c == "tcbB"[i]) + { + *y = i; + break; + } + if (i == 4) + return -1; + c = *str; + /* Then comse horizontal position. */ + for (i = 0; i < 3; i++) + if (c == "lcr"[i]) + { + *x = i; + return 0; + } + return -1; +} + + +/* Return a combining code corresponding to SYM. */ + +static int +get_combining_command (MSymbol sym) +{ + char *str = msymbol_name (sym); + int base_x, base_y, add_x, add_y, off_x, off_y; + int c; + + if (read_combining_position (str, &base_x, &base_y) < 0) + return 0; + str += 2; + c = *str; + if (c == '.') + { + off_x = off_y = 128; + str++; + } + else + { + if (c == '+' || c == '-') + { + off_y = read_decimal_number (&str) + 128; + c = *str; + } + else + off_y = 128; + if (c == '<' || c == '>') + off_x = read_decimal_number (&str) + 128; + else + off_x = 128; + } + if (read_combining_position (str, &add_x, &add_y) < 0) + return 0; + + c = MAKE_COMBINING_CODE (base_y, base_x, add_y, add_x, off_y, off_x); + return (COMBINING_CODE_TO_CMD_ID (c)); +} + + +/* Load a command from PLIST into STAGE, and return that + identification number. If ID is not INVALID_CMD_ID, that means we + are loading a top level command or a macro. In that case, use ID + as the identification number of the command. Otherwise, generate a + new id number for the command. MACROS is a list of raw macros. */ + +static int +load_command (FontLayoutStage *stage, MPlist *plist, + MPlist *macros, int id) +{ + int i; + + if (MPLIST_INTEGER_P (plist)) + { + int code = MPLIST_INTEGER (plist); + + if (code < 0) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + return code; + } + else if (MPLIST_PLIST_P (plist)) + { + /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... ) + | ( ( INTEGER INTEGER ) ... ) + | ( ( range INTEGER INTEGER ) ... ) */ + MPlist *elt = MPLIST_PLIST (plist); + int len = MPLIST_LENGTH (elt) - 1; + FontLayoutCmd *cmd; + + if (id == INVALID_CMD_ID) + { + FontLayoutCmd dummy; + id = INDEX_TO_CMD_ID (stage->used); + MLIST_APPEND1 (stage, cmds, dummy, MERROR_DRAW); + } + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + + if (MPLIST_SYMBOL_P (elt)) + { + if (MPLIST_SYMBOL (elt) != Mcond) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + elt = MPLIST_NEXT (elt); + cmd->type = FontLayoutCmdTypeCond; + cmd->body.cond.n_cmds = len; + MTABLE_CALLOC (cmd->body.cond.cmd_ids, len, MERROR_DRAW); + for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt)) + { + int this_id = load_command (stage, elt, macros, INVALID_CMD_ID); + + if (this_id == INVALID_CMD_ID) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + /* The above load_command may relocate stage->cmds. */ + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + cmd->body.cond.cmd_ids[i] = this_id; + } + } + else + { + cmd->type = FontLayoutCmdTypeRule; + if (MPLIST_MTEXT_P (elt)) + { + char *str = (char *) MTEXT_DATA (MPLIST_MTEXT (elt)); + + if (regcomp (&cmd->body.rule.src.re.preg, str, REG_EXTENDED)) + MERROR (MERROR_FONT, INVALID_CMD_ID); + cmd->body.rule.src_type = SRC_REGEX; + cmd->body.rule.src.re.pattern = strdup (str); + } + else if (MPLIST_INTEGER_P (elt)) + { + cmd->body.rule.src_type = SRC_INDEX; + cmd->body.rule.src.match_idx = MPLIST_INTEGER (elt); + } + else if (MPLIST_PLIST_P (elt)) + { + MPlist *pl = MPLIST_PLIST (elt); + int size = MPLIST_LENGTH (pl); + + if (MPLIST_INTEGER_P (pl)) + { + int i; + + cmd->body.rule.src_type = SRC_SEQ; + cmd->body.rule.src.seq.n_codes = size; + MTABLE_CALLOC (cmd->body.rule.src.seq.codes, size, + MERROR_FONT); + for (i = 0; i < size; i++, pl = MPLIST_NEXT (pl)) + { + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.seq.codes[i] + = (unsigned) MPLIST_INTEGER (pl); + } + } + else if (MPLIST_SYMBOL_P (pl) && size == 3) + { + cmd->body.rule.src_type = SRC_RANGE; + pl = MPLIST_NEXT (pl); + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.range.from + = (unsigned) MPLIST_INTEGER (pl); + pl = MPLIST_NEXT (pl); + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + cmd->body.rule.src.range.to + = (unsigned) MPLIST_INTEGER (pl); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + + elt = MPLIST_NEXT (elt); + cmd->body.rule.n_cmds = len; + MTABLE_CALLOC (cmd->body.rule.cmd_ids, len, MERROR_DRAW); + for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt)) + { + int this_id = load_command (stage, elt, macros, INVALID_CMD_ID); + + if (this_id == INVALID_CMD_ID) + MERROR (MERROR_DRAW, INVALID_CMD_ID); + /* The above load_command may relocate stage->cmds. */ + cmd = stage->cmds + CMD_ID_TO_INDEX (id); + cmd->body.rule.cmd_ids[i] = this_id; + } + } + } + else if (MPLIST_SYMBOL_P (plist)) + { + MPlist *elt; + MSymbol sym = MPLIST_SYMBOL (plist); + char *name = msymbol_name (sym); + int len = strlen (name); + FontLayoutCmd cmd; + + if (len > 4 + && ! strncmp (name, "otf:", 4) + && load_otf_command (&cmd, name + 3) >= 0) + { + if (id == INVALID_CMD_ID) + { + id = INDEX_TO_CMD_ID (stage->used); + MLIST_APPEND1 (stage, cmds, cmd, MERROR_DRAW); + } + else + stage->cmds[CMD_ID_TO_INDEX (id)] = cmd; + return id; + } + + if (len == 1) + { + if (*name == '=') + return CMD_ID_COPY; + else if (*name == '*') + return CMD_ID_REPEAT; + else if (*name == '<') + return CMD_ID_CLUSTER_BEGIN; + else if (*name == '>') + return CMD_ID_CLUSTER_END; + else if (*name == '|') + return CMD_ID_SEPARATOR; + else if (*name == '[') + return CMD_ID_LEFT_PADDING; + else if (*name == ']') + return CMD_ID_RIGHT_PADDING; + else + id = 0; + } + else + { + id = get_combining_command (sym); + if (id) + return id; + } + + i = 1; + MPLIST_DO (elt, macros) + { + if (sym == MPLIST_SYMBOL (MPLIST_PLIST (elt))) + { + id = INDEX_TO_CMD_ID (i); + if (stage->cmds[i].type == FontLayoutCmdTypeMAX) + id = load_command (stage, MPLIST_NEXT (MPLIST_PLIST (elt)), + macros, id); + return id; + } + i++; + } + MERROR (MERROR_DRAW, INVALID_CMD_ID); + } + else + MERROR (MERROR_DRAW, INVALID_CMD_ID); + + return id; +} + +static void +free_flt_command (FontLayoutCmd *cmd) +{ + if (cmd->type == FontLayoutCmdTypeRule) + { + FontLayoutCmdRule *rule = &cmd->body.rule; + + if (rule->src_type == SRC_REGEX) + { + free (rule->src.re.pattern); + regfree (&rule->src.re.preg); + } + else if (rule->src_type == SRC_SEQ) + free (rule->src.seq.codes); + free (rule->cmd_ids); + } + else if (cmd->type == FontLayoutCmdTypeCond) + free (cmd->body.cond.cmd_ids); +} + +/* Load a generator from PLIST into a newly allocated FontLayoutStage, + and return it. PLIST has this form: + PLIST ::= ( COMMAND ( CMD-NAME COMMAND ) * ) +*/ + +static FontLayoutStage * +load_generator (MPlist *plist) +{ + FontLayoutStage *stage; + MPlist *elt, *pl; + FontLayoutCmd dummy; + + MSTRUCT_CALLOC (stage, MERROR_DRAW); + MLIST_INIT1 (stage, cmds, 32); + dummy.type = FontLayoutCmdTypeMAX; + MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT); + MPLIST_DO (elt, MPLIST_NEXT (plist)) + { + if (! MPLIST_PLIST_P (elt)) + MERROR (MERROR_FONT, NULL); + pl = MPLIST_PLIST (elt); + if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_FONT, NULL); + MLIST_APPEND1 (stage, cmds, dummy, MERROR_FONT); + } + + /* Load the first command from PLIST into STAGE->cmds[0]. Macros + called in the first command are also loaded from MPLIST_NEXT + (PLIST) into STAGE->cmds[n]. */ + if (load_command (stage, plist, MPLIST_NEXT (plist), INDEX_TO_CMD_ID (0)) + == INVALID_CMD_ID) + { + MLIST_FREE1 (stage, cmds); + free (stage); + MERROR (MERROR_DRAW, NULL); + } + + return stage; +} + + +/* Load FLT of name LAYOUTER_NAME from the m17n database into a newly + allocated memory, and return it. */ + +static MFontLayoutTable * +load_flt (MSymbol layouter_name) +{ + MDatabase *mdb; + MPlist *top = NULL, *plist; + MSymbol Mcategory = msymbol ("category"); + MSymbol Mgenerator = msymbol ("generator"); + MFontLayoutTable *layouter = NULL; + MCharTable *category = NULL; + + if (! (mdb = mdatabase_find (Mfont, Mlayouter, layouter_name, Mnil))) + MERROR_GOTO (MERROR_FONT, finish); + if (! (top = (MPlist *) mdatabase_load (mdb))) + MERROR_GOTO (0, finish); + if (! MPLIST_PLIST_P (top)) + MERROR_GOTO (MERROR_FONT, finish); + + MPLIST_DO (plist, top) + { + MSymbol sym; + MPlist *elt; + + if (! MPLIST_PLIST (plist)) + MERROR_GOTO (MERROR_FONT, finish); + elt = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (elt)) + MERROR_GOTO (MERROR_FONT, finish); + sym = MPLIST_SYMBOL (elt); + elt = MPLIST_NEXT (elt); + if (! elt) + MERROR_GOTO (MERROR_FONT, finish); + if (sym == Mcategory) + { + if (category) + M17N_OBJECT_UNREF (category); + category = load_category_table (elt); + } + else if (sym == Mgenerator) + { + FontLayoutStage *stage; + + if (! category) + MERROR_GOTO (MERROR_FONT, finish); + stage = load_generator (elt); + if (! stage) + MERROR_GOTO (MERROR_FONT, finish); + stage->category = category; + M17N_OBJECT_REF (category); + if (! layouter) + { + layouter = mplist (); + /* Here don't do M17N_OBJECT_REF (category) because we + don't unref the value of the element added below. */ + mplist_add (layouter, Mcategory, category); + } + mplist_add (layouter, Mt, stage); + } + else + MERROR_GOTO (MERROR_FONT, finish); + } + + if (category) + M17N_OBJECT_UNREF (category); + + finish: + M17N_OBJECT_UNREF (top); + mplist_add (flt_list, layouter_name, layouter); + return layouter; +} + + +static void +free_flt_stage (FontLayoutStage *stage) +{ + int i; + + M17N_OBJECT_UNREF (stage->category); + for (i = 0; i < stage->used; i++) + free_flt_command (stage->cmds + i); + MLIST_FREE1 (stage, cmds); + free (stage); +} + + +static MFontLayoutTable * +get_font_layout_table (MSymbol layouter_name) +{ + MPlist *plist = mplist_find_by_key (flt_list, layouter_name); + + return (plist ? MPLIST_VAL (plist) : load_flt (layouter_name)); +} + + +/* FLS (Font Layout Service) */ + +/* Structure to hold information about a context of FLS. */ + +typedef struct +{ + /* Pointer to the current stage. */ + FontLayoutStage *stage; + + /* Encode each MGlyph->code by the current category table into this + array. An element is a category. */ + char *encoded; + /* [GIDX - ] gives a category for the glyph + index GIDX. */ + int encoded_offset; + int *match_indices; + int code_offset; + int cluster_begin_idx; + int cluster_begin_pos; + int cluster_end_pos; + int combining_code; + int left_padding; +} FontLayoutContext; + +static int run_command (int depth, + int, MGlyphString *, int, int, FontLayoutContext *); + +#define NMATCH 20 + +static int +run_rule (int depth, + FontLayoutCmdRule *rule, MGlyphString *gstring, int from, int to, + FontLayoutContext *ctx) +{ + int *saved_match_indices = ctx->match_indices; + int match_indices[NMATCH * 2]; + int consumed; + int i; + int orig_from = from; + + if (ctx->cluster_begin_idx) + { + if (ctx->cluster_begin_pos > MGLYPH (from)->pos) + ctx->cluster_begin_pos = MGLYPH (from)->pos; + if (ctx->cluster_end_pos < MGLYPH (to)->pos) + ctx->cluster_end_pos = MGLYPH (to)->pos; + } + + if (rule->src_type == SRC_SEQ) + { + int len; + + len = rule->src.seq.n_codes; + if (len > (to - from)) + return 0; + for (i = 0; i < len; i++) + if (rule->src.seq.codes[i] != gstring->glyphs[from + i].code) + break; + if (i < len) + return 0; + to = from + len; + MDEBUG_PRINT1 (" (SEQ 0x%X", rule->src.seq.codes[0]); + } + else if (rule->src_type == SRC_RANGE) + { + int head; + + if (from >= to) + return 0; + head = gstring->glyphs[from].code; + if (head < rule->src.range.from || head > rule->src.range.to) + return 0; + ctx->code_offset = head - rule->src.range.from; + to = from + 1; + MDEBUG_PRINT2 (" (RANGE 0x%X-0x%X", + rule->src.range.from, rule->src.range.to); + } + else if (rule->src_type == SRC_REGEX) + { + regmatch_t pmatch[NMATCH]; + char saved_code; + int result; + + if (from > to) + return 0; + saved_code = ctx->encoded[to - ctx->encoded_offset]; + ctx->encoded[to - ctx->encoded_offset] = '\0'; + result = regexec (&(rule->src.re.preg), + ctx->encoded + from - ctx->encoded_offset, + NMATCH, pmatch, 0); + if (result == 0 && pmatch[0].rm_so == 0) + { + MDEBUG_PRINT3 (" (REGEX \"%s\" \"%s\" %d", + rule->src.re.pattern, + ctx->encoded + from - ctx->encoded_offset, + pmatch[0].rm_eo); + ctx->encoded[to - ctx->encoded_offset] = saved_code; + for (i = 0; i < NMATCH; i++) + { + if (pmatch[i].rm_so < 0) + match_indices[i * 2] = match_indices[i * 2 + 1] = -1; + else + { + match_indices[i * 2] = from + pmatch[i].rm_so; + match_indices[i * 2 + 1] = from + pmatch[i].rm_eo; + } + } + ctx->match_indices = match_indices; + to = match_indices[1]; + } + else + { + ctx->encoded[to - ctx->encoded_offset] = saved_code; + return 0; + } + } + else if (rule->src_type == SRC_INDEX) + { + if (rule->src.match_idx >= NMATCH) + return 0; + from = ctx->match_indices[rule->src.match_idx * 2]; + if (from < 0) + return 0; + to = ctx->match_indices[rule->src.match_idx * 2 + 1]; + MDEBUG_PRINT1 (" (INDEX %d", rule->src.match_idx); + } + + consumed = 0; + depth++; + for (i = 0; i < rule->n_cmds; i++) + { + int pos; + + if (rule->cmd_ids[i] == CMD_ID_REPEAT) + { + if (! consumed) + continue; + i--; + } + pos = run_command (depth, rule->cmd_ids[i], gstring, from, to, ctx); + if (pos < 0) + MERROR (MERROR_DRAW, -1); + consumed = pos > from; + if (consumed) + from = pos; + } + + ctx->match_indices = saved_match_indices; + MDEBUG_PRINT (")"); + return (rule->src_type == SRC_INDEX ? orig_from : to); +} + +static int +run_cond (int depth, + FontLayoutCmdCond *cond, MGlyphString *gstring, int from, int to, + FontLayoutContext *ctx) +{ + int i, pos = 0; + + MDEBUG_PRINT2 ("\n [FLT] %*s(COND", depth, ""); + depth++; + for (i = 0; i < cond->n_cmds; i++) + if ((pos = run_command (depth, cond->cmd_ids[i], gstring, from, to, ctx)) + != 0) + break; + if (pos < 0) + MERROR (MERROR_DRAW, -1); + MDEBUG_PRINT (")"); + return (pos); +} + +static int +run_otf (int depth, + FontLayoutCmdOTF *otf_cmd, MGlyphString *gstring, int from, int to, + FontLayoutContext *ctx) +{ +#ifdef HAVE_OTF + int gidx = gstring->used; + + to = mfont__ft_drive_otf (gstring, from, to, + otf_cmd->script, otf_cmd->langsys, + otf_cmd->gsub_features, otf_cmd->gpos_features); + if (gidx < gstring->used) + MGLYPH (gidx)->left_padding = ctx->left_padding; +#endif + return to; +} + +static int +run_command (int depth, int id, MGlyphString *gstring, int from, int to, + FontLayoutContext *ctx) +{ + MGlyph g; + + if (id >= 0) + { + int i; + + /* Direct code (== id + ctx->code_offset) output. + The source is not consumed. */ + if (from < to) + g = *(MGLYPH (from)); + else + g = *(MGLYPH (from - 1)); + g.type = GLYPH_CHAR; + g.code = ctx->code_offset + id; + MDEBUG_PRINT1 (" (DIRECT 0x%X", g.code); + if (ctx->combining_code) + g.combining_code = ctx->combining_code; + if (ctx->left_padding) + g.left_padding = ctx->left_padding; + for (i = from; i < to; i++) + { + MGlyph *tmp = MGLYPH (i); + + if (g.pos > tmp->pos) + g.pos = tmp->pos; + else if (g.to < tmp->to) + g.to = tmp->to; + } + APPEND_GLYPH (gstring, g); + ctx->code_offset = ctx->combining_code = ctx->left_padding = 0; + MDEBUG_PRINT (")"); + return (from); + } + + if (id <= CMD_ID_OFFSET_INDEX) + { + int idx = CMD_ID_TO_INDEX (id); + FontLayoutCmd *cmd; + + if (idx >= ctx->stage->used) + MERROR (MERROR_DRAW, -1); + cmd = ctx->stage->cmds + idx; + if (cmd->type == FontLayoutCmdTypeRule) + to = run_rule (depth, &cmd->body.rule, gstring, from, to, ctx); + else if (cmd->type == FontLayoutCmdTypeCond) + to = run_cond (depth, &cmd->body.cond, gstring, from, to, ctx); + else if (cmd->type == FontLayoutCmdTypeOTF) + to = run_otf (depth, &cmd->body.otf, gstring, from, to, ctx); + + if (to < 0) + return -1; + return to; + } + + if (id <= CMD_ID_OFFSET_COMBINING) + { + ctx->combining_code = CMD_ID_TO_COMBINING_CODE (id); + return from; + } + + switch (id) + { + case CMD_ID_COPY: + { + if (from >= to) + return from; + g = *(MGLYPH (from)); + if (ctx->combining_code) + g.combining_code = ctx->combining_code; + if (ctx->left_padding) + g.left_padding = ctx->left_padding; + APPEND_GLYPH (gstring, g); + ctx->code_offset = ctx->combining_code = ctx->left_padding = 0; + return (from + 1); + } + + case CMD_ID_CLUSTER_BEGIN: + if (! ctx->cluster_begin_idx) + { + MDEBUG_PRINT1 (" <%d", MGLYPH (from)->pos); + ctx->cluster_begin_idx = gstring->used; + ctx->cluster_begin_pos = MGLYPH (from)->pos; + ctx->cluster_end_pos = MGLYPH (from)->to; + } + return from; + + case CMD_ID_CLUSTER_END: + if (ctx->cluster_begin_idx && ctx->cluster_begin_idx < gstring->used) + { + int i; + + MDEBUG_PRINT1 (" %d>", ctx->cluster_end_pos); + for (i = ctx->cluster_begin_idx; i < gstring->used; i++) + { + MGLYPH (i)->pos = ctx->cluster_begin_pos; + MGLYPH (i)->to = ctx->cluster_end_pos; + } + ctx->cluster_begin_idx = 0; + } + return from; + + case CMD_ID_SEPARATOR: + { + if (from < to) + g = *(MGLYPH (from)); + else + g = *(MGLYPH (from - 1)); + g.type = GLYPH_PAD; + /* g.c = g.code = 0; */ + g.width = 0; + APPEND_GLYPH (gstring, g); + return from; + } + + case CMD_ID_LEFT_PADDING: + ctx->left_padding = 1; + return from; + + case CMD_ID_RIGHT_PADDING: + if (gstring->used > 0) + gstring->glyphs[gstring->used - 1].right_padding = 1; + return from; + } + + MERROR (MERROR_DRAW, -1); +} + + +/* Internal API */ + +int +mfont__flt_init (void) +{ + Mcond = msymbol ("cond"); + Mrange = msymbol ("range"); + Mlayouter = msymbol ("layouter"); + flt_list = mplist (); + return 0; +} + +void +mfont__flt_fini (void) +{ + MPlist *plist, *pl; + + MPLIST_DO (plist, flt_list) + { + pl = MPLIST_PLIST (plist); + if (pl) + { + MPLIST_DO (pl, MPLIST_NEXT (pl)) + free_flt_stage (MPLIST_VAL (pl)); + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + } + M17N_OBJECT_UNREF (flt_list); +} + +unsigned +mfont__flt_encode_char (MSymbol layouter_name, int c) +{ + MFontLayoutTable *layouter = get_font_layout_table (layouter_name); + MCharTable *table; + unsigned code; + + if (! layouter) + return MCHAR_INVALID_CODE; + table = MPLIST_VAL (layouter); + code = (unsigned) mchartable_lookup (table, c); + return (code ? code : MCHAR_INVALID_CODE); +} + +int +mfont__flt_run (MGlyphString *gstring, int from, int to, + MSymbol layouter_name, MRealizedFace *ascii_rface) +{ + int stage_idx = 0; + int gidx; + int i; + FontLayoutContext ctx; + MCharTable *table; + int encoded_len; + int match_indices[NMATCH]; + MFontLayoutTable *layouter = get_font_layout_table (layouter_name); + FontLayoutStage *stage; + int from_pos, to_pos; + MGlyph dummy; + + if (! layouter) + { + /* FLT not found. Make all glyphs invisible. */ + while (from < to) + gstring->glyphs[from++].code = MCHAR_INVALID_CODE; + return to; + } + + dummy = gstring->glyphs[from]; + MDEBUG_PRINT1 (" [FLT] (%s", msymbol_name (layouter_name)); + + /* Setup CTX. */ + memset (&ctx, 0, sizeof ctx); + table = MPLIST_VAL (layouter); + layouter = MPLIST_NEXT (layouter); + stage = (FontLayoutStage *) MPLIST_VAL (layouter); + gidx = from; + /* Find previous glyphs that are also supported by the layouter. */ + while (gidx > 1 + && mchartable_lookup (table, MGLYPH (gidx - 1)->c)) + gidx--; + /* + 2 is for a separator ' ' and a terminator '\0'. */ + encoded_len = gstring->used - gidx + 2; + ctx.encoded = (char *) alloca (encoded_len); + + for (i = 0; gidx < from; i++, gidx++) + ctx.encoded[i] = (int) mchartable_lookup (table, MGLYPH (gidx)->c); + + ctx.encoded[i++] = ' '; + ctx.encoded_offset = from - i; + + /* Now each MGlyph->code contains encoded char. Set it in + ctx.encoded[], and set MGlyph->c to MGlyph->code. */ + for (gidx = from; gidx < to ; i++, gidx++) + { + ctx.encoded[i] = (int) MGLYPH (gidx)->code; + MGLYPH (gidx)->code = (unsigned) MGLYPH (gidx)->c; + } + ctx.encoded[i++] = '\0'; + + match_indices[0] = from; + match_indices[1] = to; + for (i = 2; i < NMATCH; i++) + match_indices[i] = -1; + ctx.match_indices = match_indices; + + from_pos = MGLYPH (from)->pos; + to_pos = MGLYPH (to)->pos; + + for (stage_idx = 0; 1; stage_idx++) + { + int len = to - from; + int result; + + MDEBUG_PRINT1 ("\n [FLT] (STAGE %d", stage_idx); + gidx = gstring->used; + ctx.stage = stage; + + result = run_command (2, INDEX_TO_CMD_ID (0), gstring, + ctx.encoded_offset, to, &ctx); + MDEBUG_PRINT (")"); + if (result < 0) + return -1; + to = from + (gstring->used - gidx); + REPLACE_GLYPHS (gstring, gidx, from, len); + + layouter = MPLIST_NEXT (layouter); + /* If this is the last stage, break the loop. */ + if (MPLIST_TAIL_P (layouter)) + break; + + /* Otherwise, prepare for the next iteration. */ + stage = (FontLayoutStage *) MPLIST_VAL (layouter); + table = stage->category; + if (to - from >= encoded_len) + { + encoded_len = to + 1; + ctx.encoded = (char *) alloca (encoded_len); + } + + for (i = from; i < to; i++) + { + MGlyph *g = MGLYPH (i); + + if (g->type == GLYPH_PAD) + ctx.encoded[i - from] = ' '; + else if (! g->otf_encoded) + ctx.encoded[i - from] = (int) mchartable_lookup (table, g->code); +#ifdef HAVE_FREETYPE + else + { + int c = mfont__ft_decode_otf (g); + + if (c >= 0) + { + c = (int) mchartable_lookup (table, c); + if (! c) + c = -1; + } + ctx.encoded[i - from] = (c >= 0 ? c : 1); + } +#endif /* HAVE_FREETYPE */ + } + ctx.encoded[i - from] = '\0'; + ctx.encoded_offset = from; + ctx.match_indices[0] = from; + ctx.match_indices[1] = to; + } + + MDEBUG_PRINT (")\n"); + + if (from == to) + { + /* Somehow there's no glyph contributing to characters between + FROM_POS and TO_POS. We must add one dummy space glyph for + those characters. */ + MGlyph g; + + g.type = GLYPH_SPACE; + g.c = ' ', g.code = ' '; + g.pos = from_pos, g.to = to_pos; + g.rface = ascii_rface; + INSERT_GLYPH (gstring, from, g); + to = from + 1; + } + else + { + /* Here we must check if all characters in the range is covered + by some glyph(s). If not, change and of glyphs to + cover uncovered characters. */ + int len = to_pos - from_pos; + int pos; + MGlyph **glyphs = alloca (sizeof (MGlyph) * len); + MGlyph *g, *gend = MGLYPH (to); + MGlyph *latest = gend; + + for (i = 0; i < len; i++) + glyphs[i] = NULL; + for (g = MGLYPH (from); g != gend; g++) + { + if (g->pos < latest->pos) + latest = g; + if (! glyphs[g->pos - from_pos]) + { + for (i = g->pos; i < g->to; i++) + glyphs[i - from_pos] = g; + } + } + i = 0; + if (! glyphs[0]) + { + pos = latest->pos; + for (g = latest; g->pos == pos; g++) + g->pos = from_pos; + i++; + } + for (; i < len; i++) + { + if (! glyphs[i]) + { + for (g = latest; g->pos == latest->pos; g++) + g->to = from_pos + i + 1; + } + else + latest = glyphs[i]; + } + } + return to; +} + + +/* for debugging... */ + +static void +dump_flt_cmd (FontLayoutStage *stage, int id, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + + memset (prefix, 32, indent); + prefix[indent] = 0; + + if (id >= 0) + fprintf (stderr, "0x%02X", id); + else if (id <= CMD_ID_OFFSET_INDEX) + { + int idx = CMD_ID_TO_INDEX (id); + FontLayoutCmd *cmd = stage->cmds + idx; + + if (cmd->type == FontLayoutCmdTypeRule) + { + FontLayoutCmdRule *rule = &cmd->body.rule; + int i; + + fprintf (stderr, "(rule "); + if (rule->src_type == SRC_REGEX) + fprintf (stderr, "\"%s\"", rule->src.re.pattern); + else if (rule->src_type == SRC_INDEX) + fprintf (stderr, "%d", rule->src.match_idx); + else if (rule->src_type == SRC_SEQ) + fprintf (stderr, "(seq)"); + else if (rule->src_type == SRC_RANGE) + fprintf (stderr, "(range)"); + else + fprintf (stderr, "(invalid src)"); + + for (i = 0; i < rule->n_cmds; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2); + } + fprintf (stderr, ")"); + } + else if (cmd->type == FontLayoutCmdTypeCond) + { + FontLayoutCmdCond *cond = &cmd->body.cond; + int i; + + fprintf (stderr, "(cond"); + for (i = 0; i < cond->n_cmds; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2); + } + fprintf (stderr, ")"); + } + else if (cmd->type == FontLayoutCmdTypeOTF) + { + fprintf (stderr, "(otf)"); + } + else + fprintf (stderr, "(error-command)"); + } + else if (id <= CMD_ID_OFFSET_COMBINING) + fprintf (stderr, "cominging-code"); + else + fprintf (stderr, "(predefiend %d)", id); +} + +void +dump_flt (MFontLayoutTable *flt, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MPlist *plist; + int stage_idx = 0; + + memset (prefix, 32, indent); + prefix[indent] = 0; + fprintf (stderr, "(flt"); + MPLIST_DO (plist, flt) + { + FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist); + int i; + + fprintf (stderr, "\n%s (stage %d", prefix, stage_idx); + for (i = 0; i < stage->used; i++) + { + fprintf (stderr, "\n%s ", prefix); + dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4); + } + fprintf (stderr, ")"); + stage_idx++; + } + fprintf (stderr, ")"); +} diff --git a/src/font-ft.c b/src/font-ft.c new file mode 100644 index 0000000..98a54ea --- /dev/null +++ b/src/font-ft.c @@ -0,0 +1,760 @@ +/* font-ft.c -- FreeType interface sub-module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "plist.h" +#include "internal-gui.h" +#include "font.h" +#include "face.h" + +#ifdef HAVE_FREETYPE +#include +#include FT_FREETYPE_H + +#ifdef HAVE_OTF +#include +#endif /* HAVE_OTF */ + +static FT_Library ft_library; + +typedef struct +{ + MSymbol ft_style; + MSymbol weight, style, stretch; +} MFTtoProp; + +static int ft_to_prop_size; +static MFTtoProp *ft_to_prop; + +typedef struct +{ + M17NObject control; + MFont font; + char *filename; + MPlist *charmap_list; + FT_Face ft_face; + int otf_flag; /* This font 1: is OTF, 0: may be OTF, -1: is not OTF. */ +#ifdef HAVE_OTF + OTF *otf; +#endif /* HAVE_OTF */ +} MFTInfo; + +/* List of FreeType fonts. Keys are family names, values are (MFTInfo + *) where MFTInfo->ft_face and MFTInfo->otf are NULL. */ +static MPlist *ft_font_list; + +/** Return 1 iff the filename in DIRENTRY matches FreeType font file. + We select only TrueType/OpenType/Type1 fonts. + Used as the arg SELECT of scandir () in list_ft_in_dir (). */ + +static int +check_filename (const char *name) +{ + int len = strlen (name); + const char *ext = name + (len - 4); + + if (len < 5) + return -1; + if (! memcmp (ext, ".ttf", 4) + || ! memcmp (ext, ".TTF", 4) + || ! memcmp (ext, ".otf", 4) + || ! memcmp (ext, ".OTF", 4)) + return 1; + if (! memcmp (ext, ".PFA", 4) + || ! memcmp (ext, ".pfa", 4) + || ! memcmp (ext, ".PFB", 4) + || ! memcmp (ext, ".pfb", 4)) + return 0; + return -1; +} + +static MSymbol +ft_set_property (MFont *font, char *family_name, char *style_name) +{ + MSymbol family; + MSymbol style; + int len; + char *buf, *p; + + MFONT_INIT (font); + font->property[MFONT_TYPE] = MFONT_TYPE_FT + 1; + mfont__set_property (font, MFONT_ADSTYLE, msymbol ("")); + + len = strlen (family_name) + 1; + buf = (char *) alloca (len); + memcpy (buf, family_name, len); + for (p = buf; *p; p++) + if (*p >= 'A' && *p <= 'Z') + *p += 'a' - 'A'; + family = msymbol (buf); + mfont__set_property (font, MFONT_FAMILY, family); + + if (style_name) + { + len = strlen (style_name) + 1; + buf = (char *) alloca (len); + memcpy (buf, style_name, len); + for (p = buf; *p; p++) + if (*p >= 'A' && *p <= 'Z') + *p += 'a' - 'A'; + style = msymbol (buf); + } + else + style = Mnil; + + if (style != Mnil) + { + int i; + + for (i = 0; i < ft_to_prop_size; i++) + if (ft_to_prop[i].ft_style == style) + { + mfont__set_property (font, MFONT_WEIGHT, ft_to_prop[i].weight); + mfont__set_property (font, MFONT_STYLE, ft_to_prop[i].style); + mfont__set_property (font, MFONT_STRETCH, ft_to_prop[i].stretch); + return family; + } + } + mfont__set_property (font, MFONT_WEIGHT, msymbol ("medium")); + mfont__set_property (font, MFONT_STYLE, msymbol ("r")); + mfont__set_property (font, MFONT_STRETCH, msymbol ("normal")); + return family; +} + +static void +add_font_list (char *filename, int otf_flag) +{ + FT_Face ft_face; + MFTInfo *ft_info; + MSymbol family; + char registry_buf[16]; + int i; + + if (FT_New_Face (ft_library, filename, 0, &ft_face)) + return; + + if (ft_face->family_name && ((char *) ft_face->family_name)[0]) + { + int unicode_charmap_bmp = -1, unicode_charmap_full = -1; + + MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT); + family = ft_set_property (&ft_info->font, + (char *) ft_face->family_name, + (char *) ft_face->style_name); + ft_info->filename = strdup (filename); + ft_info->charmap_list = mplist (); + mplist_add (ft_info->charmap_list, Mt, (void *) -1); + for (i = 0; i < ft_face->num_charmaps; i++) + { + sprintf (registry_buf, "%d-%d", + ft_face->charmaps[i]->platform_id, + ft_face->charmaps[i]->encoding_id); + mplist_add (ft_info->charmap_list, msymbol (registry_buf), + (void *) i); + if (ft_face->charmaps[i]->platform_id == 0) + { + if (ft_face->charmaps[i]->encoding_id == 3) + unicode_charmap_bmp = i; + else if (ft_face->charmaps[i]->encoding_id == 4) + unicode_charmap_full = i; + } + else if (ft_face->charmaps[i]->platform_id == 3) + { + if (ft_face->charmaps[i]->encoding_id == 1) + unicode_charmap_bmp = i; + else if (ft_face->charmaps[i]->encoding_id == 10) + unicode_charmap_full = i; + } + else if (ft_face->charmaps[i]->platform_id == 1 + && ft_face->charmaps[i]->encoding_id == 0) + mplist_add (ft_info->charmap_list, msymbol ("apple-roman"), + (void *) i); + } + if (unicode_charmap_bmp >= 0) + mplist_add (ft_info->charmap_list, msymbol ("unicode-bmp"), + (void *) unicode_charmap_bmp); + if (unicode_charmap_full >= 0) + mplist_add (ft_info->charmap_list, msymbol ("unicode-full"), + (void *) unicode_charmap_full); + mplist_add (ft_font_list, family, ft_info); + } + FT_Done_Face (ft_face); +} + +static void +build_font_list () +{ + MPlist *plist; + struct stat buf; + char *pathname; + + ft_font_list = mplist (); + + MPLIST_DO (plist, mfont_freetype_path) + if (MPLIST_STRING_P (plist) + && (pathname = MPLIST_STRING (plist)) + && stat (pathname, &buf) == 0) + { + if (S_ISREG (buf.st_mode)) + { + int result = check_filename (pathname); + + add_font_list (pathname, result > 0 ? 0 : -1); + } + else if (S_ISDIR (buf.st_mode)) + { + int len = strlen (pathname); + char path[PATH_MAX]; + DIR *dir = opendir (pathname); + struct dirent *dp; + int result; + + if (dir) + { + strcpy (path, pathname); + strcpy (path + len, "/"); + len++; + while ((dp = readdir (dir)) != NULL) + if ((result = check_filename (dp->d_name)) >= 0) + { + strcpy (path + len, dp->d_name); + add_font_list (path, result > 0 ? 0 : -1); + } + closedir (dir); + } + } + } +} + +static MRealizedFont *ft_select (MFrame *, MFont *, MFont *, int); +static int ft_open (MRealizedFont *); +static void ft_close (MRealizedFont *); +static void ft_find_metric (MRealizedFont *, MGlyph *); +static unsigned ft_encode_char (MRealizedFont *, int, unsigned); +static void ft_render (MDrawWindow, int, int, MGlyphString *, + MGlyph *, MGlyph *, int, MDrawRegion); + +MFontDriver ft_driver = + { ft_select, ft_open, ft_close, + ft_find_metric, ft_encode_char, ft_render }; + +/* The FreeType font driver function LIST. */ + +static MRealizedFont * +ft_select (MFrame *frame, MFont *spec, MFont *request, int limited_size) +{ + MPlist *plist; + MFTInfo *best_font; + int best_score; + MRealizedFont *rfont; + MSymbol family, registry; + + if (! ft_font_list) + build_font_list (); + best_font = NULL; + best_score = 0; + family = FONT_PROPERTY (spec, MFONT_FAMILY); + registry = FONT_PROPERTY (spec, MFONT_REGISTRY); + if (registry == Mnil) + registry = Mt; + + MPLIST_DO (plist, ft_font_list) + { + MFTInfo *ft_info; + int score; + + if (family) + { + plist = mplist_find_by_key (plist, family); + if (! plist) + break; + } + ft_info = (MFTInfo *) MPLIST_VAL (plist); + if (! mplist_find_by_key (ft_info->charmap_list, registry)) + continue; + + /* We always ignore FOUNDRY. */ + ft_info->font.property[MFONT_FOUNDRY] = spec->property[MFONT_FOUNDRY]; + score = mfont__score (&ft_info->font, spec, request, limited_size); + if (score >= 0 + && (! best_font + || best_score > score)) + { + best_font = ft_info; + best_score = score; + if (score == 0) + break; + } + } + if (! best_font) + return NULL; + + MSTRUCT_CALLOC (rfont, MERROR_FONT_FT); + rfont->frame = frame; + rfont->spec = *spec; + rfont->request = *request; + rfont->font = best_font->font; + rfont->font.property[MFONT_SIZE] = request->property[MFONT_SIZE]; + rfont->font.property[MFONT_REGISTRY] = spec->property[MFONT_REGISTRY]; + rfont->score = best_score; + rfont->info = best_font; + rfont->driver = &ft_driver; + return rfont; +} + +static void +close_ft (void *object) +{ + MFTInfo *ft_info = (MFTInfo *) object; + + if (ft_info->ft_face) + FT_Done_Face (ft_info->ft_face); +#ifdef HAVE_OTF + if (ft_info->otf) + OTF_close (ft_info->otf); +#endif /* HAVE_OTF */ + free (object); +} + +/* The FreeType font driver function OPEN. */ + +static int +ft_open (MRealizedFont *rfont) +{ + MFTInfo *ft_info; + MSymbol registry = FONT_PROPERTY (&rfont->font, MFONT_REGISTRY); + int i; + int mdebug_mask = MDEBUG_FONT; + + M17N_OBJECT (ft_info, close_ft, MERROR_FONT_FT); + ft_info->font = ((MFTInfo *) rfont->info)->font; + ft_info->otf_flag = ((MFTInfo *) rfont->info)->otf_flag; + ft_info->filename = ((MFTInfo *) rfont->info)->filename; + ft_info->charmap_list = ((MFTInfo *) rfont->info)->charmap_list; + rfont->info = ft_info; + + rfont->status = -1; + if (FT_New_Face (ft_library, ft_info->filename, 0, &ft_info->ft_face)) + goto err; + if (registry == Mnil) + registry = Mt; + i = (int) mplist_get (((MFTInfo *) rfont->info)->charmap_list, registry); + if (i >= 0 + && FT_Set_Charmap (ft_info->ft_face, ft_info->ft_face->charmaps[i])) + goto err; + if (FT_Set_Pixel_Sizes (ft_info->ft_face, 0, + rfont->font.property[MFONT_SIZE] / 10)) + goto err; + + MDEBUG_PRINT1 (" [FT-FONT] o %s\n", ft_info->filename); + rfont->status = 1; + rfont->ascent = ft_info->ft_face->ascender >> 6; + rfont->descent = ft_info->ft_face->descender >> 6; + return 0; + + err: + MDEBUG_PRINT1 (" [FT-FONT] x %s\n", ft_info->filename); + return -1; +} + +/* The FreeType font driver function CLOSE. */ + +static void +ft_close (MRealizedFont *rfont) +{ + M17N_OBJECT_UNREF (rfont->info); +} + +/* The FreeType font driver function FIND_METRIC. */ + +static void +ft_find_metric (MRealizedFont *rfont, MGlyph *g) +{ + MFTInfo *ft_info = (MFTInfo *) rfont->info; + FT_Face ft_face = ft_info->ft_face; + + if (g->code == MCHAR_INVALID_CODE) + { + unsigned unitsPerEm = ft_face->units_per_EM; + int size = rfont->font.property[MFONT_SIZE] / 10; + + g->lbearing = 0; + g->rbearing = ft_face->max_advance_width * size / unitsPerEm; + g->width = ft_face->max_advance_width * size / unitsPerEm; + g->ascent = ft_face->ascender * size / unitsPerEm; + g->descent = (- ft_face->descender) * size / unitsPerEm; + } + else + { + FT_Glyph_Metrics *metrics; + FT_UInt code; + + if (g->otf_encoded) + code = g->code; + else + code = FT_Get_Char_Index (ft_face, (FT_ULong) g->code); + + FT_Load_Glyph (ft_face, code, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); + metrics = &ft_face->glyph->metrics; + g->lbearing = metrics->horiBearingX >> 6; + g->rbearing = (metrics->horiBearingX + metrics->width) >> 6; + g->width = metrics->horiAdvance >> 6; + g->ascent = metrics->horiBearingY >> 6; + g->descent = (metrics->height - metrics->horiBearingY) >> 6; + } +} + +/* The FreeType font driver function ENCODE_CHAR. */ + +static unsigned +ft_encode_char (MRealizedFont *rfont, int c, unsigned ignored) +{ + MFTInfo *ft_info; + FT_UInt code; + + if (rfont->status == 0) + { + if (ft_open (rfont) < 0) + return -1; + } + ft_info = (MFTInfo *) rfont->info; + code = FT_Get_Char_Index (ft_info->ft_face, (FT_ULong) c); + if (! code) + return MCHAR_INVALID_CODE; +#if 0 + FT_Load_Glyph (ft_info->ft_face, code, FT_LOAD_NO_SCALE); + return (ft_info->ft_face->glyph->metrics.width > 0 + ? (unsigned) c : MCHAR_INVALID_CODE); +#endif + return ((unsigned) c); + +} + +/* The FreeType font driver function RENDER. */ + +static void +ft_render (MDrawWindow win, int x, int y, + MGlyphString *gstring, MGlyph *from, MGlyph *to, + int reverse, MDrawRegion region) +{ + MRealizedFace *rface; + MFrame *frame; + MFTInfo *ft_info; + FT_Face ft_face = NULL; + MGlyph *g; + + if (from == to) + return; + + /* It is assured that the all glyphs in the current range use the + same realized face. */ + rface = from->rface; + frame = rface->frame; + ft_info = (MFTInfo *) rface->rfont->info; + ft_face = ft_info->ft_face; + + g = from; + while (g < to) + { + if (g->type == GLYPH_CHAR) + { + FT_UInt code; + + if (g->otf_encoded) + code = g->code; + else + code = FT_Get_Char_Index (ft_face, (FT_ULong) g->code); +#ifdef FT_LOAD_TARGET_MONO + FT_Load_Glyph (ft_face, code, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO); +#else + FT_Render_Glyph (ft_face->glyph, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); +#endif + mwin__draw_bitmap (frame, win, rface, reverse, + x + ft_face->glyph->bitmap_left + g->xoff, + y - ft_face->glyph->bitmap_top + g->yoff, + ft_face->glyph->bitmap.width, + ft_face->glyph->bitmap.rows, + ft_face->glyph->bitmap.pitch, + ft_face->glyph->bitmap.buffer, + region); + } + x += g++->width; + } + + return; +} + + + +int +mfont__ft_init () +{ + struct { + char *ft_style; + char *weight, *style, *stretch; + } ft_to_prop_name[] = + { { "regular", "medium", "r", "normal" }, + { "italic", "medium", "i", "normal" }, + { "bold", "bold", "r", "normal" }, + { "bold italic", "bold", "i", "normal" }, + { "narrow", "medium", "r", "condensed" }, + { "narrow italic", "medium", "i", "condensed" }, + { "narrow bold", "bold", "r", "condensed" }, + { "narrow bold italic", "bold", "i", "condensed" }, + { "black", "black", "r", "normal" }, + { "black italic", "black", "i", "normal" } }; + int i; + + if (FT_Init_FreeType (&ft_library) != 0) + MERROR (MERROR_FONT_FT, -1); + + ft_to_prop_size = sizeof (ft_to_prop_name) / sizeof (ft_to_prop_name[0]); + MTABLE_MALLOC (ft_to_prop, ft_to_prop_size, MERROR_FONT_FT); + for (i = 0; i < ft_to_prop_size; i++) + { + ft_to_prop[i].ft_style = msymbol (ft_to_prop_name[i].ft_style); + ft_to_prop[i].weight = msymbol (ft_to_prop_name[i].weight); + ft_to_prop[i].style = msymbol (ft_to_prop_name[i].style); + ft_to_prop[i].stretch = msymbol (ft_to_prop_name[i].stretch); + } + + mfont__driver_list[MFONT_TYPE_FT] = &ft_driver; + + return 0; +} + +void +mfont__ft_fini () +{ + MPlist *plist; + + if (ft_font_list) + { + MPLIST_DO (plist, ft_font_list) + { + MFTInfo *ft_info = (MFTInfo *) MPLIST_VAL (plist); + free (ft_info->filename); + M17N_OBJECT_UNREF (ft_info->charmap_list); + free (ft_info); + } + M17N_OBJECT_UNREF (ft_font_list); + ft_font_list = NULL; + } + free (ft_to_prop); + FT_Done_FreeType (ft_library); +} + + +int +mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, + MSymbol script, MSymbol langsys, + MSymbol gsub_features, MSymbol gpos_features) +{ + int len = to - from; + MGlyph g; + int i; +#ifdef HAVE_OTF + MFTInfo *ft_info; + OTF *otf; + OTF_GlyphString otf_gstring; + OTF_Glyph *otfg; + char *script_name, *language_name; + char *gsub_feature_names, *gpos_feature_names; + int from_pos, to_pos; + int unitsPerEm; + + if (len == 0) + return from; + + ft_info = (MFTInfo *) gstring->glyphs[from].rface->rfont->info; + if (ft_info->otf_flag < 0) + goto simple_copy; + otf = ft_info->otf; + if (! otf && (otf = OTF_open (ft_info->filename))) + { + if (OTF_get_table (otf, "head") < 0 + || (OTF_check_table (otf, "GSUB") < 0 + && OTF_check_table (otf, "GPOS") < 0)) + { + OTF_close (otf); + ft_info->otf_flag = -1; + ft_info->otf = NULL; + goto simple_copy; + } + ft_info->otf = otf; + } + + script_name = msymbol_name (script); + language_name = langsys != Mnil ? msymbol_name (langsys) : NULL; + gsub_feature_names + = (gsub_features == Mt ? "*" + : gsub_features == Mnil ? NULL + : msymbol_name (gsub_features)); + gpos_feature_names + = (gpos_features == Mt ? "*" + : gpos_features == Mnil ? NULL + : msymbol_name (gpos_features)); + + g = gstring->glyphs[from]; + from_pos = g.pos; + to_pos = g.to; + for (i = from + 1; i < to; i++) + { + if (from_pos > gstring->glyphs[i].pos) + from_pos = gstring->glyphs[i].pos; + if (to_pos < gstring->glyphs[i].to) + to_pos = gstring->glyphs[i].to; + } + + unitsPerEm = otf->head->unitsPerEm; + otf_gstring.size = otf_gstring.used = len; + otf_gstring.glyphs = (OTF_Glyph *) alloca (sizeof (OTF_Glyph) * len); + memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len); + for (i = 0; i < len; i++) + { + if (gstring->glyphs[from + i].otf_encoded) + { + otf_gstring.glyphs[i].c = gstring->glyphs[from + i].c; + otf_gstring.glyphs[i].glyph_id = gstring->glyphs[from + i].code; + } + else + { + otf_gstring.glyphs[i].c = gstring->glyphs[from + i].code; + } + } + + if (OTF_drive_tables (otf, &otf_gstring, script_name, language_name, + gsub_feature_names, gpos_feature_names) < 0) + goto simple_copy; + g.pos = from_pos; + g.to = to_pos; + for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++) + { + g.combining_code = 0; + g.c = otfg->c; + if (otfg->glyph_id) + { + g.code = otfg->glyph_id; + switch (otfg->positioning_type) + { + case 1: case 2: + { + int off_x = 128, off_y = 128; + + if (otfg->f.f1.format & OTF_XPlacement) + off_x = ((double) (otfg->f.f1.value->XPlacement) + * 100 / unitsPerEm + 128); + if (otfg->f.f1.format & OTF_YPlacement) + off_y = ((double) (otfg->f.f1.value->YPlacement) + * 100 / unitsPerEm + 128); + g.combining_code + = MAKE_COMBINING_CODE (3, 2, 3, 0, off_y, off_x); + if ((otfg->f.f1.format & OTF_XAdvance) + || (otfg->f.f1.format & OTF_YAdvance)) + off_y--; + } + break; + case 3: + /* Not yet supported. */ + break; + case 4: + { + int off_x, off_y; + + off_x = ((double) (otfg->f.f4.base_anchor->XCoordinate + - otfg->f.f4.mark_anchor->XCoordinate) + * 100 / unitsPerEm + 128); + off_y = ((double) (otfg->f.f4.base_anchor->YCoordinate + - otfg->f.f4.mark_anchor->YCoordinate) + * 100 / unitsPerEm + 128); + g.combining_code + = MAKE_COMBINING_CODE (3, 0, 3, 0, off_y, off_x); + } + break; + case 5: + /* Not yet supported. */ + break; + default: /* i.e case 6 */ + /* Not yet supported. */ + break; + } + g.otf_encoded = 1; + } + else + { + g.code = otfg->c; + g.otf_encoded = 0; + } + MLIST_APPEND1 (gstring, glyphs, g, MERROR_FONT_OTF); + } + return to; + + simple_copy: +#endif /* HAVE_OTF */ + for (i = 0; i < len; i++) + { + g = gstring->glyphs[from + i]; + MLIST_APPEND1 (gstring, glyphs, g, MERROR_FONT_OTF); + } + return to; +} + +int +mfont__ft_decode_otf (MGlyph *g) +{ +#ifdef HAVE_OTF + MFTInfo *ft_info = (MFTInfo *) g->rface->rfont->info; + int c = OTF_get_unicode (ft_info->otf, (OTF_GlyphID) g->code); + + return (c ? c : -1); +#else /* not HAVE_OTF */ + return -1; +#endif /* not HAVE_OTF */ +} + +#else /* not HAVE_FREETYPE */ + +int +mfont__ft_init () +{ + return 0; +} + +void +mfont__ft_fini () +{ +} + +#endif /* HAVE_FREETYPE */ diff --git a/src/font.c b/src/font.c new file mode 100644 index 0000000..645f8e8 --- /dev/null +++ b/src/font.c @@ -0,0 +1,1646 @@ +/* font.c -- font module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nFont + @brief Font object + + The m17n GUI API represents a font by an object of the type @c + MFont. A font can have @e font @e properties. Like other types + of properties, a font property consists of a key and a value. The + key of a font property must be one of the following symbols: + + @c Mfoundry, @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Mregistry, @c Msize, @c Mresolution. + + When the key of a font property is @c Msize or @c Mresolution, its + value is an integer. Otherwise the value is a symbol. "The font + property that belongs to font F and whose key is @c Mxxx" may be + shortened to "the xxx property of F". + + The value of a foundry property is a symbol representing font + foundry information, e.g. adobe, misc, etc. + + The value of a family property is a symbol representing font family + information, e.g. times, helvetica, etc. + + The value of a weight property is a symbol representing weight + information, e.g. normal, bold, etc. + + The value of a style property is a symbol representing slant + information, e.g. normal, italic, etc. + + The value of a stretch property is a symbol representing width + information, e.g. normal, semicondensed, etc. + + The value of an adstyle property is a symbol representing abstract + font family information, e.g. serif, sans-serif, etc. + + The value of a registry property is a symbol representing registry + information, e.g. iso10646-1, iso8895-1, etc. + + The value of a size property is an integer representing design + size in the unit of 1/10 point. + + The value of a resolution property is an integer representing + assumed device resolution in the unit of dots per inch (dpi) + + The m17n library uses font objects for two purposes: to receive + font specification from an application program, and to present + available fonts to an application program. When the m17n library + presents an available font to an application program, all font + properties have a concrete value. + + The m17n library supports three kinds of fonts: Window system fonts, + FreeType fonts, and OpenType fonts. + +
    + +
  • Window system fonts + + The m17n-X library supports all fonts handled by an X server and + an X font server. The correspondence between XLFD fields and font + properties are shown below. + +@verbatim + XLFD field property + --------------- -------- + FOUNDRY foundry + FAMILY_NAME family + WEIGHT_NAME weight + SLANT style + SETWIDTH_NAME stretch + ADD_STYLE_NAME adstyle + POINT_SIZE size + RESOLUTION_Y resolution + CHARSET_REGISTRY-CHARSET_ENCODING registry +@endverbatim + + XLFD fields not listed in the above table are ignored. + +
  • FreeType fonts + + The m17n library, if configured to use the FreeType library, + supports all fonts that can be handled by the FreeType library. + The variable #mfont_freetype_path is initialized properly accoding + to the configuration of the m17n librayr and the environment + variable @c M17NDIR. See the documentation of the variable for + details. + + The family name of a FreeType font corresponds to the family + property. Style names of FreeType fonts correspond to the weight, + style, and stretch properties as below. + +@verbatim + style name weight style stretch + ---------- ------ ----- ------- + Regular medium r normal + Italic medium i normal + Bold bold r normal + Bold Italic bold i normal + Narrow medium r condensed + Narrow Italic medium i condensed + Narrow Bold bold r condensed + Narrow Bold Italic bold i condensed + Black black r normal + Black Italic black i normal +@endverbatim + + Style names not listed in the above table are treated as + "Regular". + + Combination of a platform ID and an encoding ID corresponds to the + registry property. For example, if a font has the combination (1 + 1), the registry property is 1-1. Some frequent combinations have + a predefined registry property as below. + +@verbatim + platform ID encoding ID registry property + ----------- ----------- ----------------- + 0 3 unicode-bmp + 0 4 unicode-full + 1 0 apple-roman + 3 1 unicode-bmp + 3 1 unicode-full +@endverbatim + + Thus, a font that has two combinations (1 0) and (3 1) corresponds + to four font objects whose registries are 1-0, apple-roman, 3-1, + and unicode-bmp. + +
  • OpenType fonts + + The m17n library, if configured to use both the FreeType library + and the OTF library, supports any OpenType fonts. The list of + actually available fonts is created in the same way as in the case + of FreeType fonts. If a fontset instructs to use an OpenType font + via an FLT (Font Layout Table), and the FLT has an OTF-related + command (e.g. otf:deva), the OTF library converts a character + sequence to a glyph code sequence according to the OpenType layout + tables of the font, and the FreeType library gives a bitmap image + for each glyph. + +
+ + */ + +/***ja + @addtogroup m17nFont + @brief ¥Õ¥©¥ó¥È¤È¤Ï¡¢¸Ä¡¹¤Î¥·¥¹¥Æ¥à¥Õ¥©¥ó¥È¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ë + + m17n-win API ¤Ë¤ª¤±¤ë @e ¥Õ¥©¥ó¥È ¤È¤Ï¡¢@c MFont ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È + ¤Ç¤¢¤ê¡¢¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à°Í¸¤Î @e ¥·¥¹¥Æ¥à¥Õ¥©¥ó¥È ¤È°ìÂаì¤ËÂÐ + ±þÉÕ¤±¤é¤ì¤ë¡£¥Õ¥©¥ó¥È¤Ï¸ÇÄê¸Ä¿ô¤Î @e ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£ ¤ò»ý¤Ä¡£ + ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£¤Ï¥­¡¼¤ÈÃͤ«¤é¤Ê¤ë¡£¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢ + + @c Mfoundry, @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Mregistry, @c Msize, @c Mresolution + + ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£¥­¡¼¤¬ @c Msize ¤¢¤ë¤¤¤Ï @c Mresolution ¤Î¾ì¹ç¡¢ + ÃͤÏÀ°¿ô¤Ç¤¢¤ë¡£¥­¡¼¤¬¤½¤ì°Ê³°¤Î¾ì¹ç¡¢Ãͤϥ·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£¡Ö¥Õ¥©¥ó + ¥È F ¤Î¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¥­¡¼¤¬ @c Mxxx ¤Ç¤¢¤ë¤â¤Î¡×¤Î¤³¤È¤ò + ´Êñ¤Ë¡ÖF ¤Î xxx ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ + + Family ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢times, helvetica Åù¤Î¥Õ¥©¥ó¥È¥Õ¥¡¥ß¥ê¡¼¤ò + ɽ¤ï¤¹¡£ + + Weight ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢normal, bold Åù¤ÎÂÀ¤µ¤Ë´Ø¤¹¤ë¾ðÊó¤òɽ¤ï¤¹¡£ + + Style ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢normal, italic Åù¤Î·¹¤­¤Ë´Ø¤¹¤ë¾ðÊó¤òɽ¤ï + ¤¹¡£ + + Stretch ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢normal, semicondensed Åù¤Îʸ»úÉý¤Ë´Ø¤¹¤ë + ¾ðÊó¤òɽ¤ï¤¹¡£ + + Adstyle ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢serif, sans-serif Åù¤ÎÃê¾ÝŪ¤Ê¥Õ¥©¥ó¥È + ¥Õ¥¡¥ß¥ê¡¼¤òɽ¤ï¤¹¡£ + + Registry ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢iso10646, iso8895-1 ¤Î¥ì¥¸¥¹¥È¥ê¤òɽ¤ï + ¤¹¡£ + + Size ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¥Õ¥©¥ó¥È¤Î¥Ç¥¶¥¤¥ó¥µ¥¤¥º¤òɽ¤ï¤¹¡£Ã±°Ì + ¤Ï 1/10 ¥Ý¥¤¥ó¥È¤Ç¤¢¤ë¡£ + + Resolution ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢ÁÛÄꤵ¤ì¤Æ¤¤¤ë¥Ç¥Ð¥¤¥¹¤Î²òÁüÅÙ¤ò ɽ¤ï + ¤¹¡£Ã±°Ì¤Ï dots per inch (dpi) ¤Ç¤¢¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include "config.h" + +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "mtext.h" +#include "symbol.h" +#include "plist.h" +#include "charset.h" +#include "internal-gui.h" +#include "font.h" +#include "face.h" + +MFontDriver *mfont__driver_list[MFONT_TYPE_MAX]; + +/** Indices to font properties sorted by their priority. */ +static int font_score_priority[] = + { MFONT_SIZE, + MFONT_ADSTYLE, + MFONT_FAMILY, + MFONT_WEIGHT, + MFONT_STYLE, + MFONT_STRETCH, + MFONT_FOUNDRY + }; + +#define FONT_SCORE_PRIORITY_SIZE \ + (sizeof font_score_priority / sizeof font_score_priority[0]) + +/* Indexed by a font property MFONT_XXX, and the value is how many + bits to shift the difference of property values. */ +static int font_score_shift_bits[MFONT_PROPERTY_MAX]; + +/** Predefined symbols for each font property. The order is important + because the function score_font () decides how well a font matches + with a spec by checking how close the index is. */ + +static char *common_foundry[] = + { "misc", + "adobe" }; +static char *common_family[] = + { "fixed", + "courier", + "helvetica", + "times" }; +static char *common_weight[] = + { "ultralight", + "extralight", + "light", + "demilight", + "book", + "normal", + "medium", + "regular", + "demibold", + "bold", + "extrabold", + "ultrabold", + "black" }; +static char *common_style[] = + { "o", + "i", + "r", + "ri", + "ro" }; +static char *common_stretch[] = + { "condensed", + "narrow", + "semicondensed", + "normal", + "semiexpanded", + "expanded" }; +static char *common_adstyle[] = + { "serif", + "", + "sans" }; +static char *common_registry[] = + { "iso8859-1" }; + +/* Table containing all the data above. */ + +struct MFontCommonNames +{ + int num; + char **names; +}; + +static struct MFontCommonNames font_common_names[] = + { + { sizeof (common_foundry) / sizeof (char *), common_foundry}, + { sizeof (common_family) / sizeof (char *), common_family}, + { sizeof (common_weight) / sizeof (char *), common_weight}, + { sizeof (common_style) / sizeof (char *), common_style}, + { sizeof (common_stretch) / sizeof (char *), common_stretch}, + { sizeof (common_adstyle) / sizeof (char *), common_adstyle}, + { sizeof (common_registry) / sizeof (char *), common_registry} + }; + + +/** Table of available font property names. */ + +MFontPropertyTable mfont__property_table[MFONT_REGISTRY + 1]; + + +/** Return the numeric value of SYMBOL as the Nth font property. */ + +#define FONT_PROPERTY_NUMERIC(symbol, n) \ + ((symbol) == Mnil \ + ? 0 \ + : ((int) msymbol_get ((symbol), mfont__property_table[(n)].property))) + + +/** Set the numeric value of SYMBOL as the Nth font property to NUMERIC. */ + +#define SET_FONT_PROPERTY_NUMERIC(symbol, n, numeric) \ + msymbol_put((symbol), mfont__property_table[(n)].property, \ + (void *) (numeric)) + +static char * +gen_font_name (char *buf, MFont *font) +{ + char size[16]; + int i; + + buf[0] = '\0'; + for (i = 0; i <= MFONT_REGISTRY; i++) + if (FONT_PROPERTY (font, i) != Mnil) + { + char *name = msymbol_name (FONT_PROPERTY (font, i)); + + if (name[0]) + { + if (i > 0) + strcat (buf, ","); + strcat (buf, name); + } + } + sprintf (size, ",%d", font->property[MFONT_SIZE] / 10); + strcat (buf, size); + return buf; +} + + + +/* Font selector. */ + +struct MFontEncoding { + MFont spec; + MSymbol encoding_name; + MCharset *encoding_charset; + MSymbol repertory_name; + MCharset *repertory_charset; +}; + +static MPlist *font_encoding_list; +static MFontEncoding default_encoding; + +/** Load font encoding table from the data . + The data has this form: + (FONT-SPEC ENCODING) ... + where FONT-SPEC has this form: + ([FOUNDRY FAMILY [WEIGHT [STYLE [STRETCH [ADSTYLE]]]]] REGISTRY) + All elements are symbols. */ + +static int +load_font_encoding_table () +{ + MDatabase *mdb; + MPlist *encoding_list, *plist, *pl, *elt; + + font_encoding_list = pl = mplist (); + + mdb = mdatabase_find (Mfont, msymbol ("encoding"), Mnil, Mnil); + if (! mdb + || ! (encoding_list = (MPlist *) mdatabase_load (mdb))) + MERROR (MERROR_FONT, -1); + + MPLIST_DO (plist, encoding_list) + { + MFontEncoding *encoding; + MSymbol registry; + + MSTRUCT_CALLOC (encoding, MERROR_FONT); + + if (! MPLIST_PLIST_P (plist) + || (elt = MPLIST_PLIST (plist), mplist_length (elt) < 2) + || ! MPLIST_PLIST_P (elt)) + MWARNING (MERROR_FONT); + registry = mfont__set_spec_from_plist (&encoding->spec, + MPLIST_PLIST (elt)); + elt = MPLIST_NEXT (elt); + if (! MPLIST_SYMBOL_P (elt)) + MWARNING (MERROR_FONT); + encoding->encoding_name = MPLIST_SYMBOL (elt); + elt = MPLIST_NEXT (elt); + if (MPLIST_TAIL_P (elt)) + encoding->repertory_name = encoding->encoding_name; + else if (! MPLIST_SYMBOL_P (elt)) + MWARNING (MERROR_FONT); + else + encoding->repertory_name = MPLIST_SYMBOL (elt); + + if (registry == Mnil) + registry = Mt; + pl = mplist_add (pl, registry, encoding); + continue; + + warning: + free (encoding); + } + + M17N_OBJECT_UNREF (encoding_list); + return 0; +} + +typedef struct { + MFont spec; + int resize; +} MFontResize; + +static MPlist *font_resize_list; + +/** Load font size table from the data . + The data has this form: + (FONT-SPEC RESIZE-FACTOR) ... + where FONT-SPEC has this form: + ([FOUNDRY FAMILY [WEIGHT [STYLE [STRETCH [ADSTYLE]]]]] REGISTRY) + All elements of FONT-SPEC are symbols. */ + +static int +load_font_resize_table () +{ + MDatabase *mdb; + MPlist *size_adjust_list, *plist, *pl, *elt; + + font_resize_list = pl = mplist (); + + mdb = mdatabase_find (Mfont, msymbol ("resize"), Mnil, Mnil); + if (! mdb) + return -1; + if (! (size_adjust_list = (MPlist *) mdatabase_load (mdb))) + MERROR (MERROR_FONT, -1); + + MPLIST_DO (plist, size_adjust_list) + { + MFontResize *resize; + MSymbol registry; + + MSTRUCT_CALLOC (resize, MERROR_FONT); + + if (! MPLIST_PLIST_P (plist) + || (elt = MPLIST_PLIST (plist), mplist_length (elt) != 2) + || ! MPLIST_PLIST_P (elt)) + MWARNING (MERROR_FONT); + registry = mfont__set_spec_from_plist (&resize->spec, + MPLIST_PLIST (elt)); + elt = MPLIST_NEXT (elt); + if (! MPLIST_INTEGER_P (elt)) + MWARNING (MERROR_FONT); + resize->resize = MPLIST_INTEGER (elt); + + if (registry == Mnil) + registry = Mt; + pl = mplist_add (pl, registry, resize); + continue; + + warning: + free (resize); + } + + M17N_OBJECT_UNREF (size_adjust_list); + return 0; +} + +/** Return a font encoding (and repertory) of FONT. */ + +static MFontEncoding * +find_encoding (MFont *font) +{ + MSymbol registry = FONT_PROPERTY (font, MFONT_REGISTRY); + MFontEncoding *encoding = NULL; + MPlist *plist; + + if (! font_encoding_list) + load_font_encoding_table (); + if (! MPLIST_TAIL_P (font_encoding_list)) + while (1) + { + plist = font_encoding_list; + while (registry ? (plist = mplist_find_by_key (plist, registry)) + : plist) + { + encoding = (MFontEncoding *) MPLIST_VAL (plist); + if (mfont__match_p (font, &encoding->spec, MFONT_ADSTYLE)) + { + if (! encoding->encoding_charset) + encoding->encoding_charset + = MCHARSET (encoding->encoding_name); + if (! encoding->encoding_charset) + { + mplist_pop (plist); + continue; + } + if (encoding->repertory_name == encoding->encoding_name) + encoding->repertory_charset = encoding->encoding_charset; + else if (encoding->repertory_name != Mnil) + { + encoding->repertory_charset + = MCHARSET (encoding->repertory_name); + if (! encoding->repertory_charset) + { + mplist_pop (plist); + continue; + } + } + return encoding; + } + else + plist = MPLIST_NEXT (plist); + } + if (registry == Mnil || registry == Mt) + break; + registry = Mt; + } + return &default_encoding; +} + + + +/* Internal API */ + +int +mfont__init () +{ + int i, shift; + + Mfoundry = msymbol ("foundry"); + mfont__property_table[MFONT_FOUNDRY].property = Mfoundry; + Mfamily = msymbol ("family"); + mfont__property_table[MFONT_FAMILY].property = Mfamily; + Mweight = msymbol ("weight"); + mfont__property_table[MFONT_WEIGHT].property = Mweight; + Mstyle = msymbol ("style"); + mfont__property_table[MFONT_STYLE].property = Mstyle; + Mstretch = msymbol ("stretch"); + mfont__property_table[MFONT_STRETCH].property = Mstretch; + Madstyle = msymbol ("adstyle"); + mfont__property_table[MFONT_ADSTYLE].property = Madstyle; + Mregistry = msymbol ("registry"); + mfont__property_table[MFONT_REGISTRY].property = Mregistry; + + Msize = msymbol ("size"); + Mresolution = msymbol ("resolution"); + + /* The first entry of each mfont__property_table must be Mnil so + that actual properties get positive numeric numbers. */ + for (i = 0; i <= MFONT_REGISTRY; i++) + { + MLIST_INIT1 (&mfont__property_table[i], names, 8); + MLIST_APPEND1 (&mfont__property_table[i], names, Mnil, MERROR_FONT); + } + + /* Register predefined font property names. */ + for (i = 0; i <= MFONT_REGISTRY; i++) + { + int j; + + for (j = 0; j < font_common_names[i].num; j++) + { + MSymbol sym = msymbol (font_common_names[i].names[j]); + + if (sym == Mnil) + return -1; + if (msymbol_put(sym, mfont__property_table[i].property, + (void *) (j + 1)) < 0) + return -1; + MLIST_APPEND1 (&mfont__property_table[i], names, sym, MERROR_FONT); + } + } + + memset (mfont__driver_list, 0, sizeof mfont__driver_list); + + /* Here, SHIFT starts from 1, not 0. This is because the lowest bit + of a score is a flag for a scalable font (see the documentation + of mfont_score). */ + i = FONT_SCORE_PRIORITY_SIZE - 1; + for (shift = 1; i >= 0; i--) + { + font_score_shift_bits[font_score_priority[i]] = shift; + if (font_score_priority[i] == MFONT_SIZE) + shift += 16; + else + shift += 2; + } + + MFONT_INIT (&default_encoding.spec); + default_encoding.encoding_name = Mnil; + default_encoding.encoding_charset = NULL; + default_encoding.repertory_name = Mnil; + default_encoding.repertory_charset = NULL; + { + char *path, *buf; + int bufsize; + + mfont_freetype_path = mplist (); + bufsize = strlen (M17NDIR) + 7; + buf = alloca (bufsize); + sprintf (buf, "%s/fonts", M17NDIR); + mplist_add (mfont_freetype_path, Mstring, strdup (buf)); + path = getenv ("M17NDIR"); + if (path) + { + i = strlen (path) + 7; + if (i > bufsize) + buf = alloca (i); + sprintf (buf, "%s/fonts", path); + mplist_push (mfont_freetype_path, Mstring, strdup (buf)); + } + } + +#ifdef HAVE_FREETYPE + if (mfont__ft_init () < 0) + return -1; +#endif /* HAVE_FREETYPE */ + if (mfont__flt_init () < 0) + return -1; + + return 0; +} + +void +mfont__fini () +{ + MPlist *plist; + int i; + + mfont__flt_fini (); +#ifdef HAVE_FREETYPE + mfont__ft_fini (); +#endif /* HAVE_FREETYPE */ + + MPLIST_DO (plist, mfont_freetype_path) + free (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (mfont_freetype_path); + + if (font_resize_list) + { + MPLIST_DO (plist, font_resize_list) + free (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (font_resize_list); + font_resize_list = NULL; + } + if (font_encoding_list) + { + MPLIST_DO (plist, font_encoding_list) + free (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (font_encoding_list); + font_encoding_list = NULL; + } + for (i = 0; i <= MFONT_REGISTRY; i++) + MLIST_FREE1 (&mfont__property_table[i], names); +} + +void +mfont__free_realized (MRealizedFont *rfont) +{ + if (rfont->info) + M17N_OBJECT_UNREF (rfont->info); + free (rfont); +} + + +/* Compare FONT with REQUEST and return how much they differs. If + FONT does not match with SPEC, return -1. */ + +int +mfont__score (MFont *font, MFont *spec, MFont *request, int limited_size) +{ + int score = 0; + int i = FONT_SCORE_PRIORITY_SIZE; + + while (--i >= 0) + { + enum MFontProperty prop = font_score_priority[i]; + + if (request->property[prop] != 0) + { + int val = 0; + + if (spec->property[prop] && font->property[prop] + && font->property[prop] != spec->property[prop]) + return -1; + if (font->property[prop]) + val = abs (font->property[prop] - request->property[prop]); + if (prop == MFONT_SIZE) + { + if (font->property[MFONT_RESY] == 0) + /* This is a scalable font. We prefer a bitmap font + if the size matches exactly. */ + score |= 1; + else + score |= (val << font_score_shift_bits[MFONT_SIZE] + | ((limited_size && val > 0) ? 0x400000 : 0)); + } + else + score |= (val > 3 ? 3 : val) << font_score_shift_bits[prop]; + } + } + return score; +} + + +/** Return 1 iff FONT matches SPEC. */ + +int +mfont__match_p (MFont *font, MFont *spec, int prop) +{ + for (; prop >= 0; prop--) + if (spec->property[prop] && font->property[prop] + && font->property[prop] != spec->property[prop]) + return 0; + return 1; +} + + +void +mfont__set_spec_from_face (MFont *spec, MFace *face) +{ + int i; + + for (i = 0; i <= MFONT_ADSTYLE; i++) + mfont__set_property (spec, i, face->property[i]); + /* The value 1 is "iso8859-1". */ + spec->property[MFONT_REGISTRY] = 1; + spec->property[MFONT_SIZE] = (int) (face->property[MFACE_SIZE]); + spec->property[MFONT_RESY] = 0; + spec->property[MFONT_TYPE] = 0; +} + + +extern MSymbol +mfont__set_spec_from_plist (MFont *spec, MPlist *plist) +{ + int i; + MSymbol spec_list[MFONT_REGISTRY + 1]; + MSymbol registry; + + MFONT_INIT (spec); + memset (spec_list, 0, sizeof spec_list); + for (i = 0; ! MPLIST_TAIL_P (plist); i++, plist = MPLIST_NEXT (plist)) + { + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_FONT, Mnil); + spec_list[i] = MPLIST_SYMBOL (plist); + } + registry = spec_list[i - 1]; + mfont__set_property (spec, MFONT_REGISTRY, registry); + for (i -= 2; i >= 0; i--) + mfont__set_property (spec, i, spec_list[i]); + return registry; +} + +MRealizedFont * +mfont__select (MFrame *frame, MFont *spec, MFont *request, int limited_size) +{ + MSymbol registry = FONT_PROPERTY (spec, MFONT_REGISTRY); + MPlist *realized_font_list; + MRealizedFont *best_font[MFONT_TYPE_MAX]; + int best_index; + int i; + int mdebug_mask = MDEBUG_FONT; + + if (registry == Mnil) + registry = Mt; + + MPLIST_DO (realized_font_list, frame->realized_font_list) + { + MRealizedFont *best = MPLIST_VAL (realized_font_list); + + if (MPLIST_KEY (realized_font_list) == registry + && ! memcmp (&best->spec, spec, sizeof (MFont)) + && ! memcmp (&best->request, request, sizeof (MFont))) + return best; + } + + MDEBUG_PUSH_TIME (); + best_index = -1; + for (i = 0; i < MFONT_TYPE_MAX; i++) + { + MFontDriver *driver = mfont__driver_list[i]; + + best_font[i] = (driver + ? (driver->select) (frame, spec, request, limited_size) + : NULL); + if (best_font[i] + && (best_index < 0 + || best_font[best_index]->score > best_font[i]->score)) + best_index = i; + } + for (i = 0; i < MFONT_TYPE_MAX; i++) + { + if (i == best_index) + mplist_add (frame->realized_font_list, registry, best_font[i]); + else if (best_font[i]) + free (best_font[i]); + } + + if (mdebug__flag & mdebug_mask) + { + char buf1[256], buf2[256]; + MFont font = *spec; + + for (i = 0; i < MFONT_PROPERTY_MAX; i++) + if (! font.property[i]) + font.property[i] = request->property[i]; + gen_font_name (buf2, &font); + + if (best_index >= 0) + MDEBUG_PRINT_TIME ("FONT", + (stderr, " to select <%s> from <%s>.", + gen_font_name (buf1, + &best_font[best_index]->font), + buf2)); + else + MDEBUG_PRINT_TIME ("FONT", (stderr, " to fail to find <%s>.", buf2)); + MDEBUG_POP_TIME (); + } + + return (best_index >= 0 ? best_font[best_index] : NULL); +} + + +/** Open a font specified in RFONT. Return 0 if successfully + opened, otherwise return -1. */ + +int +mfont__open (MRealizedFont *rfont) +{ + MPlist *realized_font_list; + MSymbol registry = FONT_PROPERTY (&rfont->font, MFONT_REGISTRY); + + if (rfont->status) + mdebug_hook (); + + MPLIST_DO (realized_font_list, rfont->frame->realized_font_list) + { + MRealizedFont *this_rfont = MPLIST_VAL (realized_font_list); + + if (this_rfont->status != 0 + && MPLIST_KEY (realized_font_list) == registry + && ! memcmp (&this_rfont->font, &rfont->font, sizeof (MFont))) + { + if (rfont->info) + M17N_OBJECT_UNREF (rfont->info); + rfont->info = this_rfont->info; + M17N_OBJECT_REF (this_rfont->info); + rfont->status = this_rfont->status; + return (this_rfont->status > 0 ? 0 : -1); + } + } + + return (rfont->driver->open) (rfont); +} + +void +mfont__close (MRealizedFont *rfont) +{ + (rfont->driver->close) (rfont); +} + +void +mfont__resize (MFont *spec, MFont *request) +{ + MSymbol registry = FONT_PROPERTY (spec, MFONT_REGISTRY); + MFontResize *resize; + MPlist *plist; + + if (! font_resize_list) + load_font_resize_table (); + if (! MPLIST_TAIL_P (font_resize_list)) + while (1) + { + plist = font_resize_list; + while (registry ? (plist = mplist_find_by_key (plist, registry)) + : plist) + { + resize = (MFontResize *) MPLIST_VAL (plist); + if (mfont__match_p (spec, &resize->spec, MFONT_ADSTYLE)) + { + request->property[MFONT_SIZE] + = request->property[MFONT_SIZE] * resize->resize / 100; + return; + } + plist = MPLIST_NEXT (plist); + } + if (registry == Mt) + break; + registry = Mt; + } +} + +/* Return 1 if C is encodable, 0, if C is not encodable, -1 if it + can't be decided now. */ + +int +mfont__encodable_p (MRealizedFont *rfont, MSymbol layouter_name, int c) +{ + MFontEncoding *encoding; + + if (layouter_name != Mnil) + return (mfont__flt_encode_char (layouter_name, c) + != MCHAR_INVALID_CODE); + if (! rfont->encoding) + rfont->encoding = find_encoding (&rfont->spec); + encoding = rfont->encoding; + if (! encoding->repertory_charset) + return -1; + return (ENCODE_CHAR (encoding->repertory_charset, c) != MCHAR_INVALID_CODE); +} + +unsigned +mfont__encode_char (MRealizedFont *rfont, int c) +{ + MFontEncoding *encoding; + unsigned code; + + if (rfont->layouter != Mnil) + return mfont__flt_encode_char (rfont->layouter, c); + if (! rfont->encoding) + rfont->encoding = find_encoding (&rfont->font); + encoding = rfont->encoding; + if (! encoding->encoding_charset) + return MCHAR_INVALID_CODE; + code = ENCODE_CHAR (encoding->encoding_charset, c); + if (code == MCHAR_INVALID_CODE) + return MCHAR_INVALID_CODE; + if (! encoding->repertory_charset) + return (rfont->driver->encode_char) (rfont, c, code); + if (ENCODE_CHAR (encoding->repertory_charset, c) == MCHAR_INVALID_CODE) + return MCHAR_INVALID_CODE; + return code; +} + +void +mfont__get_metric (MRealizedFont *rfont, MGlyph *g) +{ + (rfont->driver->find_metric) (rfont, g); +} + + +void +mfont__set_property (MFont *font, enum MFontProperty key, MSymbol val) +{ + int numeric; + + if (val == Mnil) + numeric = 0; + else + { + numeric = FONT_PROPERTY_NUMERIC (val, key); + if (! numeric) + { + numeric = mfont__property_table[key].used; + MLIST_APPEND1 (mfont__property_table + key, names, val, MERROR_FONT); + SET_FONT_PROPERTY_NUMERIC (val, key, numeric); + } + } + font->property[key] = numeric; +} + +void +mfont__set_spec (MFont *font, MSymbol *attrs, + unsigned short size, unsigned short resy) +{ + int i; + + for (i = 0; i <= MFONT_REGISTRY; i++) + mfont__set_property (font, i, attrs[i]); + font->property[MFONT_SIZE] = size; + font->property[MFONT_RESY] = resy; +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + + +/* External API */ + +/*** @addtogroup m17nFont */ +/*** @{ */ +/*=*/ + +/***en @name Variables: Keys of font property. */ +/***ja @name ÊÑ¿ô: ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë */ +/*** @{ */ +/*=*/ + +/***en + @brief Key of font property specifying foundry. + + The variable #Mfoundry is a symbol of name "foundry" and + is used as a key of font property and face property. The property + value must be a symbol whose name is a foundry name of a font. */ + +MSymbol Mfoundry; + +/***en + @brief Key of font property specifying foundry. + + The variable #Mfamily is a symbol of name "family" and is + used as a key of font property and face property. The property + value must be a symbol whose name is a family name of a font. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î family ¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mfamily ¤Ï "family" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó + ¥È¤Î family ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú + ¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î family ¤ò»ØÄê + ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */ + +MSymbol Mfamily; + +/***en + @brief Key of font property specifying weight. + + The variable #Mweight is a symbol of name "weight" and is + used as a key of font property and face property. The property + value must be a symbol whose name is a weight name of a font (e.g + "medium", "bold"). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î weight ¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mweight ¤Ï "weight" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó + ¥È¤Î weight ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú + ¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î weight ¤ò»ØÄê + ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */ + +MSymbol Mweight; + +/***en + @brief Key of font property specifying style. + + The variable #Mstyle is a symbol of name "style" and is + used as a key of font property and face property. The property + value must be a symbol whose name is a style name of a font (e.g + "r", "i", "o"). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î style ¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mstyle ¤Ï "style" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó¥È + ¤Î style ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú¿ô¤È + ¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î style ¤ò»ØÄê + ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */ + +MSymbol Mstyle; + +/***en + @brief Key of font property specifying stretch. + + The variable #Mstretch is a symbol of name "stretch" and + is used as a key of font property and face property. The property + value must be a symbol whose name is a stretch name of a font (e.g + "normal", "condensed"). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î stretch ¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mstretch ¤Ï "stretch" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥© + ¥ó¥È¤Î stretch ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î + °ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î stretch ¤ò»Ø + Äꤹ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */ + +MSymbol Mstretch; + +/***en + @brief Key of font property specifying additional style. + + The variable #Madstyle is a symbol of name "adstyle" and + is used as a key of font property and face property. The property + value must be a symbol whose name is an additional style name of a + font (e.g "serif", "", "sans"). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î adstyle ¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Madstyle ¤Ï "adstyle" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥© + ¥ó¥È¤Î adstyle ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î + °ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î adstyle ¤ò»Ø + Äꤹ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Madstyle; + +/***en + @brief Key of font property specifying registry. + + The variable #Mregistry is a symbol of name "registry" + and is used as a key of font property. The property value must be + a symbol whose name is a registry name a font registry + (e.g. "iso8859-1", "jisx0208.1983-0"). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£ registry ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mregistry ¤Ï "registry" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥© + ¥ó¥È¤Î registry ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë¡¢´Ø¿ô mfont_get_prop () + ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Mregistry; + +/***en + @brief Key of font property specifying size. + + The variable #Msize is a symbol of name "size" and is + used as a key of font property and face property. The property + value must be an integer specifying a font design size in the unit + of 1/10 point (on 100 dpi display). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£ size ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Msize ¤Ï "size" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó¥È¤Î size ¥×¥í + ¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î size ¤ò»ØÄꤹ + ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Msize; + +/***en + @brief Key of font property specifying resolution. + + The variable #Mresolution is a symbol of name "size" and + is used as a key of font property and face property. The property + value must be an integer to specifying a font resolution in the + unit of dots per inch (dpi). */ + +/***ja + @brief ¥Õ¥©¥ó¥È¥×¥í¥Ñ¥Æ¥£ resolution ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë + + ¥·¥ó¥Ü¥ë @c Mresolution ¤Ï "resolution" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + ¥Õ¥©¥ó¥È¤Î resolution ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤­¤Ë¡¢´Ø¿ô + mfont_get_prop () ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Mresolution; + +/*=*/ +/*** @} */ +/*=*/ + +/***en + @brief List of font files and directories that contain font files. + + The variable @c mfont_freetype_path is a plist of FreeType font + files and directories that contain FreeType font files. Key of + the element is @c Mstring, and the value is a string that + represents a font file or a directory. + + The macro M17N_INIT () sets up this variable to contain the + sub-directory "fonts" of the m17n database and the environment + variable "M17NDIR". The first call of mframe () creates the + internal list of the actually available fonts from this variable. + Thus, an application program, if necessary, must modify the + variable before calling mframe (). If it is going to add a new + element, value must be a string that can be safely freed. + + If the m17n library is not configured to use the FreeType library, + this variable is not used. */ + +MPlist *mfont_freetype_path; + +/*=*/ + +/***en + @brief Create a new font. + + The mfont () function creates a new font object that has no + property. + + @return + This function returns a pointer to the created font object. */ + + +MFont * +mfont () +{ + MFont *font; + + MSTRUCT_CALLOC (font, MERROR_FONT); + return font; +} + +/*=*/ + +/***en + @brief Create a new font from fontname. + + The mfont_from_name () function creates a new font object. The + properties are extracted fontname $NAME. + + How to extract properties is window system dependent. The m17n-X + library parses $NAME as XLFD (X Logical Font Description). + + @return + If the operation was successful, this function returns a pointer + to the created font. Otherwise it returns @c NULL. */ + +/***ja + @brief ¥Õ¥©¥ó¥È̾¤«¤é¥Õ¥©¥ó¥È¤òºî¤ë + + ´Ø¿ô mfont_from_name () ¤Ï¥Õ¥©¥ó¥È̾ $NAME ¤ò²òÀϤ·¡¢¿·¤·¤¤¥Õ¥©¥ó + ¥È¤òºî¤ë¡£ + + ¥Õ¥©¥ó¥È̾¤Îʸˡ¤Ï¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¡£m17n-X ¥é¥¤¥Ö¥é¥ê + ¤Î¾ì¹ç¤Ï XLFD (X Logical Font Description) ¤Ë½¾¤¦¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mfont_from_name () ¤Ï¿·¤·¤¯ºî¤é¤ì¤¿¥Õ¥©¥ó¥È¤Ø¤Î + ¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£$NAME ¤Î²òÀϤ˼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£ */ + +MFont * +mfont_from_name (char *name) +{ + MFont template, *font; + + if (mwin__parse_font_name (name, &template) < 0) + return NULL; + MSTRUCT_CALLOC (font, MERROR_FONT); + *font = template; + return font; +} + +/*=*/ + +/***en + @brief Return a copy of a font. + + The mfont_copy () function returns a new copy of $FONT. */ + +MFont * +mfont_copy (MFont *font) +{ + MFont *copy; + + MSTRUCT_MALLOC (copy, MERROR_FONT); + *copy = *font; + return copy; +} + +/*=*/ + +/***en + @brief Create a fontname from a font. + + The mfont_name () function creates a fontname string created from + $FONT. + + The syntax of fontname is window system dependent. The m17n-X + library returns a fontname conforming to XLFD (X Logical Font + Description). + + @return + This function returns the created fontname string, which is not freed + unless the user explicitly does so by free (). */ + +char * +mfont_name (MFont *font) +{ + return mwin__build_font_name (font); +} + +/*=*/ + +/***en + @brief Get a property value of a font. + + The mfont_get_prop () function gets the value of $KEY property of + $FONT. $KEY must be one of the following symbols: + + @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Mregistry, @c Msize, @c Mresolution. + + @return + If $KEY is @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, @c + Madstyle, or @c Mregistry, this function returns the + corresponding value as a symbol. If the font does not have $KEY + property, it returns @c Mnil. + + If $KEY is @c Msize or @c Mresolution, this function returns the + corresponding value as an integer. If the font does not have $KEY + property, it returns 0. + + If $KEY is something else, it returns @c NULL and assigns an error + code to the external variable @c merror_code. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î¥×¥í¥Ñ¥Æ¥£ÃͤòÆÀ¤ë + + ´Ø¿ô mfont_get_prop () ¤Ï¥Õ¥©¥ó¥È $FONT ¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ + $KEY ¤Ç¤¢¤ë¤â¤Î¤ÎÃͤòÊÖ¤¹¡£$KEY ¤Ï°Ê²¼¤Î¥·¥ó¥Ü¥ë¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤±¤ì + ¤Ð¤Ê¤é¤Ê¤¤¡£ + + @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Mregistry, @c Msize, @c Mresolution. + + @return + ¤â¤· $KEY ¤¬ @c Msize ¤¢¤ë¤¤¤Ï @c Mresolution ¤Î¾ì¹ç¡¢ + mfont_get_prop () ¤ÏÀ°¿ô¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¥·¥ó¥Ü¥ë¤òÊÖ¤¹¡£¥¨¥é¡¼ + ¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼ + ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +void * +mfont_get_prop (MFont *font, MSymbol key) +{ + if (key == Mfoundry) + return (void *) FONT_PROPERTY (font, MFONT_FOUNDRY); + if (key == Mfamily) + return (void *) FONT_PROPERTY (font, MFONT_FAMILY); + if (key == Mweight) + return (void *) FONT_PROPERTY (font, MFONT_WEIGHT); + if (key == Mstyle) + return (void *) FONT_PROPERTY (font, MFONT_STYLE); + if (key == Mstretch) + return (void *) FONT_PROPERTY (font, MFONT_STRETCH); + if (key == Madstyle) + return (void *) FONT_PROPERTY (font, MFONT_ADSTYLE); + if (key == Mregistry) + return (void *) FONT_PROPERTY (font, MFONT_REGISTRY); + if (key == Msize) + { + int size = font->property[MFONT_SIZE]; + return (void *) size; + } + if (key == Mresolution) + { + int resy = font->property[MFONT_RESY]; + return (void *) resy; + } + + MERROR (MERROR_FONT, NULL); +} + + +/*=*/ +/***en + @brief Put a property value to a font. + + The mfont_put_prop () function puts a font property whose key is + $KEY and value is $VAL to $FONT. $KEY must be one of the following + symbols: + + @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Mregistry, @c Msize, @c Mresolution. + + If $KEY is @c Msize of @c Mresolution, $VAL must be an integer. + Otherwise, $VAL must be a symbol. */ + +int +mfont_put_prop (MFont *font, MSymbol key, void *val) +{ + if (key == Mfoundry) + mfont__set_property (font, MFONT_FOUNDRY, (MSymbol) val); + else if (key == Mfamily) + mfont__set_property (font, MFONT_FAMILY, (MSymbol) val); + else if (key == Mweight) + mfont__set_property (font, MFONT_WEIGHT, (MSymbol) val); + else if (key == Mstyle) + mfont__set_property (font, MFONT_STYLE, (MSymbol) val); + else if (key == Mstretch) + mfont__set_property (font, MFONT_STRETCH, (MSymbol) val); + else if (key == Madstyle) + mfont__set_property (font, MFONT_ADSTYLE, (MSymbol) val); + else if (key == Mregistry) + mfont__set_property (font, MFONT_REGISTRY, (MSymbol) val); + else if (key == Msize) + { + unsigned size = (unsigned) val; + font->property[MFONT_SIZE] = size; + } + else if (key == Mresolution) + { + unsigned resy = (unsigned) val; + font->property[MFONT_RESY] = resy; + } + else + MERROR (MERROR_FONT, -1); + return 0; +} + +/*=*/ + +/***en + @brief Return the font selection priority. + + The mfont_selection_priority () function returns a newly created + array of six symbols. The elements are the following + keys of font properties ordered by priority. + + @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Msize. + + The m17n library selects the best matching font according to the + order of this array. A font that has a different value for a + property of lower priority is preferred to a font that has a + different value for a property of higher priority. */ + +MSymbol * +mfont_selection_priority () +{ + MSymbol *keys; + int i; + + MTABLE_MALLOC (keys, FONT_SCORE_PRIORITY_SIZE, MERROR_FONT); + for (i = 0; i < FONT_SCORE_PRIORITY_SIZE; i++) + { + enum MFontProperty prop = font_score_priority[i]; + + if (prop == MFONT_SIZE) + keys[i] = Msize; + else if (prop == MFONT_ADSTYLE) + keys[i] = Madstyle; + else if (prop == MFONT_FAMILY) + keys[i] = Mfamily; + else if (prop == MFONT_WEIGHT) + keys[i] = Mweight; + else if (prop == MFONT_STYLE) + keys[i] = Mstyle; + else if (prop == MFONT_STRETCH) + keys[i] = Mstretch; + else + keys[i] = Mfoundry; + } + return keys; +} + +/*=*/ + +/***en + @brief Set the font selection priority. + + The mfont_set_selection_priority () function sets font selection + priority according to $KEYS, which is an array of six symbols. + Each element must be one of the below. No two elements must be + the same. + + @c Mfamily, @c Mweight, @c Mstyle, @c Mstretch, + @c Madstyle, @c Msize. + + See the documentation of the function mfont_selection_priority () + for details. */ + +int +mfont_set_selection_priority (MSymbol *keys) +{ + int priority[FONT_SCORE_PRIORITY_SIZE]; + int i, j; + + for (i = 0; i < FONT_SCORE_PRIORITY_SIZE; i++, keys++) + { + enum MFontProperty prop; + + if (*keys == Msize) + prop = MFONT_SIZE; + else if (*keys == Madstyle) + prop = MFONT_ADSTYLE; + else if (*keys == Mfamily) + prop = MFONT_FAMILY; + else if (*keys == Mweight) + prop = MFONT_WEIGHT; + else if (*keys == Mstyle) + prop = MFONT_STYLE; + else if (*keys == Mstretch) + prop = MFONT_STRETCH; + else if (*keys == Mfoundry) + prop = MFONT_FOUNDRY; + else + /* Invalid element. */ + return -1; + for (j = 0; j < i; j++) + if (priority[j] == prop) + /* Duplicated element. */ + return -1; + priority[i] = prop; + } + for (i = 0; i < FONT_SCORE_PRIORITY_SIZE; i++) + font_score_priority[i] = priority[i]; + return 0; +} + +/*=*/ + +/***en + @brief Find a font. + + The mfont_find () function returns a pointer to the available font + that matches best the specification $SPEC on frame $FRAME. + + $SCORE, if not NULL, must point to a place to store the score + value that indicates how well the found font matches to $SPEC. A + smaller score means a better match. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤òõ¤¹ + + ´Ø¿ô mfont_find () ¤Ï¡¢¥Õ¥ì¡¼¥à $FRAME ¾å¤Ç¥Õ¥©¥ó¥ÈÄêµÁ $SPEC ¤Ë¤â¤Ã + ¤È¤â¶á¤¤¥Õ¥©¥ó¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ */ + +MFont * +mfont_find (MFrame *frame, MFont *spec, int *score, int limited_size) +{ + MFont spec_copy; + MRealizedFont *rfont; + + MFONT_INIT (&spec_copy); + spec_copy.property[MFONT_REGISTRY] = spec->property[MFONT_REGISTRY]; + + rfont = mfont__select (frame, &spec_copy, spec, limited_size); + if (!rfont) + return NULL; + if (score) + *score = rfont->score; + return &rfont->font; +} + +/*=*/ +/***en + @brief Set encoding of a font. + + The mfont_set_encoding () function sets the encoding information + of $FONT. + + If $ENCODING_NAME is be a symbol representing a charset that has + the same encoding as the font. + + If $REPERTORY_NAME @c Mnil or a symbol representing a charset that + has the same repertory as the font. If it is @c Mnil, whether a + specific character is supported by the font is asked to each font + driver. + + @return + If the operation was successful, this function returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable @c merror_code. */ + +int +mfont_set_encoding (MFont *font, MSymbol encoding_name, MSymbol repertory_name) +{ + MCharset *encoding_charset = MCHARSET (encoding_name); + MCharset *repertory_charset; + MSymbol registry; + MFontEncoding *encoding; + MPlist *plist; + + if (! encoding_charset) + MERROR (MERROR_FONT, -1); + if (repertory_name != Mnil) + { + repertory_charset = MCHARSET (repertory_name); + if (! repertory_charset) + MERROR (MERROR_FONT, -1); + } + else + repertory_charset = NULL; + + MSTRUCT_CALLOC (encoding, MERROR_FONT); + encoding->spec = *font; + encoding->encoding_name = encoding_name; + encoding->encoding_charset = encoding_charset; + encoding->repertory_name = repertory_name; + encoding->repertory_charset = repertory_charset; + registry = FONT_PROPERTY (font, MFONT_REGISTRY); + if (registry == Mnil) + registry = Mt; + if (! font_encoding_list) + load_font_encoding_table (); + mplist_push (font_encoding_list, registry, encoding); + MPLIST_DO (plist, MPLIST_NEXT (font_encoding_list)) + if (! memcmp (font, &((MFontEncoding *) MPLIST_VAL (plist))->spec, + sizeof (MFont))) + { + mplist_pop (plist); + break; + } + return 0; +} + +/*** @} */ + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump a font + + The mdebug_dump_font () function prints $FONT in a human readable + way to the stderr. + + @return + This function returns $FONT. */ + +MFont * +mdebug_dump_font (MFont *font) +{ + char *name = mwin__build_font_name (font); + + fprintf (stderr, "%s", name); + free (name); + return font; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000..6c77228 --- /dev/null +++ b/src/font.h @@ -0,0 +1,253 @@ +/* font.h -- header file for the font module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_FONT_H_ +#define _M17N_FONT_H_ + +/** Type of font stored in MFont->property[MFONT_TYPE]. */ + +enum MFontType + { + /** Fonts supproted by a window system natively. On X Window + System, it is an X font. */ + MFONT_TYPE_WIN, + + /** Fonts supported by FreeType library. */ + MFONT_TYPE_FT, + + /** anchor */ + MFONT_TYPE_MAX + }; + + +enum MFontProperty + { + /* The order of MFONT_FOUNDRY to MFONT_ADSTYLE must be the same as + enum MFaceProperty. */ + MFONT_FOUNDRY, + MFONT_FAMILY, + MFONT_WEIGHT, + MFONT_STYLE, + MFONT_STRETCH, + MFONT_ADSTYLE, + MFONT_REGISTRY, + MFONT_SIZE, + MFONT_RESY, + MFONT_TYPE, + /* anchor */ + MFONT_PROPERTY_MAX + }; + +/** Information about a font. This structure is used in three ways: + FONT-OBJ, FONT-OPENED, and FONT-SPEC. + + FONT-OBJ: To store information of an existing font. Instances + appear only in of MDisplay. + + FONT-OPENED: To store information of an opened font. Instances + appear only in of MDisplay. + + FONT-SPEC: To store specifications of a font. Instances appear + only in of MFontset. */ + +struct MFont +{ + /** Numeric value of each font property. Also used as an index to + the table @c mfont__property_table to get the actual name of the + property. + + For FONT-OBJ, FONT-OPENED: The value is greater than zero. + + For FONT-SPEC: The value is equal to or greater than zero. Zero + means that the correponding property is not specified (i.e. wild + card). + + [MFONT_SIZE] is the size of the font in 1/10 pixels. + + For FONT-OBJ: If the value is 0, the font is scalable or + auto-scaled. + + For FONT-OPENED: The actual size of opened font. + + [MFONT_RESY] is the designed resolution of the font in + DPI, or zero. Zero means that the font is scalable. + + For the time being, we mention only Y-resolution (resy) and + assume that resx is always equal to resy. */ + unsigned short property[MFONT_PROPERTY_MAX]; +}; + +typedef struct +{ + int size, inc, used; + MSymbol property; + MSymbol *names; +} MFontPropertyTable; + +extern MFontPropertyTable mfont__property_table[MFONT_REGISTRY + 1]; + +/** Return the symbol of the Nth font property of FONT. */ +#define FONT_PROPERTY(font, n) \ + mfont__property_table[(n)].names[(font)->property[(n)]] + +typedef struct MFontEncoding MFontEncoding; + +struct MRealizedFont +{ + /* Frame on which the font is realized. */ + MFrame *frame; + + /* Font spec used to find the font. */ + MFont spec; + + /* Font spec requested by a face. */ + MFont request; + + /* The found font. */ + MFont font; + + /* How well matches with . */ + int score; + + MFontDriver *driver; + + /* Font Layout Table for the font. */ + MSymbol layouter; + + /* 0: not yet tried to open + -1: failed to open + 1: succeessfully opened. */ + int status; + + /* Extra information set by ->select or ->open. If + non-NULL, it must be a pointer to a managed object. */ + void *info; + + short ascent, descent; + + MFontEncoding *encoding; +}; + +/** Structure to hold a list of fonts of each registry. */ + +typedef struct +{ + MSymbol tag; + int nfonts; + MFont *fonts; +} MFontList; + + +struct MFontDriver +{ + /** Return a font best matching with SPEC. */ + MRealizedFont *(*select) (MFrame *frame, MFont *spec, MFont *request, + int limitted_size); + + /** Open a font specified by RFONT. */ + int (*open) (MRealizedFont *rfont); + + /** Close a font specified by RFONT. */ + void (*close) (MRealizedFont *rfont); + + void (*find_metric) (MRealizedFont *rfont, MGlyph *g); + + /** Encode C into the glyph code the font. CODE is a code point of + C in rfont->encoder->encoding_charset. If the font has no glyph + for C, return MCHAR_INVALID_CODE. */ + unsigned (*encode_char) (MRealizedFont *rfont, int c, unsigned code); + + /** Draw glyphs from FROM to TO (exclusive) on window WIN of FRAME + at coordinate (X, Y) relative to WIN. */ + void (*render) (MDrawWindow win, int x, int y, + MGlyphString *gstring, MGlyph *from, MGlyph *to, + int reverse, MDrawRegion region); +}; + +/** Initialize the members of FONT. */ + +#define MFONT_INIT(font) memset ((font), 0, sizeof (MFont)) + + +extern MFontDriver *mfont__driver_list[MFONT_TYPE_MAX]; + +extern MSymbol Mlayouter; + +extern int mfont__flt_init (); + +extern void mfont__flt_fini (); + +#ifdef HAVE_FREETYPE +extern int mfont__ft_init (); + +extern void mfont__ft_fini (); + +extern int mfont__ft_drive_otf (MGlyphString *gstring, int from, int to, + MSymbol script, MSymbol langsys, + MSymbol gsub_features, MSymbol gpos_features); +extern int mfont__ft_decode_otf (MGlyph *g); + +#endif /* HAVE_FREETYPE */ + +extern void mfont__free_realized (MRealizedFont *rfont); + +extern int mfont__match_p (MFont *font, MFont *spec, int prop); + +extern int mfont__score (MFont *font, MFont *spec, MFont *request, + int limitted_size); + +extern void mfont__set_spec_from_face (MFont *spec, MFace *face); + +extern MSymbol mfont__set_spec_from_plist (MFont *spec, MPlist *plist); + +extern void mfont__resize (MFont *spec, MFont *request); + +extern int mfont__encodable_p (MRealizedFont *rfont, MSymbol layouter_name, + int c); + +extern unsigned mfont__encode_char (MRealizedFont *rfont, int c); + +extern MRealizedFont *mfont__select (MFrame *frame, MFont *spec, + MFont *request, int limitted_size); + +extern int mfont__open (MRealizedFont *rfont); + +extern void mfont__close (MRealizedFont *rfont); + +extern void mfont__get_metric (MRealizedFont *rfont, MGlyph *g); + +extern void mfont__set_property (MFont *font, enum MFontProperty key, + MSymbol val); + +extern int mfont__split_name (char *name, int *property_idx, + unsigned short *point, unsigned short *resy); + +extern void mfont__set_spec (MFont *font, + MSymbol attrs[MFONT_PROPERTY_MAX], + unsigned short size, unsigned short resy); + +extern unsigned mfont__flt_encode_char (MSymbol layouter_name, int c); + +extern int mfont__flt_run (MGlyphString *gstring, int from, int to, + MSymbol layouter_name, MRealizedFace *ascii_rface); + +#endif /* _M17N_FONT_H_ */ diff --git a/src/fontset.c b/src/fontset.c new file mode 100644 index 0000000..af938ae --- /dev/null +++ b/src/fontset.c @@ -0,0 +1,934 @@ +/* fontset.c -- fontset module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nFontset + @brief A fontset is an object that maps a character to fonts. + + A @e fontset is an object of the type @c MFontset. When drawing an + M-text, a fontset provides rules to select a font for each + character in the M-text according to the following information. + + - The script character property of a character. + - The language text property of a character. + - The charset text property of a character. + + The documentation of mdraw_text () describes how that information is + used. */ + +/***ja + @addtogroup m17nFontset + @brief ¥Õ¥©¥ó¥È¥»¥Ã¥È¤Ï°ìÄê¤Î¥¹¥¿¥¤¥ë¤ò¶¦Í­¤¹¤ë¥Õ¥©¥ó¥È¤Î½¸¹ç¤Ç¤¢¤ë + + @e ¥Õ¥©¥ó¥È¥»¥Ã¥È ¤Ï @c MFontset ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ê¡¢Â¿¸À¸ìʸ + ½ñ¤Îɽ¼¨¤ÎºÝ¡¢¸«¤«¤±¤Î»÷¤¿¥Õ¥©¥ó¥È¤ò½¸¤á¤Æ°ì´Ó¤·¤Æ¼è¤ê°·¤¦¤¿¤á¤Ë»È + ¤ï¤ì¤ë¡£¥Õ¥©¥ó¥È¥»¥Ã¥È¤Ï¤Þ¤¿¡¢¸À¸ì¡¢Ê¸»ú¥»¥Ã¥È¡¢Ê¸»ú¤¬Í¿¤¨¤é¤ì¤¿¤È + ¤­¤ËŬÀڤʥե©¥ó¥È¤òÁªÂò¤¹¤ë¤¿¤á¤Î¾ðÊó¤âÊÝ»ý¤·¤Æ¤¤¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "plist.h" +#include "character.h" +#include "charset.h" +#include "internal-gui.h" +#include "font.h" +#include "fontset.h" + +struct MFontset +{ + M17NObject control; + + /* Name of the fontset. */ + MSymbol name; + + /* Initialized to 0, and incremented by one each time the fontset is + modified. */ + unsigned tick; + + /* Database from which to load the contents of the fontset. Once + loaded, this member is set to NULL. */ + MDatabase *mdb; + + /* SCRIPT vs PER-LANGUAGE (which is a plist LANGUAGE vs FONT-GROUP) */ + MPlist *per_script; + + /* CHARSET vs FONT-GROUP */ + MPlist *per_charset; + + /* FONT-GROUP */ + MPlist *fallback; + + /* Plist of Mt vs font specs. */ + MPlist *font_spec_list; +}; + +static MFontset *default_fontset; + +static MPlist *fontset_list; + +struct MRealizedFontset +{ + /* Fontset from which the realized fontset is realized. */ + MFontset *fontset; + + /* Initialized to ->tick. */ + unsigned tick; + + /* Font spec extracted from a face. */ + MFont spec; + + /* The frame on which the realized fontset is realized. */ + MFrame *frame; + + MPlist *per_script; + + MPlist *per_charset; + + MPlist *fallback; +}; + + +static MPlist * +load_font_group (MPlist *plist, MPlist *elt, MPlist *spec_list) +{ + MPLIST_DO (elt, elt) + { + /* ELT ::= ( FONT-SPEC-LIST [ LAYOUTER ] ) ... */ + MPlist *elt2, *p; + MFont font, *spec = NULL; + MSymbol layouter_name; + + if (! MPLIST_PLIST_P (elt)) + MWARNING (MERROR_FONTSET); + elt2 = MPLIST_PLIST (elt); + if (! MPLIST_PLIST_P (elt2)) + MWARNING (MERROR_FONTSET); + mfont__set_spec_from_plist (&font, MPLIST_PLIST (elt2)); + MPLIST_DO (p, spec_list) + { + if (! memcmp (MPLIST_VAL (p), &font, sizeof (MFont))) + { + spec = MPLIST_VAL (p); + break; + } + } + if (! spec) + { + MSTRUCT_MALLOC (spec, MERROR_FONTSET); + *spec = font; + mplist_add (spec_list, Mt, spec); + } + elt2 = MPLIST_NEXT (elt2); + layouter_name = Mt; + if (MPLIST_SYMBOL_P (elt2)) + layouter_name = MPLIST_SYMBOL (elt2); + if (layouter_name == Mnil) + layouter_name = Mt; + plist = mplist_add (plist, layouter_name, spec); + continue; + warning: + /* ANSI-C requires some statement after a label. */ + continue; + } + return plist; +} + +/* Load FONTSET->per_script from the data in FONTSET->mdb. */ + +static void +load_fontset_contents (MFontset *fontset) +{ + MPlist *per_script, *per_charset, *fallback, *spec_list, *font_group; + MSymbol script, lang; + MPlist *fontset_def, *plist; + + fontset->per_script = per_script = mplist (); + fontset->per_charset = per_charset = mplist (); + fontset->fallback = fallback = mplist (); + fontset->font_spec_list = spec_list = mplist (); + if (! (fontset_def = (MPlist *) mdatabase_load (fontset->mdb))) + return; + + MPLIST_DO (plist, fontset_def) + { + /* PLIST ::= ( SCRIPT ( LANGUAGE FONT-SPEC-ELT ... ) ... ) + | (CHARSET FONT-SPEC-ELT ...) + | FONT-SPEC-ELT */ + MPlist *elt; + + if (! MPLIST_PLIST_P (plist)) + MWARNING (MERROR_FONTSET); + elt = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (elt)) + MWARNING (MERROR_FONTSET); + script = MPLIST_SYMBOL (elt); + elt = MPLIST_NEXT (elt); + if (! MPLIST_PLIST_P (elt)) + MWARNING (MERROR_FONTSET); + if (script == Mnil) + fallback = load_font_group (fallback, elt, spec_list); + else if (MPLIST_PLIST_P (MPLIST_PLIST (elt))) + { + font_group = mplist_find_by_key (fontset->per_charset, script); + if (! font_group) + { + font_group = mplist (); + per_charset = mplist_add (per_charset, script, font_group); + } + load_font_group (font_group, elt, spec_list); + } + else + { + MPlist *per_lang = mplist_find_by_key (fontset->per_script, script); + + if (! per_lang) + { + per_lang = mplist (); + per_script = mplist_add (per_script, script, per_lang); + } + + MPLIST_DO (elt, elt) + { + /* ELT ::= ( LANGUAGE FONT-DEF ...) ... */ + MPlist *elt2; + + if (! MPLIST_PLIST_P (elt)) + MWARNING (MERROR_FONTSET); + elt2 = MPLIST_PLIST (elt); + if (! MPLIST_SYMBOL_P (elt2)) + MWARNING (MERROR_FONTSET); + lang = MPLIST_SYMBOL (elt2); + if (lang == Mnil) + lang = Mt; + font_group = mplist_find_by_key (per_lang, lang); + if (! font_group) + { + font_group = mplist (); + mplist_add (per_lang, lang, font_group); + } + elt2 = MPLIST_NEXT (elt2); + load_font_group (font_group, elt2, spec_list); + } + } + continue; + + warning: + /* ANSI-C requires some statement after a label. */ + continue; + } + + M17N_OBJECT_UNREF (fontset_def); + fontset->mdb = NULL; +} + +static void +free_fontset (void *object) +{ + MFontset *fontset = (MFontset *) object; + MPlist *plist, *pl, *p; + + if (fontset->per_script) + { + MPLIST_DO (plist, fontset->per_script) + { + MPLIST_DO (pl, MPLIST_PLIST (plist)) + { + p = MPLIST_PLIST (pl); + M17N_OBJECT_UNREF (p); + } + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + M17N_OBJECT_UNREF (fontset->per_script); + } + if (fontset->per_charset) + { + MPLIST_DO (plist, fontset->per_charset) + { + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + M17N_OBJECT_UNREF (fontset->per_charset); + } + if (fontset->fallback) + M17N_OBJECT_UNREF (fontset->fallback); + plist = mplist_find_by_key (fontset_list, fontset->name); + if (! plist) + mdebug_hook (); + mplist_pop (plist); + if (fontset->font_spec_list) + { + if (((M17NObject *) (fontset->font_spec_list))->ref_count == 1) + MPLIST_DO (plist, fontset->font_spec_list) + free (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (fontset->font_spec_list); + } + free (object); +} + +static void +realize_font_group (MFrame *frame, MFont *request, MPlist *font_group, + int size) +{ + MPlist *plist = MPLIST_VAL (font_group), *pl, *p; + + mplist_set (font_group, Mnil, NULL); + MPLIST_DO (pl, plist) + { + MRealizedFont *rfont = mfont__select (frame, MPLIST_VAL (pl), request, + size); + + if (rfont) + { + rfont->layouter = MPLIST_KEY (pl); + if (rfont->layouter == Mt) + rfont->layouter = Mnil; + MPLIST_DO (p, font_group) + if (((MRealizedFont *) (MPLIST_VAL (p)))->score > rfont->score) + break; + mplist_push (p, Mt, rfont); + } + } +} + +int +check_fontset_element (MFrame *frame, MPlist *element, MGlyph *g, int size) +{ + MRealizedFont *rfont = (MRealizedFont *) MPLIST_VAL (element); + + if (! rfont) + /* We have already failed to select this font. */ + return 0; + if (! rfont->frame) + { + rfont = mfont__select (frame, &rfont->spec, &rfont->request, size); + free (MPLIST_VAL (element)); + MPLIST_VAL (element) = rfont; + if (! rfont) + /* No font matches this spec. */ + return 0; + } + + g->code = mfont__encode_char (rfont, g->c); + return (g->code != MCHAR_INVALID_CODE); +} + + + +/* Internal API */ + +int +mfont__fontset_init () +{ + Mfontset = msymbol ("fontset"); + Mfontset->managing_key = 1; + fontset_list = mplist (); + default_fontset = mfontset ("default"); + if (! default_fontset->mdb) + { + MFont font; + + MFONT_INIT (&font); + mfont_put_prop (&font, Mregistry, msymbol ("iso8859-1")); + mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil, + &font, Mnil, 1); + mfont_put_prop (&font, Mregistry, msymbol ("iso10646-1")); + mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil, + &font, Mnil, 1); + } + return 0; +} + + +void +mfont__fontset_fini () +{ + while (! MPLIST_TAIL_P (fontset_list)) + free_fontset ((MFontset *) MPLIST_VAL (fontset_list)); + M17N_OBJECT_UNREF (fontset_list); + fontset_list = NULL; +} + + +MRealizedFontset * +mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face) +{ + MRealizedFontset *realized; + MFont request; + MPlist *per_script, *per_lang, *per_charset, *font_group; + MPlist *plist, *pl, *p; + + if (fontset->mdb) + load_fontset_contents (fontset); + + mfont__set_spec_from_face (&request, face); + if (request.property[MFONT_SIZE] <= 0) + { + mdebug_hook (); + request.property[MFONT_SIZE] = 120; + } + MPLIST_DO (p, frame->realized_fontset_list) + { + realized = (MRealizedFontset *) MPLIST_VAL (p); + if (fontset->name == MPLIST_KEY (p) + && ! memcmp (&request, &realized->spec, sizeof (request))) + return realized; + } + + MSTRUCT_MALLOC (realized, MERROR_FONTSET); + realized->fontset = fontset; + realized->tick = fontset->tick; + realized->spec = request; + realized->frame = frame; + realized->per_script = per_script = mplist (); + MPLIST_DO (plist, fontset->per_script) + { + per_lang = mplist (); + per_script = mplist_add (per_script, MPLIST_KEY (plist), per_lang); + MPLIST_DO (pl, MPLIST_PLIST (plist)) + { + font_group = mplist (); + mplist_add (font_group, Mplist, MPLIST_VAL (pl)); + per_lang = mplist_add (per_lang, MPLIST_KEY (pl), font_group); + } + } + + realized->per_charset = per_charset = mplist (); + MPLIST_DO (plist, fontset->per_charset) + { + font_group = mplist (); + mplist_add (font_group, Mplist, MPLIST_VAL (plist)); + per_charset = mplist_add (per_charset, MPLIST_KEY (plist), font_group); + } + + realized->fallback = mplist (); + mplist_add (realized->fallback, Mplist, fontset->fallback); + + mplist_add (frame->realized_fontset_list, fontset->name, realized); + return realized; +} + + +void +mfont__free_realized_fontset (MRealizedFontset *realized) +{ + MPlist *plist, *pl, *p; + MRealizedFont *rfont; + + if (realized->per_script) + { + MPLIST_DO (plist, realized->per_script) + { + MPLIST_DO (pl, MPLIST_PLIST (plist)) + { + MPLIST_DO (p, MPLIST_PLIST (pl)) + if ((rfont = MPLIST_VAL (p)) && ! rfont->frame) + free (rfont); + p = MPLIST_PLIST (pl); + M17N_OBJECT_UNREF (p); + } + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + M17N_OBJECT_UNREF (realized->per_script); + } + if (realized->per_charset) + { + MPLIST_DO (plist, realized->per_charset) + { + MPLIST_DO (pl, MPLIST_PLIST (plist)) + if ((rfont = MPLIST_VAL (pl)) && ! rfont->frame) + free (rfont); + pl = MPLIST_PLIST (plist); + M17N_OBJECT_UNREF (pl); + } + M17N_OBJECT_UNREF (realized->per_charset); + } + if (realized->fallback) + { + MPLIST_DO (plist, realized->fallback) + if ((rfont = MPLIST_VAL (plist)) && ! rfont->frame) + free (rfont); + M17N_OBJECT_UNREF (realized->fallback); + } + + free (realized); +} + + +MRealizedFont * +mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num, + MSymbol script, MSymbol language, MSymbol charset, + int size) +{ + MFrame *frame = realized->frame; + MCharset *preferred_charset = (charset == Mnil ? NULL : MCHARSET (charset)); + MPlist *per_charset, *per_script, *per_lang, *font_group; + MPlist *font_groups[256], *plist; + int n_font_group = 0; + MRealizedFont *first = NULL, *rfont; + int first_len; + int i; + + if (preferred_charset + && (per_charset = mplist_get (realized->per_charset, charset)) != NULL) + font_groups[n_font_group++] = per_charset; + if (script != Mnil + && ((per_script = mplist_find_by_key (realized->per_script, script)) + != NULL)) + { + /* The first loop is for matching language (if any), and the second + loop is for non-matching languages. */ + if (language == Mnil) + language = Mt; + for (i = 0; i < 2; i++) + { + MPLIST_DO (per_lang, MPLIST_PLIST (per_script)) + if ((MPLIST_KEY (per_lang) == language) != i) + font_groups[n_font_group++] = MPLIST_PLIST (per_lang); + } + } + font_groups[n_font_group++] = realized->fallback; + + if (n_font_group == 1) + { + /* As we only have a fallback font group, try all the other + fonts too. */ + MPLIST_DO (per_script, realized->per_script) + MPLIST_DO (per_lang, MPLIST_PLIST (per_script)) + font_groups[n_font_group++] = MPLIST_PLIST (per_lang); + MPLIST_DO (per_charset, realized->per_charset) + font_groups[n_font_group++] = MPLIST_PLIST (per_charset); + } + + for (i = 0; i < n_font_group; i++) + { + int j; + + if (MPLIST_PLIST_P (font_groups[i])) + realize_font_group (frame, &realized->spec, font_groups[i], size); + + MPLIST_DO (plist, font_groups[i]) + { + rfont = (MRealizedFont *) MPLIST_VAL (plist); + g->code = mfont__encode_char (rfont, g->c); + if (g->code != MCHAR_INVALID_CODE) + break; + } + if (MPLIST_TAIL_P (plist)) + continue; + for (j = 1; j < *num; j++) + { + g[j].code = mfont__encode_char (rfont, g[j].c); + if (g[j].code == MCHAR_INVALID_CODE) + break; + } + if (! first) + first = rfont, first_len = j; + if (j == *num) + /* We found a font that can display all requested + characters. */ + break; + + MPLIST_DO (plist, MPLIST_NEXT (plist)) + { + rfont = (MRealizedFont *) MPLIST_VAL (plist); + for (j = 0; j < *num; j++) + { + g[j].code = mfont__encode_char (rfont, g[j].c); + if (g[j].code == MCHAR_INVALID_CODE) + break; + } + if (j == *num) + break; + } + if (! MPLIST_TAIL_P (plist)) + break; + } + + if (i == n_font_group) + { + if (! first) + return NULL; + rfont = first, *num = first_len; + for (i = 0; i < *num; i++) + g[i].code = mfont__encode_char (rfont, g[i].c); + } + if (! rfont->status + && mfont__open (rfont) < 0) + { + MPLIST_VAL (font_group) = NULL; + return NULL; + } + return rfont; +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nFontset */ +/*** @{ */ + +/*=*/ +/***en + @brief Return a fontset. + + The mfontset () function returns a pointer to a fontset object of + name $NAME. If $NAME is @c NULL, it returns a pointer to the + default fontset. + + If no fontset has the name $NAME, a new one is created. At that + time, if there exists a data \<@c fontset, $NAME\> in the m17n + database, the fontset contents are initialized according to the + data. If no such data exists, the fontset contents are left + vacant. + + The macro M17N_INIT () creates the default fontset. An + application program can modify it before the first call of mframe + (). + + @return + This function returns a pointer to the found or newly created + fontset. */ + +MFontset * +mfontset (char *name) +{ + MSymbol sym; + MFontset *fontset; + + if (! name) + return default_fontset; + sym = msymbol (name); + fontset = mplist_get (fontset_list, sym); + if (fontset) + return fontset; + M17N_OBJECT (fontset, free_fontset, MERROR_FONTSET); + fontset->name = sym; + fontset->mdb = mdatabase_find (Mfontset, sym, Mnil, Mnil); + if (! fontset->mdb) + { + fontset->per_script = mplist (); + fontset->per_charset = mplist (); + fontset->fallback = mplist (); + } + mplist_put (fontset_list, sym, fontset); + M17N_OBJECT_REF (fontset); + return fontset; +} + +/*=*/ + +/***en + @brief Return the name of a fontset + + The mfontset_name () function returns the name of $FONTSET. */ +MSymbol +mfontset_name (MFontset *fontset) +{ + return fontset->name; +} + +/*=*/ + +/***en + @brief Make a copy a fontset + + The mfontset_copy () function makes a copy of $FONTSET, gives it a + name $NAME, and returns a pointer to the created copy. $NAME must + not be a name of existing fontset. Otherwise, this function + returns NULL without making a copy. */ + +MFontset * +mfontset_copy (MFontset *fontset, char *name) +{ + MSymbol sym = msymbol (name); + MFontset *copy = mplist_get (fontset_list, sym); + MPlist *plist, *pl; + + if (copy) + return NULL; + M17N_OBJECT (copy, free_fontset, MERROR_FONTSET); + copy->name = sym; + + if (fontset->per_script) + { + copy->per_script = mplist (); + MPLIST_DO (plist, fontset->per_script) + { + MPlist *new = mplist (); + + MPLIST_DO (pl, MPLIST_PLIST (plist)) + mplist_add (new, MPLIST_KEY (pl), mplist_copy (MPLIST_PLIST (pl))); + mplist_add (copy->per_script, MPLIST_KEY (plist), new); + } + } + if (fontset->per_charset) + { + copy->per_charset = mplist (); + MPLIST_DO (plist, fontset->per_charset) + mplist_add (copy->per_charset, MPLIST_KEY (plist), + mplist_copy (MPLIST_PLIST (plist))); + } + if (fontset->fallback) + copy->fallback = mplist_copy (fontset->fallback); + + copy->font_spec_list = fontset->font_spec_list; + M17N_OBJECT_REF (copy->font_spec_list); + + mplist_put (fontset_list, sym, copy); + M17N_OBJECT_REF (copy); + return copy; +} + +/*=*/ + +/***en + @brief Modify the contents of a fontset. + + The mfontset_modify_entry () function associates, in $FONTSET, a + copy of $FONT with the $SCRIPT / $LANGUAGE pair or with $CHARSET. + + Each font in a fontset is associated with a particular + script/language pair, with a particular charset, or with the + symbol @c Mnil. The fonts that are associated with the same item + make a group. + + If $SCRIPT is not @c Mnil, it must be a symbol identifying a + script. In this case, $LANGUAGE is either a symbol identifying a + language or @c Mnil, and $FONT is associated with the $SCRIPT / + $LANGUAGE pair. + + If $CHARSET is not @c Mnil, it must be a symbol representing a + charset object. In this case, $FONT is associated with that + charset. + + If both $SCRIPT and $CHARSET are not @c Mnil, two copies of $FONT + are created. Then one is associated with the script/language pair + and the other with the charset. + + If both $SCRIPT and $CHARSET are @c Mnil, $FONT is associated + with @c Mnil. This kind of fonts are called fallback + fonts. + + The argument $HOW specifies the priority of $FONT. If $HOW is + positive, $FONT has the highest priority in the group of fonts + that are associated with the same item. If $HOW is negative, + $FONT has the lowest priority. If $HOW is zero, $FONT becomes the + only available font for the associated item; all the other fonts + are removed from the group. + + If $LAYOUTER_NAME is not @c Mnil, it must be a symbol + representing a @ref flt. In that case, if $FONT is selected for + drawing an M-text, that font layout table is used to generate a + glyph code sequence from a character sequence. + + @return + If the operation was successful, mfontset_modify_entry () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable @c merror_code. */ + +/***ja + @brief ¸À¸ì¤È¥¹¥¯¥ê¥×¥È¤ÎÁȤ߹ç¤ï¤»¤Ë¥Õ¥©¥ó¥È¤ò´ØÏ¢ÉÕ¤±¤ë + + ´Ø¿ô mfontset_set_language_script () ¤Ï¡¢$LANGUAGE ¤È $SCRIPT ¤ÎÁÈ + ¤ß¹ç¤ï¤»¤ËÂФ·¤Æ¥Õ¥©¥ó¥È $FONT ¤ò»È¤¦¤è¤¦¡¢¥Õ¥©¥ó¥È¥»¥Ã¥È $FONTSET + ¤òÀßÄꤹ¤ë¡£$LANGUAGE ¤È $SCRIPT ¤Ï¡¢¤½¤ì¤¾¤ì¸À¸ì¤È¥¹¥¯¥ê¥×¥È¤ò»Ø + Äꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£ + + ¸À¸ì¤È¥¹¥¯¥ê¥×¥È¤ÎÁȤ߹ç¤ï¤»°ì¤Ä¤ËÂФ·¤ÆÊ£¿ô¤Î¥Õ¥©¥ó¥È¤òÀßÄꤹ¤ë¤³ + ¤È¤â¤Ç¤­¤ë¡£$PREPEND_P ¤¬ 0 °Ê³°¤Ê¤é¤Ð¡¢$FONT ¤Ï¤½¤ÎÁȤ߹ç¤ï¤»¤ËÂÐ + ¤·¤ÆºÇÍ¥Àè¤ÇÍѤ¤¤é¤ì¤ë¤â¤Î¤È¤Ê¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢Í¥ÀèÅÙºÇÄã¤Î¥Õ¥© + ¥ó¥È¤È¤·¤ÆÅÐÏ¿¤µ¤ì¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¤È¤­¡¢mfontset_set_language_script () ¤Ï 0 ¤òÊÖ¤¹¡£ + ¼ºÇÔ¤·¤¿¤È¤­¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_SYMBOL */ + +int +mfontset_modify_entry (MFontset *fontset, + MSymbol script, MSymbol language, MSymbol charset, + MFont *spec, MSymbol layouter_name, + int how) +{ + MPlist *per_lang, *plist[3], *pl; + MFont *font = NULL; + int i; + + if (fontset->mdb) + load_fontset_contents (fontset); + + MPLIST_DO (pl, fontset->font_spec_list) + { + if (! memcmp (MPLIST_VAL (pl), spec, sizeof (MFont))) + { + font = MPLIST_VAL (pl); + break; + } + } + if (! font) + { + font = mfont (); + *font = *spec; + mplist_add (fontset->font_spec_list, Mt, font); + } + + i = 0; + if (script != Mnil) + { + if (language == Mnil) + language = Mt; + per_lang = mplist_get (fontset->per_script, script); + if (! per_lang) + mplist_add (fontset->per_script, script, per_lang = mplist ()); + plist[i] = mplist_get (per_lang, language); + if (! plist[i]) + mplist_add (per_lang, language, plist[i] = mplist ()); + i++; + } + if (charset != Mnil) + { + plist[i] = mplist_get (fontset->per_charset, charset); + if (! plist[i]) + mplist_add (fontset->per_charset, charset, plist[i] = mplist ()); + i++; + } + if (script == Mnil && charset == Mnil) + { + plist[i++] = fontset->fallback; + } + + if (layouter_name == Mnil) + layouter_name = Mt; + for (i--; i >= 0; i--) + { + if (how == -1) + mplist_push (plist[i], layouter_name, font); + else if (how == 1) + mplist_add (plist[i], layouter_name, font); + else + { + mplist_set (plist[i], Mnil, NULL); + mplist_add (plist[i], layouter_name, font); + } + } + + return 0; +} + +/*** @} */ + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump a fontset + + The mdebug_dump_fontset () function prints $FONTSET in a human readable + way to the stderr. $INDENT specifies how many columns to indent + the lines but the first one. + + @return + This function returns $FONTSET. */ + +MFontset * +mdebug_dump_fontset (MFontset *fontset, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MPlist *plist, *pl, *p; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "(fontset %s", fontset->name->name); + if (fontset->per_script) + MPLIST_DO (plist, fontset->per_script) + { + fprintf (stderr, "\n %s(%s", prefix, MPLIST_KEY (plist)->name); + MPLIST_DO (pl, MPLIST_PLIST (plist)) + { + fprintf (stderr, "\n %s(%s", prefix, MPLIST_KEY (pl)->name); + MPLIST_DO (p, MPLIST_PLIST (pl)) + { + fprintf (stderr, "\n %s(%s ", prefix, + MPLIST_KEY (p)->name); + mdebug_dump_font (MPLIST_VAL (p)); + fprintf (stderr, ")"); + } + fprintf (stderr, ")"); + } + fprintf (stderr, ")"); + } + if (fontset->per_charset) + MPLIST_DO (pl, fontset->per_charset) + { + fprintf (stderr, "\n %s(%s", prefix, MPLIST_KEY (pl)->name); + MPLIST_DO (p, MPLIST_PLIST (pl)) + { + fprintf (stderr, "\n %s(%s ", prefix, MPLIST_KEY (p)->name); + mdebug_dump_font (MPLIST_VAL (p)); + fprintf (stderr, ")"); + } + fprintf (stderr, ")"); + } + + if (fontset->fallback) + MPLIST_DO (p, fontset->fallback) + { + fprintf (stderr, "\n %s(%s ", prefix, MPLIST_KEY (p)->name); + mdebug_dump_font (MPLIST_VAL (p)); + fprintf (stderr, ")"); + } + + fprintf (stderr, ")"); + return fontset; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/fontset.h b/src/fontset.h new file mode 100644 index 0000000..872650a --- /dev/null +++ b/src/fontset.h @@ -0,0 +1,37 @@ +/* fontset.h -- header file for the fontset module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_FONTSET_H_ +#define _M17N_FONTSET_H_ + +extern MRealizedFontset *mfont__realize_fontset (MFrame *frame, + MFontset *fontset, + MFace *face); + +void mfont__free_realized_fontset (MRealizedFontset *realized); + +extern MRealizedFont *mfont__lookup_fontset (MRealizedFontset *realized, + MGlyph *g, int *num, + MSymbol script, MSymbol language, + MSymbol charset, int size); + +#endif /* _M17N_FONTSET_H_ */ diff --git a/src/input-gui.c b/src/input-gui.c new file mode 100644 index 0000000..f9b3144 --- /dev/null +++ b/src/input-gui.c @@ -0,0 +1,730 @@ +/* input-gui.c -- gui-based input method module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nInputMethodWin + @brief Input method support on window systems. + + The input driver @c minput_gui_driver is provided for internal + input methods that is useful on window systems. It displays + preedit text and status text at the inputting spot. See the + documentation of @c minput_gui_driver for more detail. + + In the m17n-X library, the foreign input method of name @c Mxim is + provided. It uses XIM (X Input Method) as a background input + engine. The symbol @c Mxim has a property @c Minput_driver whose + value is a pointer to the input driver @c minput_xim_driver. See + the documentation of @c minput_xim_driver for more detail. */ + +/***ja + @addtogroup m17nInputMethodWin + @brief ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¾å¤ÎÆþÎϥ᥽¥Ã¥É¤Î¥µ¥Ý¡¼¥È + + ÆþÎϥɥ饤¥Ð @c minput_gui_driver ¤Ï¡¢¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¾å¤ÇÊØÍø¤Ë + ÍѤ¤¤é¤ì¤ëÆâÉôÆþÎϥ᥽¥Ã¥É¤Î¤¿¤á¤Î¤â¤Î¤Ç¤¢¤ë¡£¤³¤Î¥É¥é¥¤¥Ð¤ÏÆþÎÏ¥¹ + ¥Ý¥Ã¥È¤Ë preedit ¥Æ¥­¥¹¥È¤È status ¥Æ¥­¥¹¥È¤òɽ¼¨¤¹¤ë¡£¾ÜºÙ¤Ë¤Ä¤¤ + ¤Æ¤Ï @c minput_gui_driver ¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + m17n-X ¥é¥¤¥Ö¥é¥ê¤Ï¡¢@c Mxim ¤È¸À¤¦Ì¾Á°¤ò»ý¤Ä³°ÉôÆþÎϥ᥽¥Ã¥É¤òÄó + ¶¡¤·¤Æ¤¤¤ë¡£¤³¤ì¤Ï XIM (X Input Method) ¤ò¥Ð¥Ã¥¯¥°¥é¥¦¥ó¥É¤ÎÆþÎÏ¥¨ + ¥ó¥¸¥ó¤È¤·¤ÆÍøÍѤ¹¤ë¡£¥·¥ó¥Ü¥ë @c Mxim ¤Ï @c Minput_driver ¤È¤¤¤¦ + ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ã¤Æ¤ª¤ê¡¢¤½¤ÎÃͤÏÆþÎϥɥ饤¥Ð @c minput_xim_driver + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£ ¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï @c minput_xim_driver ¤Î¥É¥­¥å + ¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "internal-gui.h" +#include "input.h" + +typedef struct +{ + MDrawWindow win; + MDrawMetric geometry; + MDrawControl control; + int mapped; +} MInputGUIWinInfo; + +typedef struct +{ + MInputContextInfo *ic_info; + + MFrame *frame; + /* .x and .y are not used. */ + MInputGUIWinInfo client; + /* In the following members, is relative to . */ + MInputGUIWinInfo focus; + MInputGUIWinInfo preedit; + MInputGUIWinInfo status; + MInputGUIWinInfo candidates; +} MInputGUIContextInfo; + +static MFace *status_face; +static MFaceBoxProp face_box_prop; + +static int +win_create_ic (MInputContext *ic) +{ + MInputGUIContextInfo *win_ic_info; + MInputGUIArgIC *win_info = (MInputGUIArgIC *) ic->arg; + MFrame *frame = win_info->frame; + + if ((*minput_default_driver.create_ic) (ic) < 0) + return -1; + + MSTRUCT_CALLOC (win_ic_info, MERROR_IM); + win_ic_info->ic_info = (MInputContextInfo *) ic->info; + win_ic_info->frame = frame; + win_ic_info->client.win = win_info->client; + mwin__window_geometry (frame, win_info->client, win_info->client, + &win_ic_info->client.geometry); + win_ic_info->focus.win = win_info->focus; + mwin__window_geometry (frame, win_info->focus, win_info->client, + &win_ic_info->focus.geometry); + + win_ic_info->preedit.win = mwin__create_window (frame, win_info->client); + win_ic_info->preedit.control.two_dimensional = 1; + win_ic_info->preedit.control.as_image = 1; + win_ic_info->preedit.control.with_cursor = 1; + win_ic_info->preedit.control.cursor_width = 1; + win_ic_info->preedit.control.disable_caching = 1; + win_ic_info->preedit.geometry.x = -1; + win_ic_info->preedit.geometry.y = -1; + + win_ic_info->status.win = mwin__create_window (frame, win_info->client); + win_ic_info->status.control.as_image = 1; + + win_ic_info->candidates.win = mwin__create_window (frame, win_info->client); + win_ic_info->candidates.control.as_image = 1; + + ic->info = win_ic_info; + + return 0; +} + +static void +win_destroy_ic (MInputContext *ic) +{ + MInputGUIContextInfo *win_ic_info = (MInputGUIContextInfo *) ic->info; + MInputContextInfo *ic_info = (MInputContextInfo *) win_ic_info->ic_info; + + mwin__destroy_window (win_ic_info->frame, win_ic_info->preedit.win); + mwin__destroy_window (win_ic_info->frame, win_ic_info->status.win); + mwin__destroy_window (win_ic_info->frame, win_ic_info->candidates.win); + ic->info = ic_info; + (*minput_default_driver.destroy_ic) (ic); + free (win_ic_info); +} + +static int +win_filter (MInputContext *ic, MSymbol key, void *arg) +{ + MInputGUIContextInfo *win_ic_info = (MInputGUIContextInfo *) ic->info; + MInputContextInfo *ic_info = (MInputContextInfo *) win_ic_info->ic_info; + int ret; + + if (! ic + || ! ic->active) + return 0; + + if (key == Mnil) + { + if (! arg) + return 0; + key = minput_event_to_key (win_ic_info->frame, arg); + if (key == Mnil) + return 1; + } + ic->info = ic_info; + ret = (*minput_default_driver.filter) (ic, key, arg); + ic->info = win_ic_info; + return ret; +} + +static void +adjust_window_and_draw (MFrame *frame, MInputContext *ic, MText *mt, int type) +{ + MInputGUIContextInfo *win_ic_info = (MInputGUIContextInfo *) ic->info; + MDrawControl *control; + MDrawWindow win; + MDrawMetric *geometry, physical, logical; + int xoff = win_ic_info->focus.geometry.x; + int yoff = win_ic_info->focus.geometry.y; + int x0, x1, y0, y1; + int len = mtext_nchars (mt); + + if (type == 0) + { + win = win_ic_info->preedit.win; + control = &win_ic_info->preedit.control; + geometry = &win_ic_info->preedit.geometry; + len++; + } + else if (type == 1) + { + win = win_ic_info->status.win; + control = &win_ic_info->status.control; + geometry = &win_ic_info->status.geometry; + } + else + { + win = win_ic_info->candidates.win; + control = &win_ic_info->candidates.control; + geometry = &win_ic_info->candidates.geometry; + } + + mdraw_text_extents (frame, mt, 0, len, control, &physical, &logical, NULL); + x0 = physical.x, x1 = x0 + physical.width; + y0 = physical.y, y1 = y0 + physical.height; + if (x0 > logical.x) + x0 = logical.x; + if (x1 < logical.x + logical.width) + x1 = logical.x + logical.width; + if (y0 > logical.y) + y0 = logical.y; + if (y1 < logical.y + logical.height) + y1 = logical.y + logical.height; + physical.width = x1 - x0; + physical.height = y1 - y0; + physical.x = xoff + ic->spot.x; + if (physical.x + physical.width > win_ic_info->client.geometry.width) + physical.x = win_ic_info->client.geometry.width - physical.width; + if (type == 0) + { + if (y0 > - ic->spot.ascent) + { + physical.height += y0 + ic->spot.ascent; + y0 = - ic->spot.ascent; + } + if (y1 < ic->spot.descent) + { + physical.height += ic->spot.descent - y1; + } + physical.y = yoff + ic->spot.y + y0; + } + else if (type == 1) + { + physical.y = yoff + ic->spot.y + ic->spot.descent + 2; + if (physical.y + physical.height > win_ic_info->client.geometry.height + && yoff + ic->spot.y - ic->spot.ascent - 2 - physical.height >= 0) + physical.y = yoff + ic->spot.y - ic->spot.ascent - 2 - physical.height; + } + else + { + if (win_ic_info->status.mapped) + { + /* We assume that status is already drawn. */ + if (win_ic_info->status.geometry.y < yoff + ic->spot.y) + /* As there was no lower room for status, candidates must also + be drawn upper. */ + physical.y = win_ic_info->status.geometry.y - 1 - physical.height; + else + { + /* There was a lower room for status. */ + physical.y = (win_ic_info->status.geometry.y + + win_ic_info->status.geometry.height + + 1); + if (physical.y + physical.height + > win_ic_info->client.geometry.height) + /* But not for candidates. */ + physical.y = (yoff + ic->spot.y - ic->spot.ascent - 1 + - physical.height); + } + } + else + { + physical.y = yoff + ic->spot.y + ic->spot.descent + 2; + if ((physical.y + physical.height + > win_ic_info->client.geometry.height) + && (yoff + ic->spot.y - ic->spot.ascent - 2 - physical.height + >= 0)) + physical.y = (yoff + ic->spot.y - ic->spot.ascent - 2 + - physical.height); + } + } + + mwin__adjust_window (frame, win, geometry, &physical); + mdraw_text_with_control (frame, win, -x0, -y0, mt, 0, len, control); +} + +static void +win_callback (MInputContext *ic, MSymbol command) +{ + MInputGUIContextInfo *win_ic_info = (MInputGUIContextInfo *) ic->info; + MFrame *frame = win_ic_info->frame; + + if (command == Minput_preedit_draw) + { + MText *mt; + MFace *face = mface (); + + if (! win_ic_info->preedit.mapped) + { + mwin__map_window (frame, win_ic_info->preedit.win); + win_ic_info->preedit.mapped = 1; + } + win_ic_info->preedit.control.cursor_pos = ic->cursor_pos; + if (ic->spot.fontsize) + mface_put_prop (face, Msize, (void *) ic->spot.fontsize); + mface_merge (face, mface_underline); + mtext_push_prop (ic->preedit, 0, mtext_nchars (ic->preedit), + Mface, face); + M17N_OBJECT_UNREF (face); + if (ic->im->language != Mnil) + mtext_put_prop (ic->preedit, 0, mtext_nchars (ic->preedit), Mlanguage, + ic->im->language); + if (ic->candidate_list) + mtext_push_prop (ic->preedit, ic->candidate_from, ic->candidate_to, + Mface, mface_reverse_video); + if (mtext_nchars (ic->produced) == 0) + mt = ic->preedit; + else + { + mt = mtext_dup (ic->produced); + mtext_cat (mt, ic->preedit); + win_ic_info->preedit.control.cursor_pos + += mtext_nchars (ic->produced); + } + adjust_window_and_draw (frame, ic, mt, 0); + if (ic->candidate_list) + mtext_pop_prop (ic->preedit, 0, mtext_nchars (ic->preedit), Mface); + mtext_pop_prop (ic->preedit, 0, mtext_nchars (ic->preedit), Mface); + if (mtext_nchars (ic->produced) != 0) + M17N_OBJECT_UNREF (mt); + } + else if (command == Minput_status_draw) + { + if (! win_ic_info->client.win) + return; + mtext_put_prop (ic->status, 0, mtext_nchars (ic->status), Mface, + status_face); + if (ic->im->language != Mnil) + mtext_put_prop (ic->status, 0, mtext_nchars (ic->status), Mlanguage, + ic->im->language); + adjust_window_and_draw (frame, ic, ic->status, 1); + } + else if (command == Minput_candidates_draw) + { + MPlist *group; + MText *mt; + int i, len; + int from, to; + + if (! ic->candidate_list || ! ic->candidate_show) + { + if (win_ic_info->candidates.mapped) + { + mwin__unmap_window (frame, win_ic_info->candidates.win); + win_ic_info->candidates.mapped = 0; + } + return; + } + + if (! win_ic_info->candidates.mapped) + { + mwin__map_window (frame, win_ic_info->candidates.win); + win_ic_info->candidates.mapped = 1; + } + + i = 0; + group = ic->candidate_list; + while (1) + { + if (mplist_key (group) == Mtext) + len = mtext_len (mplist_value (group)); + else + len = mplist_length (mplist_value (group)); + if (i + len > ic->candidate_index) + break; + i += len; + group = mplist_next (group); + } + + mt = mtext (); + if (mplist_key (group) == Mtext) + { + MText *candidates = (MText *) mplist_value (group); + + from = (ic->candidate_index - i) * 2 + 1; + to = from + 1; + for (i = 0; i < len; i++) + { + mtext_cat_char (mt, ' '); + mtext_cat_char (mt, mtext_ref_char (candidates, i)); + } + } + else + { + MPlist *pl; + + for (pl = (MPlist *) mplist_value (group); + i < ic->candidate_index && mplist_key (pl) != Mnil; + i++, pl = mplist_next (pl)) + { + mtext_cat_char (mt, ' '); + mtext_cat (mt, (MText *) mplist_value (pl)); + } + from = mtext_nchars (mt) + 1; + to = from + mtext_nchars ((MText *) mplist_value (pl)); + for (; mplist_key (pl) != Mnil; pl = mplist_next (pl)) + { + mtext_cat_char (mt, ' '); + mtext_cat (mt, (MText *) mplist_value (pl)); + } + } + mtext_cat_char (mt, ' '); + mtext_push_prop (mt, 0, mtext_nchars (mt), Mface, status_face); + mtext_push_prop (mt, from, to, Mface, mface_reverse_video); + if (ic->im->language != Mnil) + mtext_put_prop (mt, 0, mtext_nchars (mt), Mlanguage, ic->im->language); + adjust_window_and_draw (frame, ic, mt, 2); + M17N_OBJECT_UNREF (mt); + } + else if (command == Minput_set_spot) + { + minput__callback (ic, Minput_preedit_draw); + minput__callback (ic, Minput_status_draw); + minput__callback (ic, Minput_candidates_draw); + } + else if (command == Minput_toggle) + { + if (ic->active) + { + minput__callback (ic, Minput_preedit_done); + minput__callback (ic, Minput_status_done); + minput__callback (ic, Minput_candidates_done); + } + else + { + minput__callback (ic, Minput_preedit_start); + minput__callback (ic, Minput_status_start); + minput__callback (ic, Minput_candidates_start); + } + } + else if (command == Minput_preedit_start) + { + } + else if (command == Minput_preedit_done) + { + if (win_ic_info->preedit.mapped) + { + mwin__unmap_window (frame, win_ic_info->preedit.win); + win_ic_info->preedit.mapped = 0; + } + } + else if (command == Minput_status_start) + { + if (! win_ic_info->status.mapped) + { + mwin__map_window (frame, win_ic_info->status.win); + win_ic_info->status.mapped = 1; + } + } + else if (command == Minput_status_done) + { + if (win_ic_info->status.mapped) + { + mwin__unmap_window (frame, win_ic_info->status.win); + win_ic_info->status.mapped = 0; + } + } + else if (command == Minput_candidates_start) + { + if (! win_ic_info->candidates.mapped) + { + mwin__map_window (frame, win_ic_info->candidates.win); + win_ic_info->candidates.mapped = 1; + } + } + else if (command == Minput_candidates_done) + { + if (win_ic_info->candidates.mapped) + { + mwin__unmap_window (frame, win_ic_info->candidates.win); + win_ic_info->candidates.mapped = 0; + } + } +} + +static int +win_lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt) +{ + MInputGUIContextInfo *win_ic_info = (MInputGUIContextInfo *) ic->info; + MInputContextInfo *ic_info = (MInputContextInfo *) win_ic_info->ic_info; + int ret; + + ic->info = ic_info; + ret = (*minput_default_driver.lookup) (ic, key, arg, mt); + ic->info = win_ic_info; + return ret; +} + + + +int +minput__win_init () +{ + minput_gui_driver = minput_default_driver; + + minput_gui_driver.create_ic = win_create_ic; + minput_gui_driver.destroy_ic = win_destroy_ic; + minput_gui_driver.filter = win_filter; + minput_gui_driver.lookup = win_lookup; + { + MPlist *plist = mplist (); + + minput_gui_driver.callback_list = plist; + plist = mplist_add (plist, Minput_preedit_start, (void *) win_callback); + plist = mplist_add (plist, Minput_preedit_draw, (void *) win_callback); + plist = mplist_add (plist, Minput_preedit_done, (void *) win_callback); + plist = mplist_add (plist, Minput_status_start, (void *) win_callback); + plist = mplist_add (plist, Minput_status_draw, (void *) win_callback); + plist = mplist_add (plist, Minput_status_done, (void *) win_callback); + plist = mplist_add (plist, Minput_candidates_start, (void *) win_callback); + plist = mplist_add (plist, Minput_candidates_draw, (void *) win_callback); + plist = mplist_add (plist, Minput_candidates_done, (void *) win_callback); + plist = mplist_add (plist, Minput_set_spot, (void *) win_callback); + plist = mplist_add (plist, Minput_toggle, (void *) win_callback); + } + minput_driver = &minput_gui_driver; + + face_box_prop.width = 1; + face_box_prop.color_top = face_box_prop.color_left + = face_box_prop.color_bottom = face_box_prop.color_right + = msymbol ("black"); + face_box_prop.inner_hmargin = face_box_prop.inner_vmargin = 2; + face_box_prop.outer_hmargin = face_box_prop.outer_vmargin = 1; + status_face = mface (); + mface_put_prop (status_face, Mbox, &face_box_prop); + + return 0; +} + +void +minput__win_fini () +{ + M17N_OBJECT_UNREF (status_face); + if (minput_gui_driver.callback_list) + { + M17N_OBJECT_UNREF (minput_gui_driver.callback_list); + minput_gui_driver.callback_list = NULL; + } +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nInputMethodWin */ +/*** @{ */ + +/*=*/ +/***en + @brief Input driver for internal input methods on window systems. + + The input driver @c minput_gui_driver is for internal input + methods to be used on window systems. + + It creates sub-windows for a preedit text and a status text, and + displays them at the input spot set by the function + minput_set_spot (). + + The function m17n_initialize_win () set the variable @c + minput_driver to the pointer to this driver so that all internal + input methods use it. + + Therefore, unless @c minput_driver is changed from the default, + the driver dependent arguments to the functions whose name begin + with minput_ must are treated as follows. + + The argument $ARG of the function minput_open_im () is ignored. + + The argument $ARG of the function minput_create_ic () must be a + pointer to the structure @c MInputGUIArgIC. See the documentation + of @c MInputGUIArgIC for more detail. + + If the argument $KEY is @c Mnil, the argument $ARG of the + function minput_filter () must be a pointer to the object of type + @c XEvent. In that case, $KEY is generated from $ARG. + + The argument $ARG of the function minput_lookup () must be the + same one as that of the function minput_filter (). */ + +/***ja + @brief ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤ÎÆâÉôÆþÎϥ᥽¥Ã¥ÉÍÑÆþÎϥɥ饤¥Ð + + ÆþÎϥɥ饤¥Ð @c minput_gui_driver ¤Ï¡¢¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¾å¤ÇÍѤ¤¤é + ¤ì¤ëÆþÎϥ᥽¥Ã¥ÉÍѤΤâ¤Î¤Ç¤¢¤ë¡£ + + ¤³¤Î¥É¥é¥¤¥Ð¤Ï¡¢´Ø¿ô minput_set_spot () ¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤¿ÆþÎÏ¥¹¥Ý¥Ã + ¥È¤Ë preedit ¥Æ¥­¥¹¥ÈÍѤΥµ¥Ö¥¦¥£¥ó¥É¥¦¤È status ¥Æ¥­¥¹¥ÈÍѤΥµ¥Ö + ¥¦¥£¥ó¥É¥¦¤òºî¤ê¡¢¤½¤ì¤¾¤ì¤òɽ¼¨¤¹¤ë¡£ + + ´Ø¿ô m17n_initialize_win () ¤ÏÊÑ¿ô @c minput_driver ¤ò¤³¤Î¥É¥é¥¤¥Ð + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤ËÀßÄꤷ¡¢Á´¤Æ¤ÎÆâÉôÆþÎϥ᥽¥Ã¥É¤¬¤³¤Î¥É¥é¥¤¥Ð¤ò»È¤¦¤è + ¤¦¤Ë¤¹¤ë¡£ + + ¤·¤¿¤¬¤Ã¤Æ¡¢@c minput_driver ¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤΤޤޤǤ¢¤ì¤Ð¡¢minput_ + ¤Ç»Ï¤Þ¤ë̾Á°¤ò»ý¤Ä°Ê²¼¤Î´Ø¿ô·²¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô¤Ï¼¡¤Î¤è¤¦¤Ë + ¤Ê¤ë¡£ + + ´Ø¿ô minput_open_im () ¤Î°ú¿ô $ARG ¤Ï̵»ë¤µ¤ì¤ë¡£ + + ´Ø¿ô minput_create_ic () ¤Î°ú¿ô $ARG ¤Ï¹½Â¤ÂÎ @c MInputGUIArgIC ¤Ø + ¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï @c MInputGUIArgIC ¤Î + ¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + ´Ø¿ô minput_filter () ¤Î°ú¿ô $ARG ¤¬ @c Mnil ¤Î¾ì¹ç¡¢ $ARG ¤Ï + XEvent ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¤³¤Î¾ì¹ç $KEY + ¤Ï $ARG ¤«¤éÀ¸À®¤µ¤ì¤ë¡£ + + ´Ø¿ô minput_lookup () ¤Î°ú¿ô $ARG ¤Ï´Ø¿ô minput_filter () °ú¿ô + $ARG ¤ÈƱ¤¸¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ */ + +MInputDriver minput_gui_driver; + +/*=*/ + +/***en + @brief Convert an event to an input key. + + The minput_event_to_key () function returns the input key + corresponding to event $EVENT on $FRAME by a window system + dependent manner. + + In the m17n-X library, $EVENT must be a pointer to the struct @c + XKeyEvent, and it is handled as below. + + At first, the keysym name of $EVENT is acquired by the function @c + XKeysymToString. + + Then, the name is modified as below. + + If the name is one of "a" .. "z" and $EVENT has a Shift modifier, + the name is converted to "A" .. "Z" respectively, and the Shift + modifier is cleared. + + If the name is one byte length and $EVENT has a Control modifier, + the byte is bitwise anded by 0x1F and the Control modifier is + cleared. + + If $EVENT still has Shift, Control, Meta, Alt, Super, and/or Hyper + modifiers, the name is prepended by "S-", "C-", "M-", "A-", "s-", + and/or "H-" respectively in this order. + + For instance, if the keysym name is "a" and the event has Shift, + Meta, and Hyper modifiers, the resulting name is "H-M-A". + + At last, a symbol who has the name is returned. */ + +/***ja + @brief ¥­¡¼Ì¾¾Î¤òÆþÎÏ¥­¡¼¤ËÊÑ´¹¤¹¤ë + + ´Ø¿ô minput_name_to_key () ¤Ï¡¢Ì¾Á° $NAME ¤ËÂбþ¤¹¤ëÆþÎÏ¥­¡¼¤òÊÖ¤¹¡£ + + $NAME ¤Ï¼¡¤Î·Á¤ò¤È¤ë¡£ + + [ MODIFIER-MNEMONIC '-' ] * KEY-NAME + + ¤³¤³¤Ç MODIFIER-MNEMONIC ¤Ï 'S', 'C', 'M', 'A', 's', 'H' ¤Î¤¤¤º¤ì + ¤«¤Ç¤¢¤ê¡¢¤½¤ì¤¾¤ì Shift, Control, Meta, Alt, Super, Hyper ¤Î³Æ¥â + ¥Ç¥£¥Õ¥¡¥¤¥¢¤ò¼¨¤¹¡£KEY-NAME ¤ÏÆþÎÏ¥­¡¼¤Î¥·¥ó¥Ü¥ê¥Ã¥¯¤Ê̾Á°¤òɽ¤¹Ê¸»úÎó¡£ + + ÆþÎÏ¥­¡¼¤Î²¼ 16 ¥Ó¥Ã¥È¤Ï "keysym bits" ¤È¤è¤Ð¤ì¡¢KEY-NAME ¤ËÂбþ¤¹ + ¤ë¥³¡¼¥É¤òɽ¤¹¡£¾å7 bits (¤Ä¤Þ¤ê 17 ¥Ó¥Ã¥ÈÌܤ«¤é 23 ¥Ó¥Ã¥ÈÌܤޤÇ) + ¤Ï"modifier bits" ¤È¸Æ¤Ð¤ì¡¢MODIFIER-MNEMONIC ¤òɽ¸½¤·¤Æ¤¤¤ë¡£ + + KEY-NAME ¤¬ 1 ¥Ð¥¤¥ÈŤǤ¢¤ë¾ì¹ç¡¢ÆþÎÏ¥­¡¼¤Ï¼¡¤Î¤è¤¦¤Ë¹½À®¤µ¤ì¤ë¡£ + + ¤Þ¤º¡¢keysym bits ¤Ë¤Ï¤½¤Î¥Ð¥¤¥È¥³¡¼¥É¤½¤Î¤â¤Î¤¬ÀßÄꤵ¤ì¤ë¡£ + + ¼¡¤¤¤Ç¡¢Shift, Control, Meta ¤Î³Æ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤Ï°Ê²¼¤Î¼ê³¤­¤Ç + keysym bits ¤ËÈ¿±Ç¤µ¤ì¤ë¡£ + + (1) Shift ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤¬¤¢¤ê¡¢keysym bits ¤¬¾®Ê¸»ú¤Ç¤¢¤ì¤Ð(¤Ä¤Þ + ¤ê 'a' ¤«¤é 'z' ¤Ç¤¢¤ì¤Ð), Âçʸ»ú (¤Ä¤Þ¤ê 'A' through 'Z') ¤ËÊÑ´¹ + ¤µ¤ì¤ë¡£Shift ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤Ï modifier bits ¤«¤é¼è¤ê½ü¤«¤ì¤ë¡£ + + (2) Control ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤¬¤¢¤ì¤Ð, keysym bits ¤Ï¥Ó¥Ã¥Èñ°Ì¤Ç + 0x1F ¤È and ±é»»¤ò¹Ô¤¦¡£Control ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤Ï modifier bits + ¤«¤é¼è¤ê½ü¤«¤ì¤ë¡£ + + (3) Meta ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤¬¤¢¤ì¤Ð, keysym bits ¤Ï¥Ó¥Ã¥Èñ°Ì¤Ç 0x80 + ¤È or ±é»»¤ò¹Ô¤¦¡£ Meta ¥â¥Ç¥£¥Õ¥¡¥¤¥¢¤Ï modifier bits ¤«¤é¼è¤ê½ü + ¤«¤ì¤ë¡£ + + ¤¿¤È¤¨¤Ð¡¢"S-a" ¤È "A" ¤Ï¤É¤Á¤é¤â 65 ¤ò¡¢"C-a" ¤È "C-A" ¤Ï¤É¤Á¤é¤â + 1 ¤òÊÖ¤·¡¢"M-a" ¤Ï 225 ¤ò, "C-M-a" ¤Ï 129 ¤òÊÖ¤¹¡£ + + KEY-NAME ¤¬ 2 ¥Ð¥¤¥È°Ê¾å¤Ç¤¢¤ë»þ¤Ë¤Ï¡¢Âбþ¤¹¤ëkeysym bits ¤Ï@c + m17n @c ¥é¥¤¥Ö¥é¥ê ÆâÉô¤Î¥Æ¡¼¥Ö¥ë¤Ë¤è¤Ã¤ÆÆÀ¤ë»ö¤¬¤Ç¤­¤ë¡£¤³¤Î¤È¤­ + ¥Æ¡¼¥Ö¥ë¤Ë KEY-NAME ¤¬¤Ê¤±¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï -1 ¤òÊÖ¤¹¡£ */ + +MSymbol +minput_event_to_key (MFrame *frame, void *event) +{ + int modifiers; + MSymbol key = mwin__parse_event (frame, event, &modifiers); + char *name, *str; + + if (! modifiers) + return key; + + name = msymbol_name (key); + str = alloca (strlen (name) + 2 * 6 + 1); + str[0] = '\0'; + if (modifiers & MINPUT_KEY_SHIFT_MODIFIER) + strcat (str, "S-"); + if (modifiers & MINPUT_KEY_CONTROL_MODIFIER) + strcat (str, "C-"); + if (modifiers & MINPUT_KEY_META_MODIFIER) + strcat (str, "M-"); + if (modifiers & MINPUT_KEY_ALT_MODIFIER) + strcat (str, "A-"); + if (modifiers & MINPUT_KEY_SUPER_MODIFIER) + strcat (str, "s-"); + if (modifiers & MINPUT_KEY_HYPER_MODIFIER) + strcat (str, "H-"); + strcat (str, name); + + return msymbol (str); +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..5f1c920 --- /dev/null +++ b/src/input.c @@ -0,0 +1,2358 @@ +/* input.c -- input method module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nInputMethod + @brief API for Input method + + An input method is an object to enable inputting various + characters. An input method is identified by a pair of symbols, + LANGUAGE and NAME. This pair decides an input driver of the input + method. An input driver is a set of functions for handling the + input method. There are two kinds of input methods; internal one + and foreign one. + +
    + +
  • Internal Input Method + + An internal input method has non @c Mnil LANGUAGE, and the body is + defined in the m17n database by the tag . For this kind of input methods, the m17n library uses two + predefined input method drivers, one for CUI use and the other for + GUI use. Those driver utilize the input processing engine + provided by the m17n library itself. The m17n database may + provides an input method that is not only for a specific language. + The database uses @c Mt as the language of such an input method. + + An internal input method accepts an input key which is a symbol + associated with an input event. As there is no way for the @c + m17n @c library to know how input events are represented in an + application program, a application programmer have to convert an + input event to an input key by himself. See the documentation of + the function minput_event_to_key () for the detail. + +
  • Foreign Input Method + + A foreign input method has @c Mnil LANGUAGE, and the body is + defined in an external resources (e.g. XIM of X Window System). + For this kind of input methods, the symbol NAME must have a + property of key @c Minput_driver, and the value must be a pointer + to an input driver. So, by preparing a proper input + driver, any kind of input method can be treated in the framework + of the @c m17n @c library. + + For convenience, the m17n-X library provides an input driver that + enables the input style of OverTheSpot for XIM, and stores @c + Minput_driver property of the symbol @c Mxim with a pointer to + that driver. See the documentation of m17n GUI API for the + detail. + +
+ + PROCESSING FLOW + + The typical processing flow of handling an input method is: open + an input method, create an input context for the input method, + filter an input key, and looking up a produced text in the input + context. */ + +/*=*/ + +/***ja + @addtogroup m17nInputMethod + @brief ÆþÎϥ᥽¥Ã¥ÉÍÑAPI + + ÆþÎϥ᥽¥Ã¥É¤Ï¿ÍͤÊʸ»ú¤òÆþÎϤ¹¤ë¤¿¤á¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ë¡£ÆþÎÏ¥á + ¥½¥Ã¥É¤Ï¥·¥ó¥Ü¥ë LANGUAGE ¤È NAME ¤ÎÁȤˤè¤Ã¤Æ¼±Ê̤µ¤ì¡¢¤³¤ÎÁȤˤè¤Ã + ¤ÆÆþÎϥɥ饤¥Ð¤¬·è¤Þ¤ë¡£ÆþÎϥɥ饤¥Ð¤È¤Ï»ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤ò°·¤¦¤¿ + ¤á¤Î´Ø¿ô¤Î½¸¤Þ¤ê¤Ç¤¢¤ë¡£ + + ÆþÎϥ᥽¥Ã¥É¤Ë¤ÏÆâÉô¥á¥½¥Ã¥É¤È³°Éô¥á¥½¥Ã¥É¤ÎÆóÄ̤꤬¤¢¤ë¡£ + + ÆâÉôÆþÎϥ᥽¥Ã¥É + + ÆâÉôÆþÎϥ᥽¥Ã¥É¤Ï LANGUAGE ¤¬ @c Mnil °Ê³°¤Î¤â¤Î¤Ç¤¢¤ê¡¢ËÜÂÎ¤Ï + m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤Ë ¤È¤¤¤¦¥¿¥°ÉÕ + ¤­¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤ËÂФ·¤Æ¡¢m17n¥é¥¤¥Ö¥é¥ê¤Ë + ¤ÏCUIÍѤÈGUIÍѤ½¤ì¤¾¤ì¤ÎÆþÎϥɥ饤¥Ð¤¬¤¢¤é¤«¤¸¤á½àÈ÷¤µ¤ì¤Æ¤¤¤ë¡£¤³ + ¤ì¤é¤Î¥É¥é¥¤¥Ð¤Ïm17n¥é¥¤¥Ö¥é¥ê¼«¿È¤ÎÆþÎϽèÍý¥¨¥ó¥¸¥ó¤òÍøÍѤ¹¤ë¡£ + + ÆâÉôÆþÎϥ᥽¥Ã¥É¤Ï¡¢¥æ¡¼¥¶¤ÎÆþÎÏ¥¤¥Ù¥ó¥È¤ËÂбþ¤·¤¿ÆþÎÏ¥­¡¼¤ò¼õ¤±¼è + ¤ë¡£ÆþÎÏ¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£@c m17n @c ¥é¥¤¥Ö¥é¥ê ¤ÏÆþÎÏ¥¤¥Ù¥ó¥È + ¤¬¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¡¦¥×¥í¥°¥é¥à¤Ç¤É¤Î¤è¤¦¤Ëɽ¸½¤µ¤ì¤Æ¤¤¤ë¤«¤òÃΤë½Ñ + ¤ò»ý¤¿¤Ê¤¤¤Î¤Ç¡¢ÆþÎÏ¥¤¥Ù¥ó¥È¤«¤éÆþÎÏ¥­¡¼¤Ø¤ÎÊÑ´¹¤Ï¥×¥í¥°¥é¥Þ¤ÎÀÕǤ + ¤Ç¹Ô¤ï¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï´Ø¿ô minput_event_to_key () + ¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + ³°ÉôÆþÎϥ᥽¥Ã¥É + + ³°ÉôÆþÎϥ᥽¥Ã¥É¤Ï LANGUAGE ¤¬ @c Mnil ¤Î¤â¤Î¤Ç¤¢¤ê¡¢¤ÇËÜÂΤϳ°Éô + ¤Î¥ê¥½¡¼¥¹¤È¤·¤ÆÄêµÁ¤µ¤ì¤ë¡£¡Ê¤¿¤È¤¨¤ÐX Window System ¤ÎXIM ¤Ê¤É¡£) + ¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤Ç¤Ï¥·¥ó¥Ü¥ë NAME ¤Ï@c Minput_driver ¤ò¥­¡¼¤È + ¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤÏÆþÎϥɥ饤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê + ¤é¤Ê¤¤¡£¤·¤¿¤¬¤Ã¤Æ¡¢Å¬ÀÚ¤ÊÆþÎϥɥ饤¥Ð¤ò½àÈ÷¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢¤¤¤« + ¤Ê¤ëÆþÎϥ᥽¥Ã¥É¤â @c m17n @c ¥é¥¤¥Ö¥é¥ê¤ÎÏÈÁȤÎÃæ¤Ç°·¤¦»ö¤¬¤Ç¤­¤ë¡£ + + ´Êñ¤Î¤¿¤á¡¢m17n X ¥é¥¤¥Ö¥é¥ê¤ÏXIM ¤Î OverTheSpot ¤ÎÆþÎÏ¥¹¥¿¥¤¥ë¤ò + ¼Â¸½¤¹¤ëÆþÎϥɥ饤¥Ð¤òÄ󶡤·¤Æ¤¤¤ë¡£¤Þ¤¿¥·¥ó¥Ü¥ë @c Mxim ¤Î @c + Minput_driver ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¤½¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÝ»ý¤· + ¤Æ¤¤¤ë¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ïm17n-win API ¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + ½èÍý¤Îή¤ì + + ÆþÎϥ᥽¥Ã¥É½èÍý¤Îŵ·¿Åª¤Ê½èÍý¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£ÆþÎϥ᥽¥Ã¥É¤Î¥ª¡¼ + ¥×¥ó¡¢¤½¤ÎÆþÎϥ᥽¥Ã¥É¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ÎÀ¸À®¡¢ÆþÎÏ¥¤¥Ù¥ó¥È¤Î¥Õ¥£ + ¥ë¥¿¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ç¤ÎÀ¸À®¥Æ¥­¥¹¥È¤Î¸¡º÷¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include + +#include "config.h" +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "mtext.h" +#include "input.h" +#include "symbol.h" +#include "plist.h" + +static MSymbol Minput_method; + +/** Symbols to load an input method data. */ +static MSymbol Mtitle, Mmacro, Mmodule, Mstate; + +/** Symbols for actions. */ +static MSymbol Minsert, Mdelete, Mmark, Mmove, Mpushback, Mundo, Mcall, Mshift; +static MSymbol Mselect, Mshow, Mhide; +static MSymbol Mset, Madd, Msub, Mmul, Mdiv, Mequal, Mless, Mgreater; + +static MSymbol Mcandidate_list, Mcandidate_index; + +static MSymbol Minit, Mfini; + +/** Symbols for key events. */ +static MSymbol one_char_symbol[256]; + +/** Structure to hold a map. */ + +struct MIMMap +{ + /** List of actions to take when we reach the map. In a root map, + the actions are executed only when there's no more key. */ + MPlist *map_actions; + + /** List of deeper maps. If NULL, this is a terminal map. */ + MPlist *submaps; + + /** List of actions to take when we leave the map successfully. In + a root map, the actions are executed only when none of submaps + handle the current key. */ + MPlist *branch_actions; +}; + +typedef MPlist *(*MIMExternalFunc) (MPlist *plist); + +typedef struct +{ + void *handle; + MPlist *func_list; /* function name vs (MIMExternalFunc *) */ +} MIMExternalModule; + +struct MIMState +{ + /** Name of the state. */ + MSymbol name; + + /** Title of the state, or NULL. */ + MText *title; + + /** Key translation map of the state. Built by merging all maps of + branches. */ + MIMMap *map; +}; + + +static int +marker_code (MSymbol sym) +{ + char *name; + + if (sym == Mnil) + return -1; + name = MSYMBOL_NAME (sym); + return ((name[0] == '@' + && ((name[1] >= '0' && name[1] <= '9') + || name[1] == '<' || name[1] == '>' + || name[1] == '=' || name[1] == '+' || name[1] == '-' + || name[1] == '[' || name[1] == ']') + && name[2] == '\0') + ? name[1] : -1); +} + +int +integer_value (MInputContext *ic, MPlist *arg) +{ + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + int code; + MText *preedit = ic->preedit; + int len = mtext_nbytes (preedit); + + if (MPLIST_INTEGER_P (arg)) + return MPLIST_INTEGER (arg); + code = marker_code (MPLIST_SYMBOL (arg)); + if (code < 0) + return (int) mplist_get (ic_info->vars, MPLIST_SYMBOL (arg)); + if (code >= '0' && code <= '9') + code -= '0'; + else if (code == '=') + code = ic->cursor_pos; + else if (code == '-' || code == '[') + code = ic->cursor_pos - 1; + else if (code == '+' || code == ']') + code = ic->cursor_pos + 1; + else if (code == '<') + code = 0; + else if (code == '<') + code = len; + return (code >= 0 && code < len ? mtext_ref_char (preedit, code) : -1); +} + + +/* Parse PLIST as an action list while modifying the list to regularize + actions. PLIST should have this form: + PLIST ::= ( (ACTION-NAME ACTION-ARG *) *). + Return 0 if successfully parsed, otherwise return -1. */ + +static int +parse_action_list (MPlist *plist, MPlist *macros) +{ + MPLIST_DO (plist, plist) + { + if (MPLIST_MTEXT_P (plist)) + { + /* This is a short form of (insert MTEXT). */ + /* if (mtext_nchars (MPLIST_MTEXT (plist)) == 0) + MERROR (MERROR_IM, -1); */ + } + else if (MPLIST_PLIST_P (plist) + && (MPLIST_MTEXT_P (MPLIST_PLIST (plist)) + || MPLIST_PLIST_P (MPLIST_PLIST (plist)))) + { + MPlist *pl; + + /* This is a short form of (insert (GROUPS *)). */ + MPLIST_DO (pl, MPLIST_PLIST (plist)) + { + if (MPLIST_PLIST_P (pl)) + { + MPlist *elt; + + MPLIST_DO (elt, MPLIST_PLIST (pl)) + if (! MPLIST_MTEXT_P (elt) + || mtext_nchars (MPLIST_MTEXT (elt)) == 0) + MERROR (MERROR_IM, -1); + } + else + { + if (! MPLIST_MTEXT_P (pl) + || mtext_nchars (MPLIST_MTEXT (pl)) == 0) + MERROR (MERROR_IM, -1); + } + } + } + else if (MPLIST_INTEGER_P (plist)) + { + int c = MPLIST_INTEGER (plist); + + if (c < 0 || c > MCHAR_MAX) + MERROR (MERROR_IM, -1); + } + else if (MPLIST_PLIST_P (plist) + && MPLIST_SYMBOL_P (MPLIST_PLIST (plist))) + { + MPlist *pl = MPLIST_PLIST (plist); + MSymbol action_name = MPLIST_SYMBOL (pl); + + pl = MPLIST_NEXT (pl); + + if (action_name == Minsert) + { + if (MPLIST_MTEXT_P (pl)) + { + if (mtext_nchars (MPLIST_MTEXT (pl)) == 0) + MERROR (MERROR_IM, -1); + } + else if (MPLIST_PLIST_P (pl)) + { + MPLIST_DO (pl, pl) + { + if (MPLIST_PLIST_P (pl)) + { + MPlist *elt; + + MPLIST_DO (elt, MPLIST_PLIST (pl)) + if (! MPLIST_MTEXT_P (elt) + || mtext_nchars (MPLIST_MTEXT (elt)) == 0) + MERROR (MERROR_IM, -1); + } + else + { + if (! MPLIST_MTEXT_P (pl) + || mtext_nchars (MPLIST_MTEXT (pl)) == 0) + MERROR (MERROR_IM, -1); + } + } + } + else if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mselect + || action_name == Mdelete + || action_name == Mmove) + { + if (! MPLIST_SYMBOL_P (pl) + && ! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mmark + || action_name == Mcall + || action_name == Mshift) + { + if (! MPLIST_SYMBOL_P (pl)) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mshow || action_name == Mhide + || action_name == Mundo) + { + if (! MPLIST_TAIL_P (pl)) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mpushback) + { + if (! MPLIST_INTEGER_P (pl)) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mset || action_name == Madd + || action_name == Msub || action_name == Mmul + || action_name == Mdiv) + { + if (! (MPLIST_SYMBOL_P (pl) + && (MPLIST_INTEGER_P (MPLIST_NEXT (pl)) + || MPLIST_SYMBOL_P (MPLIST_NEXT (pl))))) + MERROR (MERROR_IM, -1); + } + else if (action_name == Mequal || action_name == Mless + || action_name == Mgreater) + { + if (! ((MPLIST_INTEGER_P (pl) || MPLIST_SYMBOL_P (pl)) + && (MPLIST_INTEGER_P (MPLIST_NEXT (pl)) + || MPLIST_SYMBOL_P (MPLIST_NEXT (pl))))) + MERROR (MERROR_IM, -1); + pl = MPLIST_NEXT (MPLIST_NEXT (pl)); + if (! MPLIST_PLIST_P (pl)) + MERROR (MERROR_IM, -1); + if (parse_action_list (MPLIST_PLIST (pl), macros) < 0) + MERROR (MERROR_IM, -1); + pl = MPLIST_NEXT (pl); + if (MPLIST_PLIST_P (pl) + && parse_action_list (MPLIST_PLIST (pl), macros) < 0) + MERROR (MERROR_IM, -1); + } + else if (! macros || ! mplist_get (macros, action_name)) + MERROR (MERROR_IM, -1); + } + else + MERROR (MERROR_IM, -1); + } + + return 0; +} + + +/* Load a translation into MAP from PLIST. + PLIST has this form: + PLIST ::= ( KEYSEQ MAP-ACTION * ) */ + +static int +load_translation (MIMMap *map, MPlist *plist, MPlist *branch_actions, + MPlist *macros) +{ + MSymbol *keyseq; + int len, i; + + if (MPLIST_MTEXT_P (plist)) + { + MText *mt = MPLIST_MTEXT (plist); + + len = mtext_nchars (mt); + if (len == 0 || len != mtext_nbytes (mt)) + MERROR (MERROR_IM, -1); + keyseq = (MSymbol *) alloca (sizeof (MSymbol) * len); + for (i = 0; i < len; i++) + keyseq[i] = one_char_symbol[MTEXT_DATA (mt)[i]]; + } + else if (MPLIST_PLIST_P (plist)) + { + MPlist *elt = MPLIST_PLIST (plist); + + len = MPLIST_LENGTH (elt); + if (len == 0) + MERROR (MERROR_IM, -1); + keyseq = (MSymbol *) alloca (sizeof (int) * len); + for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt)) + { + if (MPLIST_INTEGER_P (elt)) + { + int c = MPLIST_INTEGER (elt); + + if (c < 0 || c >= 0x100) + MERROR (MERROR_IM, -1); + keyseq[i] = one_char_symbol[c]; + } + else if (MPLIST_SYMBOL_P (elt)) + keyseq[i] = MPLIST_SYMBOL (elt); + else + MERROR (MERROR_IM, -1); + } + } + else + MERROR (MERROR_IM, -1); + + for (i = 0; i < len; i++) + { + MIMMap *deeper = NULL; + + if (map->submaps) + deeper = mplist_get (map->submaps, keyseq[i]); + else + map->submaps = mplist (); + if (! deeper) + { + /* Fixme: It is better to make all deeper maps at once. */ + MSTRUCT_CALLOC (deeper, MERROR_IM); + mplist_put (map->submaps, keyseq[i], deeper); + } + map = deeper; + } + + /* We reach a terminal map. */ + if (map->map_actions + || map->branch_actions) + /* This map is already defined. We avoid overriding it. */ + return 0; + + plist = MPLIST_NEXT (plist); + if (! MPLIST_TAIL_P (plist)) + { + if (parse_action_list (plist, macros) < 0) + MERROR (MERROR_IM, -1); + map->map_actions = plist; + M17N_OBJECT_REF (plist); + } + if (branch_actions) + { + map->branch_actions = branch_actions; + M17N_OBJECT_REF (branch_actions); + } + + return 0; +} + +/* Load a branch from PLIST into MAP. PLIST has this form: + PLIST ::= ( MAP-NAME BRANCH-ACTION * ) + MAPS is a plist of raw maps. + STATE is the current state. */ + +static int +load_branch (MPlist *plist, MPlist *maps, MIMMap *map, MPlist *macros) +{ + MSymbol map_name; + MPlist *branch_actions; + + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_IM, -1); + map_name = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (MPLIST_TAIL_P (plist)) + branch_actions = NULL; + else if (parse_action_list (plist, macros) < 0) + MERROR (MERROR_IM, -1); + else + branch_actions = plist; + if (map_name == Mnil) + { + map->branch_actions = branch_actions; + if (branch_actions) + M17N_OBJECT_REF (branch_actions); + } + else if (map_name == Mt) + { + map->map_actions = branch_actions; + if (branch_actions) + M17N_OBJECT_REF (branch_actions); + } + else + { + plist = (MPlist *) mplist_get (maps, map_name); + if (! plist || ! MPLIST_PLIST_P (plist)) + MERROR (MERROR_IM, -1); + MPLIST_DO (plist, plist) + if (! MPLIST_PLIST_P (plist) + || (load_translation (map, MPLIST_PLIST (plist), branch_actions, + macros) + < 0)) + MERROR (MERROR_IM, -1); + } + + return 0; +} + +/* Load a macro from PLIST into MACROS. + PLIST has this from: + PLIST ::= ( MACRO-NAME ACTION * ) + MACROS is a plist of macro names vs action list. */ +static int +load_macros (MPlist *plist, MPlist *macros) +{ + MSymbol name; + + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_IM, -1); + name = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (MPLIST_TAIL_P (plist) + || parse_action_list (plist, macros) < 0) + MERROR (MERROR_IM, -1); + mplist_put (macros, name, plist); + M17N_OBJECT_REF (plist); + return 0; +} + +/* Load an external module from PLIST into EXTERNALS. + PLIST has this form: + PLIST ::= ( MODULE-NAME FUNCTION * ) + EXTERNALS is a plist of MODULE-NAME vs (MIMExternalModule *). */ + +#ifndef DLOPEN_SHLIB_EXT +#define DLOPEN_SHLIB_EXT ".so" +#endif + +static int +load_external_module (MPlist *plist, MPlist *externals) +{ + void *handle; + MSymbol module; + char *module_file; + MIMExternalModule *external; + MPlist *func_list; + void *func; + + if (MPLIST_MTEXT_P (plist)) + module = msymbol ((char *) MTEXT_DATA (MPLIST_MTEXT (plist))); + else if (MPLIST_SYMBOL_P (plist)) + module = MPLIST_SYMBOL (plist); + module_file = alloca (strlen (MSYMBOL_NAME (module)) + + strlen (DLOPEN_SHLIB_EXT) + 1); + sprintf (module_file, "%s%s", MSYMBOL_NAME (module), DLOPEN_SHLIB_EXT); + + handle = dlopen (module_file, RTLD_NOW); + if (! handle) + { + fprintf (stderr, "%s\n", dlerror ()); + MERROR (MERROR_IM, -1); + } + func_list = mplist (); + MPLIST_DO (plist, MPLIST_NEXT (plist)) + { + if (! MPLIST_SYMBOL_P (plist)) + MERROR_GOTO (MERROR_IM, err_label); + func = dlsym (handle, MSYMBOL_NAME (MPLIST_SYMBOL (plist))); + if (! func) + MERROR_GOTO (MERROR_IM, err_label); + mplist_add (func_list, MPLIST_SYMBOL (plist), func); + } + + MSTRUCT_MALLOC (external, MERROR_IM); + external->handle = handle; + external->func_list = func_list; + mplist_add (externals, module, external); + return 0; + + err_label: + dlclose (handle); + M17N_OBJECT_UNREF (func_list); + return -1; +} + + +/** Load a state from PLIST into a newly allocated state object. + PLIST has this form: + PLIST ::= ( STATE-NAME STATE-TITLE ? BRANCH * ) + BRANCH ::= ( MAP-NAME BRANCH-ACTION * ) + MAPS is a plist of defined maps. + Return the state object. */ + +static MIMState * +load_state (MPlist *plist, MPlist *maps, MSymbol language, MPlist *macros) +{ + MIMState *state; + + MSTRUCT_CALLOC (state, MERROR_IM); + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_IM, NULL); + state->name = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + if (MPLIST_MTEXT_P (plist)) + { + state->title = MPLIST_MTEXT (plist); + mtext_put_prop (state->title, 0, mtext_nchars (state->title), + Mlanguage, language); + M17N_OBJECT_REF (state->title); + plist = MPLIST_NEXT (plist); + } + MSTRUCT_CALLOC (state->map, MERROR_IM); + MPLIST_DO (plist, plist) + if (! MPLIST_PLIST_P (plist) + || load_branch (MPLIST_PLIST (plist), maps, state->map, macros) < 0) + MERROR (MERROR_IM, NULL); + return state; +} + + +static void +free_map (MIMMap *map) +{ + MPlist *plist; + + M17N_OBJECT_UNREF (map->map_actions); + if (map->submaps) + { + MPLIST_DO (plist, map->submaps) + free_map ((MIMMap *) MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (map->submaps); + } + M17N_OBJECT_UNREF (map->branch_actions); + free (map); +} + +/* Load an input method from PLIST into IM_INTO, and return it. */ + +static int +load_input_method (MSymbol language, MSymbol name, MPlist *plist, + MInputMethodInfo *im_info) +{ + MText *title = NULL; + MPlist *maps = NULL; + MPlist *states = NULL; + MPlist *externals = NULL; + MPlist *macros = NULL; + MPlist *elt; + + if (! MPLIST_PLIST_P (plist)) + MERROR (MERROR_IM, -1); + for (; MPLIST_PLIST_P (plist); plist = MPLIST_NEXT (plist)) + { + elt = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (elt)) + MERROR_GOTO (MERROR_IM, err); + if (MPLIST_SYMBOL (elt) == Mtitle) + { + elt = MPLIST_NEXT (elt); + if (MPLIST_MTEXT_P (elt)) + { + title = MPLIST_MTEXT (elt); + M17N_OBJECT_REF (title); + } + else + MERROR_GOTO (MERROR_IM, err); + } + else if (MPLIST_SYMBOL (elt) == Mmap) + { + maps = mplist__from_alist (MPLIST_NEXT (elt)); + if (! maps) + MERROR_GOTO (MERROR_IM, err); + } + else if (MPLIST_SYMBOL (elt) == Mmacro) + { + macros = mplist (); + MPLIST_DO (elt, MPLIST_NEXT (elt)) + { + if (! MPLIST_PLIST_P (elt) + || load_macros (MPLIST_PLIST (elt), macros) < 0) + MERROR_GOTO (MERROR_IM, err); + } + } + else if (MPLIST_SYMBOL (elt) == Mmodule) + { + externals = mplist (); + MPLIST_DO (elt, MPLIST_NEXT (elt)) + { + if (! MPLIST_PLIST_P (elt) + || load_external_module (MPLIST_PLIST (elt), externals) < 0) + MERROR_GOTO (MERROR_IM, err); + } + } + else if (MPLIST_SYMBOL (elt) == Mstate) + { + states = mplist (); + MPLIST_DO (elt, MPLIST_NEXT (elt)) + { + MIMState *state; + + if (! MPLIST_PLIST_P (elt)) + MERROR_GOTO (MERROR_IM, err); + state = load_state (MPLIST_PLIST (elt), maps, language, macros); + if (! state) + MERROR_GOTO (MERROR_IM, err); + mplist_put (states, state->name, state); + } + } + } + + MPLIST_DO (elt, maps) + M17N_OBJECT_UNREF (MPLIST_VAL (elt)); + M17N_OBJECT_UNREF (maps); + im_info->title = title; + im_info->externals = externals; + im_info->macros = macros; + im_info->states = states; + return 0; + + err: + if (maps) + { + MPLIST_DO (elt, maps) + M17N_OBJECT_UNREF (MPLIST_VAL (elt)); + M17N_OBJECT_UNREF (maps); + } + if (title) + M17N_OBJECT_UNREF (title); + if (states) + { + MPLIST_DO (plist, states) + { + MIMState *state = (MIMState *) MPLIST_VAL (plist); + + if (state->title) + M17N_OBJECT_UNREF (state->title); + if (state->map) + free_map (state->map); + free (state); + } + M17N_OBJECT_UNREF (states); + } + if (externals) + { + MPLIST_DO (plist, externals) + { + MIMExternalModule *external = MPLIST_VAL (plist); + + dlclose (external->handle); + M17N_OBJECT_UNREF (external->func_list); + free (external); + MPLIST_KEY (plist) = Mt; + } + M17N_OBJECT_UNREF (externals); + } + return -1; +} + + + +static int take_action_list (MInputContext *ic, MPlist *action_list); + +static void +shift_state (MInputContext *ic, MSymbol state_name) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + MIMState *state = ic_info->state; + + /* Find a state to shift to. If not found, shift to the initial + state. */ + state = (MIMState *) mplist_get (im_info->states, state_name); + if (! state) + state = (MIMState *) MPLIST_VAL (im_info->states); + + /* Enter the new state. */ + ic_info->state = state; + ic_info->map = state->map; + ic_info->state_key_head = ic_info->key_head; + if (state == (MIMState *) MPLIST_VAL (im_info->states)) + { + /* We have shifted to the initial state. */ + MPlist *p; + + mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit), + Mcandidate_list, NULL, 0); + mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit), + Mcandidate_index, NULL, 0); + mtext_cat (ic->produced, ic->preedit); + mtext_reset (ic->preedit); + ic->candidate_list = NULL; + ic->candidate_show = 0; + ic->preedit_changed = ic->candidates_changed = 1; + MPLIST_DO (p, ic_info->markers) + MPLIST_VAL (p) = 0; + MPLIST_DO (p, ic_info->vars) + MPLIST_VAL (p) = 0; + ic->cursor_pos = 0; + memmove (ic_info->keys, ic_info->keys + ic_info->state_key_head, + sizeof (int) * (ic_info->used - ic_info->state_key_head)); + ic_info->used -= ic_info->state_key_head; + ic_info->state_key_head = ic_info->key_head = 0; + } + mtext_cpy (ic_info->preedit_saved, ic->preedit); + ic_info->state_pos = ic->cursor_pos; + ic->status = state->title; + if (! ic->status) + ic->status = im_info->title; + ic->status_changed = 1; + if (ic_info->key_head == ic_info->used + && ic_info->map == ic_info->state->map + && ic_info->map->map_actions) + take_action_list (ic, ic_info->map->map_actions); +} + + +static MPlist * +find_candidates_group (MPlist *plist, int index, + int *start_index, int *end_index, int *group_index) +{ + int i = 0, gidx = 0, len; + + MPLIST_DO (plist, plist) + { + if (MPLIST_MTEXT_P (plist)) + len = mtext_nchars (MPLIST_MTEXT (plist)); + else + len = mplist_length (MPLIST_PLIST (plist)); + if (i + len > index) + { + if (start_index) + *start_index = i; + if (end_index) + *end_index = i + len; + if (group_index) + *group_index = gidx; + return plist; + } + i += len; + gidx++; + } + return NULL; +} + +static void +preedit_insert (MInputContext *ic, int pos, MText *mt, int c) +{ + MInputContextInfo *ic_info = ((MInputContext *) ic)->info; + MPlist *markers; + int nchars = mt ? mtext_nchars (mt) : 1; + + if (mt) + mtext_ins (ic->preedit, pos, mt); + else + mtext_ins_char (ic->preedit, pos, c, 1); + MPLIST_DO (markers, ic_info->markers) + if (MPLIST_INTEGER (markers) > pos) + MPLIST_VAL (markers) = (void *) (MPLIST_INTEGER (markers) + nchars); + if (ic->cursor_pos >= pos) + ic->cursor_pos += nchars; + ic->preedit_changed = 1; +} + + +static void +preedit_delete (MInputContext *ic, int from, int to) +{ + MInputContextInfo *ic_info = ((MInputContext *) ic)->info; + MPlist *markers; + + mtext_del (ic->preedit, from, to); + MPLIST_DO (markers, ic_info->markers) + { + if (MPLIST_INTEGER (markers) > to) + MPLIST_VAL (markers) + = (void *) (MPLIST_INTEGER (markers) - (to - from)); + else if (MPLIST_INTEGER (markers) > from); + MPLIST_VAL (markers) = (void *) from; + } + if (ic->cursor_pos >= to) + ic->cursor_pos -= to - from; + else if (ic->cursor_pos > from) + ic->cursor_pos = from; + ic->preedit_changed = 1; +} + + +static int +new_index (MInputContext *ic, int current, int limit, MSymbol sym, MText *mt) +{ + int code = marker_code (sym); + + if (mt && (code == '[' || code == ']')) + { + int pos = current; + + if (code == '[' && current > 0) + { + if (mtext_prop_range (mt, Mcandidate_list, pos - 1, &pos, NULL, 1) + && pos > 0) + current = pos; + } + else if (code == ']' && current < mtext_nchars (mt)) + { + if (mtext_prop_range (mt, Mcandidate_list, pos, NULL, &pos, 1)) + current = pos; + } + return current; + } + if (code >= 0) + return (code == '<' ? 0 + : code == '>' ? limit + : code == '-' ? current - 1 + : code == '+' ? current + 1 + : code == '=' ? current + : code - '0' > limit ? limit + : code - '0'); + if (! ic) + return 0; + return (int) mplist_get (((MInputContextInfo *) ic->info)->markers, sym); +} + +static void +udpate_candidate (MInputContext *ic, MTextProperty *prop, int idx) +{ + int from = mtext_property_start (prop); + int to = mtext_property_end (prop); + int start; + MPlist *candidate_list = mtext_property_value (prop); + MPlist *group = find_candidates_group (candidate_list, idx, &start, + NULL, NULL); + int ingroup_index = idx - start; + MText *mt; + + preedit_delete (ic, from, to); + if (MPLIST_MTEXT_P (group)) + { + mt = MPLIST_MTEXT (group); + preedit_insert (ic, from, NULL, mtext_ref_char (mt, ingroup_index)); + to = from + 1; + } + else + { + int i; + MPlist *plist; + + for (i = 0, plist = MPLIST_PLIST (group); i < ingroup_index; + i++, plist = MPLIST_NEXT (plist)); + mt = MPLIST_MTEXT (plist); + preedit_insert (ic, from, mt, 0); + to = from + mtext_nchars (mt); + } + mtext_put_prop (ic->preedit, from, to, Mcandidate_list, candidate_list); + mtext_put_prop (ic->preedit, from, to, Mcandidate_index, (void *) idx); + ic->cursor_pos = to; +} + + +static int +take_action_list (MInputContext *ic, MPlist *action_list) +{ + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + MPlist *candidate_list = ic->candidate_list; + int candidate_index = ic->candidate_index; + int candidate_show = ic->candidate_show; + MTextProperty *prop; + + MPLIST_DO (action_list, action_list) + { + MPlist *action; + MSymbol name; + MPlist *args; + + if (MPLIST_MTEXT_P (action_list) + || MPLIST_INTEGER_P (action_list)) + name = Minsert, args = action_list; + else if (MPLIST_PLIST_P (action_list) + && (MPLIST_MTEXT_P (MPLIST_PLIST (action_list)) + || MPLIST_PLIST_P (MPLIST_PLIST (action_list)))) + name = Minsert, args = action_list; + else + { + action = MPLIST_PLIST (action_list); + name = MPLIST_SYMBOL (action); + args = MPLIST_NEXT (action); + } + + if (name == Minsert) + { + if (MPLIST_MTEXT_P (args)) + preedit_insert (ic, ic->cursor_pos, MPLIST_MTEXT (args), 0); + else if (MPLIST_INTEGER_P (args)) + preedit_insert (ic, ic->cursor_pos, NULL, MPLIST_INTEGER (args)); + else if (MPLIST_SYMBOL_P (args)) + { + int c = integer_value (ic, args); + + if (c >= 0 && c <= MCHAR_MAX) + preedit_insert (ic, ic->cursor_pos, NULL, c); + } + else + { + MText *mt; + int len; + + args = MPLIST_PLIST (args); + if (MPLIST_MTEXT_P (args)) + { + preedit_insert (ic, ic->cursor_pos, NULL, + mtext_ref_char (MPLIST_MTEXT (args), 0)); + len = 1; + } + else + { + mt = MPLIST_MTEXT (MPLIST_PLIST (args)); + preedit_insert (ic, ic->cursor_pos, mt, 0); + len = mtext_nchars (mt); + } + mtext_put_prop (ic->preedit, + ic->cursor_pos - len, ic->cursor_pos, + Mcandidate_list, args); + mtext_put_prop (ic->preedit, + ic->cursor_pos - len, ic->cursor_pos, + Mcandidate_index, (void *) 0); + } + } + else if (name == Mselect) + { + int start, end; + int code, idx, gindex; + int pos = ic->cursor_pos; + MPlist *group; + + if (pos == 0 + || ! (prop = mtext_get_property (ic->preedit, pos - 1, + Mcandidate_list))) + continue; + if (MPLIST_SYMBOL_P (args)) + { + code = marker_code (MPLIST_SYMBOL (args)); + if (code < 0) + continue; + } + else + code = -1; + idx = (int) mtext_get_prop (ic->preedit, pos - 1, Mcandidate_index); + group = find_candidates_group (mtext_property_value (prop), idx, + &start, &end, &gindex); + + if (code != '[' && code != ']') + { + idx = (start + + (code >= 0 + ? new_index (NULL, ic->candidate_index - start, + end - start - 1, MPLIST_SYMBOL (args), + NULL) + : MPLIST_INTEGER (args))); + if (idx < 0 + || (idx >= end + && MPLIST_TAIL_P (MPLIST_NEXT (group)))) + idx = 0; + } + else + { + int ingroup_index = idx - start; + int len; + + group = mtext_property_value (prop); + len = mplist_length (group); + if (code == '[') + { + gindex--; + if (gindex < 0) + gindex = len - 1;; + } + else + { + gindex++; + if (gindex >= len) + gindex = 0; + } + for (idx = 0; gindex > 0; gindex--, group = MPLIST_NEXT (group)) + idx += (MPLIST_MTEXT_P (group) + ? mtext_nchars (MPLIST_MTEXT (group)) + : mplist_length (MPLIST_PLIST (group))); + len = (MPLIST_MTEXT_P (group) + ? mtext_nchars (MPLIST_MTEXT (group)) + : mplist_length (MPLIST_PLIST (group))); + if (ingroup_index >= len) + ingroup_index = len - 1; + idx += ingroup_index; + } + udpate_candidate (ic, prop, idx); + } + else if (name == Mshow) + ic->candidate_show = 1; + else if (name == Mhide) + ic->candidate_show = 0; + else if (name == Mdelete) + { + int len = mtext_nchars (ic->preedit); + int to = (MPLIST_SYMBOL_P (args) + ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args), + ic->preedit) + : MPLIST_INTEGER (args)); + + if (to < 0) + to = 0; + else if (to > len) + to = len; + if (to < ic->cursor_pos) + preedit_delete (ic, to, ic->cursor_pos); + else if (to > ic->cursor_pos) + preedit_delete (ic, ic->cursor_pos, to); + } + else if (name == Mmove) + { + int len = mtext_nchars (ic->preedit); + int pos + = (MPLIST_SYMBOL_P (args) + ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args), + ic->preedit) + : MPLIST_INTEGER (args)); + + if (pos < 0) + pos = 0; + else if (pos > len) + pos = len; + if (pos != ic->cursor_pos) + { + ic->cursor_pos = pos; + ic->preedit_changed = 1; + } + } + else if (name == Mmark) + { + int code = marker_code (MPLIST_SYMBOL (args)); + + if (code < 0) + mplist_put (ic_info->markers, MPLIST_SYMBOL (args), + (void *) ic->cursor_pos); + } + else if (name == Mpushback) + { + int num = MPLIST_INTEGER (args); + + if (num > 0) + ic_info->key_head -= num; + else + ic_info->key_head = num; + if (ic_info->key_head > ic_info->used) + ic_info->key_head = ic_info->used; + } + else if (name == Mcall) + { + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MIMExternalFunc func = NULL; + MSymbol module, func_name; + MPlist *func_args, *val; + int ret = 0; + + module = MPLIST_SYMBOL (args); + args = MPLIST_NEXT (args); + func_name = MPLIST_SYMBOL (args); + + if (im_info->externals) + { + MIMExternalModule *external + = (MIMExternalModule *) mplist_get (im_info->externals, + module); + if (external) + func = (MIMExternalFunc) mplist_get (external->func_list, + func_name); + } + if (! func) + continue; + func_args = mplist (); + mplist_add (func_args, Mt, ic); + MPLIST_DO (args, MPLIST_NEXT (args)) + { + int code; + + if (MPLIST_KEY (args) == Msymbol + && MPLIST_KEY (args) != Mnil + && (code = marker_code (MPLIST_SYMBOL (args))) >= 0) + { + code = new_index (ic, ic->cursor_pos, + mtext_nchars (ic->preedit), + MPLIST_SYMBOL (args), ic->preedit); + mplist_add (func_args, Minteger, (void *) code); + } + else + mplist_add (func_args, MPLIST_KEY (args), MPLIST_VAL (args)); + } + val = (func) (func_args); + M17N_OBJECT_UNREF (func_args); + if (val && ! MPLIST_TAIL_P (val)) + ret = take_action_list (ic, val); + M17N_OBJECT_UNREF (val); + if (ret < 0) + return ret; + } + else if (name == Mshift) + { + shift_state (ic, MPLIST_SYMBOL (args)); + } + else if (name == Mundo) + { + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + int unhandle = 0; + + mtext_reset (ic->preedit); + mtext_reset (ic_info->preedit_saved); + ic->cursor_pos = ic_info->state_pos = 0; + ic_info->state_key_head = ic_info->key_head = 0; + ic_info->used -= 2; + if (ic_info->used < 0) + { + ic_info->used = 0; + unhandle = 1; + } + shift_state (ic, ((MIMState *) MPLIST_VAL (im_info->states))->name); + if (unhandle) + return -1; + break; + } + else if (name == Mset || name == Madd || name == Msub + || name == Mmul || name == Mdiv) + { + MSymbol sym = MPLIST_SYMBOL (args); + int val1 = (int) mplist_get (ic_info->vars, sym), val2; + + args = MPLIST_NEXT (args); + val2 = integer_value (ic, args); + if (name == Mset) + val1 = val2; + else if (name == Madd) + val1 += val2; + else if (name == Msub) + val1 -= val2; + else if (name == Mmul) + val1 *= val2; + else + val1 /= val2; + mplist_put (ic_info->vars, sym, (void *) val1); + } + else if (name == Mequal || name == Mless || name == Mgreater) + { + int val1, val2; + MPlist *actions1, *actions2; + int ret; + + val1 = integer_value (ic, args); + args = MPLIST_NEXT (args); + val2 = integer_value (ic, args); + args = MPLIST_NEXT (args); + actions1 = MPLIST_PLIST (args); + args = MPLIST_NEXT (args); + if (MPLIST_TAIL_P (args)) + actions2 = NULL; + else + actions2 = MPLIST_PLIST (args); + if (name == Mequal ? val1 == val2 + : name == Mless ? val1 < val2 + : val1 > val2) + ret = take_action_list (ic, actions1); + else if (actions2) + ret = take_action_list (ic, actions2); + if (ret < 0) + return ret; + } + else + { + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MPlist *actions; + + if (im_info->macros + && (actions = mplist_get (im_info->macros, name))) + { + if (take_action_list (ic, actions) < 0) + return -1; + }; + } + } + + prop = NULL; + ic->candidate_list = NULL; + if (ic->cursor_pos > 0 + && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1, + Mcandidate_list))) + { + ic->candidate_list = mtext_property_value (prop); + ic->candidate_index + = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1, + Mcandidate_index); + ic->candidate_from = mtext_property_start (prop); + ic->candidate_to = mtext_property_end (prop); + } + + ic->candidates_changed |= (candidate_list != ic->candidate_list + || candidate_index != ic->candidate_index + || candidate_show != ic->candidate_show); + return 0; +} + + +/* Handle the input key KEY in the current state and map specified in + the input context IC. If KEY is handled correctly, return 0. + Otherwise, return -1. */ + +static int +handle_key (MInputContext *ic) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + MIMMap *map = ic_info->map; + MIMMap *submap; + MSymbol key = ic_info->keys[ic_info->key_head]; + int i; + + submap = (map->submaps ? mplist_get (map->submaps, key) : NULL); + if (submap) + { + mtext_cpy (ic->preedit, ic_info->preedit_saved); + ic->cursor_pos = ic_info->state_pos; + ic_info->key_head++; + ic_info->map = map = submap; + if (map->map_actions) + { + if (take_action_list (ic, map->map_actions) < 0) + return -1; + } + else if (map->submaps) + { + for (i = ic_info->state_key_head; i < ic_info->key_head; i++) + { + MSymbol key = ic_info->keys[i]; + char *name = msymbol_name (key); + + if (! name[0] || ! name[1]) + mtext_ins_char (ic->preedit, ic->cursor_pos++, name[0], 1); + } + ic->preedit_changed = 1; + } + + /* If this is the terminal map or we have shifted to another + state, perform branch actions (if any). */ + if (! map->submaps || map != ic_info->map) + { + if (map->branch_actions) + { + if (take_action_list (ic, map->branch_actions) < 0) + return -1; + } + /* If MAP is still not the root map, shift to the current + state. */ + if (ic_info->map != ic_info->state->map) + shift_state (ic, ic_info->state->name); + } + } + else + { + /* MAP can not handle KEY. */ + + /* If MAP is the root map of the initial state, it means that + the current input method can not handle KEY. */ + if (map == ((MIMState *) MPLIST_VAL (im_info->states))->map) + return -1; + + if (map != ic_info->state->map) + { + /* If MAP is not the root map... */ + /* If MAP has branch actions, perform them. */ + if (map->branch_actions) + take_action_list (ic, map->branch_actions); + /* If MAP is still not the root map, shift to the current + state. */ + if (ic_info->map != ic_info->state->map) + { + shift_state (ic, ic_info->state->name); + /* If MAP has branch_actions, perform them. */ + if (ic_info->map->branch_actions) + take_action_list (ic, ic_info->map->branch_actions); + } + } + else + { + /* MAP is the root map, perform branch actions (if any) or + shift to the initial state. */ + if (map->branch_actions) + take_action_list (ic, map->branch_actions); + else + shift_state (ic, + ((MIMState *) MPLIST_VAL (im_info->states))->name); + } + } + return 0; +} + +static void +reset_ic (MInputContext *ic) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + + MLIST_RESET (ic_info); + ic_info->state = (MIMState *) MPLIST_VAL (im_info->states); + ic_info->map = ic_info->state->map; + ic_info->state_key_head = ic_info->key_head = 0; + ic->cursor_pos = ic_info->state_pos = 0; + ic->status = ic_info->state->title; + if (! ic->status) + ic->status = im_info->title; + ic->candidate_list = NULL; + ic->candidate_show = 0; + ic->status_changed = ic->preedit_changed = ic->candidates_changed = 1; + if (ic_info->map->map_actions) + take_action_list (ic, ic_info->map->map_actions); +} + +static int +open_im (MInputMethod *im) +{ + MDatabase *mdb; + MInputMethodInfo *im_info; + MPlist *plist; + int result; + + mdb = mdatabase_find (Minput_method, im->language, im->name, Mnil); + if (! mdb) + return -1; + plist = mdatabase_load (mdb); + if (! plist) + MERROR (MERROR_IM, -1); + MSTRUCT_CALLOC (im_info, MERROR_IM); + im->info = im_info; + result = load_input_method (im->language, im->name, plist, im_info); + M17N_OBJECT_UNREF (plist); + if (result < 0) + MERROR (MERROR_IM, -1); + return 0; +} + +static void +close_im (MInputMethod *im) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) im->info; + MPlist *plist; + + if (im_info->title) + M17N_OBJECT_UNREF (im_info->title); + if (im_info->states) + { + MPLIST_DO (plist, im_info->states) + { + MIMState *state = (MIMState *) MPLIST_VAL (plist); + + if (state->title) + M17N_OBJECT_UNREF (state->title); + if (state->map) + free_map (state->map); + free (state); + } + M17N_OBJECT_UNREF (im_info->states); + } + + if (im_info->macros) + { + MPLIST_DO (plist, im_info->macros) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (im_info->macros); + } + + if (im_info->externals) + { + MPLIST_DO (plist, im_info->externals) + { + MIMExternalModule *external = MPLIST_VAL (plist); + + dlclose (external->handle); + M17N_OBJECT_UNREF (external->func_list); + free (external); + MPLIST_KEY (plist) = Mt; + } + M17N_OBJECT_UNREF (im_info->externals); + } + free (im_info); + im->info = NULL; +} + + +static int +create_ic (MInputContext *ic) +{ + MInputMethod *im = ic->im; + MInputMethodInfo *im_info = (MInputMethodInfo *) im->info; + MInputContextInfo *ic_info; + + if (ic->info) + ic_info = (MInputContextInfo *) ic->info; + else + { + MSTRUCT_CALLOC (ic_info, MERROR_IM); + ic->info = ic_info; + } + MLIST_INIT1 (ic_info, keys, 8); + ic_info->markers = mplist (); + ic_info->vars = mplist (); + ic_info->preedit_saved = mtext (); + if (im_info->externals) + { + MPlist *func_args = mplist (), *plist; + + mplist_add (func_args, Mt, ic); + MPLIST_DO (plist, im_info->externals) + { + MIMExternalModule *external = MPLIST_VAL (plist); + MIMExternalFunc func + = (MIMExternalFunc) mplist_get (external->func_list, Minit); + + if (func) + (func) (func_args); + } + M17N_OBJECT_UNREF (func_args); + } + reset_ic (ic); + return 0; +} + +static void +destroy_ic (MInputContext *ic) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + + if (im_info->externals) + { + MPlist *func_args = mplist (), *plist; + + mplist_add (func_args, Mt, ic); + MPLIST_DO (plist, im_info->externals) + { + MIMExternalModule *external = MPLIST_VAL (plist); + MIMExternalFunc func + = (MIMExternalFunc) mplist_get (external->func_list, Mfini); + + if (func) + (func) (func_args); + } + M17N_OBJECT_UNREF (func_args); + } + MLIST_FREE1 (ic_info, keys); + M17N_OBJECT_UNREF (ic_info->preedit_saved); + M17N_OBJECT_UNREF (ic_info->markers); + M17N_OBJECT_UNREF (ic_info->vars); + free (ic->info); +} + + +/** Handle the input key KEY in the current state and map of IC->info. + If KEY is handled but no text is produced, return 0, otherwise + return 1. + + Ignore ARG. */ + +static int +filter (MInputContext *ic, MSymbol key, void *arg) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info; + MInputContextInfo *ic_info = (MInputContextInfo *) ic->info; + int i = 0; + + mtext_reset (ic->produced); + ic->status_changed = ic->preedit_changed = ic->candidates_changed = 0; + MLIST_APPEND1 (ic_info, keys, key, MERROR_IM); + ic_info->key_unhandled = 0; + do { + if (handle_key (ic) < 0) + { + /* KEY was not handled. Reset the status and break the + loop. */ + reset_ic (ic); + /* This forces returning 1. */ + ic_info->key_unhandled = 1; + break; + } + if (i++ == 100) + { + mdebug_hook (); + reset_ic (ic); + ic_info->key_unhandled = 1; + break; + } + /* Break the loop if all keys were handled. */ + } while (ic_info->key_head < ic_info->used); + + /* If the current map is the root of the initial state, we should + produce any preedit text in ic->produced. */ + if (ic_info->map == ((MIMState *) MPLIST_VAL (im_info->states))->map + && mtext_nchars (ic->preedit) > 0) + shift_state (ic, ((MIMState *) MPLIST_VAL (im_info->states))->name); + + if (mtext_nchars (ic->produced) > 0) + { + MSymbol lang = msymbol_get (ic->im->language, Mlanguage); + + if (lang != Mnil) + mtext_put_prop (ic->produced, 0, mtext_nchars (ic->produced), + Mlanguage, ic->im->language); + } + + return (! ic_info->key_unhandled && mtext_nchars (ic->produced) == 0); +} + + +/** Return 1 if the last event or key was not handled, otherwise + return 0. + + There is no need of looking up because ic->produced should already + contain the produced text (if any). + + Ignore KEY. */ + +static int +lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt) +{ + mtext_cat (mt, ic->produced); + mtext_reset (ic->produced); + return (((MInputContextInfo *) ic->info)->key_unhandled ? -1 : 0); +} + +/* Support functions for mdebug_dump_im. */ + +static void +dump_im_map (MPlist *map_list, int indent) +{ + char *prefix; + MSymbol key = MPLIST_KEY (map_list); + MIMMap *map = (MIMMap *) MPLIST_VAL (map_list); + + prefix = (char *) alloca (indent + 1); + memset (prefix, 32, indent); + prefix[indent] = '\0'; + + fprintf (stderr, "(\"%s\" ", msymbol_name (key)); + if (map->map_actions) + mdebug_dump_plist (map->map_actions, indent + 2); + if (map->submaps) + { + MPLIST_DO (map_list, map->submaps) + { + fprintf (stderr, "\n%s ", prefix); + dump_im_map (map_list, indent + 2); + } + } + if (map->branch_actions) + { + fprintf (stderr, "\n%s (branch\n%s ", prefix, prefix); + mdebug_dump_plist (map->branch_actions, indent + 4); + fprintf (stderr, ")"); + } + fprintf (stderr, ")"); +} + + +static void +dump_im_state (MIMState *state, int indent) +{ + char *prefix; + MPlist *map_list; + + prefix = (char *) alloca (indent + 1); + memset (prefix, 32, indent); + prefix[indent] = '\0'; + + fprintf (stderr, "(%s", msymbol_name (state->name)); + if (state->map->submaps) + { + MPLIST_DO (map_list, state->map->submaps) + { + fprintf (stderr, "\n%s ", prefix); + dump_im_map (map_list, indent + 2); + } + } + fprintf (stderr, ")"); +} + + + +int +minput__init () +{ + char *key_names[32] + = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "BackSpace", "Tab", "Linefeed", "Clear", NULL, "Return", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "Escape", NULL, NULL, NULL, NULL }; + char buf[6], buf2[256]; + int i; + + Minput_method = msymbol ("input-method"); + Minput_driver = msymbol ("input-driver"); + Mtitle = msymbol ("title"); + Mmacro = msymbol ("macro"); + Mmodule = msymbol ("module"); + Mmap = msymbol ("map"); + Mstate = msymbol ("state"); + Minsert = msymbol ("insert"); + Mdelete = msymbol ("delete"); + Mmove = msymbol ("move"); + Mmark = msymbol ("mark"); + Mpushback = msymbol ("pushback"); + Mundo = msymbol ("undo"); + Mcall = msymbol ("call"); + Mshift = msymbol ("shift"); + Mselect = msymbol ("select"); + Mshow = msymbol ("show"); + Mhide = msymbol ("hide"); + Mset = msymbol ("set"); + Madd = msymbol ("add"); + Msub = msymbol ("sub"); + Mmul = msymbol ("mul"); + Mdiv = msymbol ("div"); + Mequal = msymbol ("="); + Mless = msymbol ("<"); + Mgreater = msymbol (">"); + + Minput_preedit_start = msymbol ("input-preedit-start"); + Minput_preedit_done = msymbol ("input-preedit-done"); + Minput_preedit_draw = msymbol ("input-preedit-draw"); + Minput_status_start = msymbol ("input-status-start"); + Minput_status_done = msymbol ("input-status-done"); + Minput_status_draw = msymbol ("input-status-draw"); + Minput_candidates_start = msymbol ("input-candidates-start"); + Minput_candidates_done = msymbol ("input-candidates-done"); + Minput_candidates_draw = msymbol ("input-candidates-draw"); + Minput_set_spot = msymbol ("input-set-spot"); + Minput_toggle = msymbol ("input-toggle"); + + Mcandidate_list = msymbol_as_managing_key (" candidate-list"); + Mcandidate_index = msymbol (" candidate-index"); + + Minit = msymbol ("init"); + Mfini = msymbol ("fini"); + + buf[0] = 'C'; + buf[1] = '-'; + buf[3] = '\0'; + for (i = 0, buf[2] = '@'; i < ' '; i++, buf[2]++) + { + if (key_names[i]) + one_char_symbol[i] = msymbol (key_names[i]); + else + one_char_symbol[i] = msymbol (buf); + } + for (buf[2] = i; i < 127; i++, buf[2]++) + one_char_symbol[i] = msymbol (buf + 2); + one_char_symbol[i++] = msymbol ("Delete"); + buf[2] = 'M'; + buf[3] = '-'; + buf[5] = '\0'; + buf2[0] = 'M'; + buf2[1] = '-'; + for (buf[4] = '@'; i < 160; i++, buf[4]++) + { + if (key_names[i - 128]) + { + strcpy (buf2 + 2, key_names[i - 128]); + one_char_symbol[i] = msymbol (buf2); + } + else + one_char_symbol[i] = msymbol (buf); + } + for (buf[4] = i - 128; i < 255; i++, buf[2]++) + one_char_symbol[i] = msymbol (buf + 2); + one_char_symbol[i] = msymbol ("M-Delete"); + + minput_default_driver.open_im = open_im; + minput_default_driver.close_im = close_im; + minput_default_driver.create_ic = create_ic; + minput_default_driver.destroy_ic = destroy_ic; + minput_default_driver.filter = filter; + minput_default_driver.lookup = lookup; + minput_default_driver.callback_list = NULL; + minput_driver = &minput_default_driver; + return 0; +} + +void +minput__fini () +{ + if (minput_default_driver.callback_list) + { + M17N_OBJECT_UNREF (minput_default_driver.callback_list); + minput_default_driver.callback_list = NULL; + } + if (minput_driver->callback_list) + { + M17N_OBJECT_UNREF (minput_driver->callback_list); + minput_driver->callback_list = NULL; + } +} + +void +minput__callback (MInputContext *ic, MSymbol command) +{ + if (ic->im->driver.callback_list) + { + MInputCallbackFunc func + = (MInputCallbackFunc) mplist_get (ic->im->driver.callback_list, + command); + + if (func) + (func) (ic, command); + } +} + +MSymbol +minput__char_to_key (int c) +{ + if (c < 0 || c >= 0x100) + return Mnil; + + return one_char_symbol[c]; +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nInputMethod */ +/*** @{ */ +/*=*/ + +/***en + @name Variables: Predefined symbols for callback commands. + + These are the predefined symbols that are used as the @c COMMAND + argument of callback functions of an input method driver (see + #MInputDriver::callback_list). */ +/*** @{ */ +/*=*/ + +MSymbol Minput_preedit_start; +MSymbol Minput_preedit_done; +MSymbol Minput_preedit_draw; +MSymbol Minput_status_start; +MSymbol Minput_status_done; +MSymbol Minput_status_draw; +MSymbol Minput_candidates_start; +MSymbol Minput_candidates_done; +MSymbol Minput_candidates_draw; +MSymbol Minput_set_spot; +MSymbol Minput_toggle; +/*** @} */ +/*=*/ + +/***en + @brief The default input driver for internal input methods. + + The variable #minput_default_driver is the default driver for + internal input methods. + + The member MInputDriver::open_im () searches the m17n database for + an input method that matches the tag \<#Minput_method, $LANGUAGE, + $NAME\> and loads it. + + The member MInputDriver::callback_list () is @c NULL. Thus, it is + programmers responsibility to set it to a plist of proper callback + functions. Otherwise, no feedback information (e.g. preedit text) + can be shown to users. + + The macro M17N_INIT () sets the variable #minput_driver to the + pointer to this driver so that all internal input methods use it. + + Therefore, unless @c minput_driver is set differently, the driver + dependent arguments $ARG of the functions whose name begin with + "minput_" are all ignored. */ + +/***ja + @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥǥե©¥ë¥ÈÆþÎϥɥ饤¥Ð + + ÆþÎϥɥ饤¥Ð minput_default_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤΥǥե©¥ë + ¥È¤ÎÆþÎϥɥ饤¥Ð¤Ç¤¢¤ë¡£ + + ¤³¤Î¥É¥é¥¤¥Ð¤Î ¥á¥ó¥Ð¤Ï @c NULL ¤Ê¤Î¤Ç¡¢¥×¥í¥°¥é¥Þ¦¤Ç + ÀÕǤ¤ò»ý¤Ã¤Æ, ŬÀڤʥ³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ËÀßÄꤷ¡¢Preedit ¥Æ¥­¥¹¥È, + Status ¥Æ¥­¥¹¥È¤¬¥æ¡¼¥¶¤Ëɽ¼¨¤Ç¤­¤ë¤è¤¦¤Ë¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + ´Ø¿ô M17N_INIT () ¤ÏÊÑ¿ô @c minput_driver ¤ò¤³¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó + ¥¿¤ËÀßÄꤷ¡¢Á´¤Æ¤ÎÆâÉôÆþÎϥ᥽¥Ã¥É¤¬¤³¤Î¥É¥é¥¤¥Ð¤ò»È¤¦¤è¤¦¤Ë¤¹¤ë¡£ + + ¤·¤¿¤¬¤Ã¤Æ¡¢@c minput_driver ¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤΤޤޤǤ¢¤ì¤Ð¡¢minput_ + ¤Ç»Ï¤Þ¤ë°Ê²¼¤Î´Ø¿ô·²¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô $ARG ¤Ï¤É¤ì¤â̵»ë¤µ¤ì + ¤ë¡£ */ + +MInputDriver minput_default_driver; +/*=*/ + +/***en + @brief The input driver for internal input methods. + + The variable #minput_driver is a pointer to the input method + driver that is used by internal input methods. The macro + M17N_INIT () initializes it to a pointer to #minput_default_driver + (if is included) or to #minput_gui_driver (if + is included). */ + +MInputDriver *minput_driver; + +MSymbol Minput_driver; + +/*=*/ + +/***en + @brief Open an input method. + + The minput_open_im () function opens an input method that matches + language $LANGUAGE and name $NAME, and returns a pointer to the + input method object newly allocated. + + This function at first decides an input driver for the input + method as below. + + If $LANGUAGE is not #Mnil, an input driver pointed by the variable + #minput_driver is used. + + If $LANGUAGE is #Mnil and $NAME has #Minput_driver property, the + input driver pointed to by the property value is used to open the + input method. If $NAME has no such property, @c NULL is returned. + + Then, the member MInputDriver::open_im () of the input driver is + called. + + $ARG is set in the member @c arg of the structure MInputMethod so + that the input driver can refer to it. */ + +/***ja + @brief ÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë + + ´Ø¿ô mim_open () ¤Ï¸À¸ì $LANGUAGE ¤È̾Á° $NAME ¤ËŬ¹ç¤¹¤ëÆþÎϥ᥽¥Ã + ¥É¤ò¥ª¡¼¥×¥ó¤·¡¢¤½¤Î¿·¤¿¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ + + $LANGUAGE ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ã椫¤é \<@c + Minput_method, $LANGUAGE, $NAME \> ¤È¤¤¤¦¥¿¥°¤ËŬ¹ç¤¹¤ëÆþÎϥ᥽¥Ã + ¥É¤òõ¤¹¡£¸«¤Ä¤«¤Ã¤¿¾ì¹ç¤Ï¡¢ÊÑ¿ô #minput_driver ¤Ç¥Ý¥¤¥ó¥È¤µ¤ì¤Æ + ¤¤¤ëÆþÎϥɥ饤¥Ð¤òÍѤ¤¤Æ¤½¤ÎÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£¸«¤Ä¤«¤é¤Ê + ¤¤¾ì¹ç¤ä¥ª¡¼¥×¥ó¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤Ï @c NULL ¤òÊÖ¤¹¡£ + + ÊÑ¿ô #minput_driver ¤Ï¡¢#minput_default_driver ¤« @c + minput_gui_driver ¤Î¤É¤Á¤é¤«¤ò¥Ý¥¤¥ó¥È¤·¤Æ¤¤¤ë¡£Á°¼Ô¤Ï CUI ÍѤǤ¢ + ¤ê¡¢´Ø¿ôm17n_initialize () ¤ò¸Æ¤Ö¤³¤È¤Ë¤è¤Ã¤Æ #minput_driver ¤¬ + #minput_default_driver ¤ò¥Ý¥¤¥ó¥È¤¹¤ë¤è¤¦¤Ë¤Ê¤ë¡£¸å¼Ô¤Ï GUI ÍÑ¤Ç + ¤¢¤ê¡¢´Ø¿ô m17n_initialize_win () ¤Ë¤è¤Ã¤Æ¥Ý¥¤¥ó¥È¤µ¤ì¤ë¡£¾ÜºÙ¤Ë¤Ä + ¤¤¤Æ¤Ï¤³¤ì¤é¤ÎÊÑ¿ô¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + $LANGUAGE ¤¬ #Mnil ¤Ç¤¢¤ê¡¢$NAME ¤¬ #Minput_driver ¤ò¥­¡¼¤È¤¹ + ¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¾ì¹ç¤Ë¤Ï¡¢¤½¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤǥݥ¤¥ó¥È¤µ¤ì¤Æ¤¤¤ë + ÆþÎϥɥ饤¥Ð¤òÍѤ¤¤ÆÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£$NAME ¤Ë¤½¤Î¤è¤¦¤Ê + ¥×¥í¥Ñ¥Æ¥£¤¬Ìµ¤«¤Ã¤¿¾ì¹ç¤ä¥ª¡¼¥×¥ó¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤Ï @c NULL ¤ò + ÊÖ¤¹¡£ + + $ARG ¤Ï¡¢ÆþÎϥɥ饤¥Ð¤¬»²¾È¤Ç¤­¤ë¤è¤¦¤Ë¡¢¹½Â¤ÂÎ MInputMethod ¤Î¥á + ¥ó¥Ð @c arg ¤Ë¥»¥Ã¥È¤µ¤ì¤ë¡£ + + @latexonly \IPAlabel{minput_open} @endlatexonly + +*/ + +MInputMethod * +minput_open_im (MSymbol language, MSymbol name, void *arg) +{ + MInputMethod *im; + MInputDriver *driver; + + if (language) + driver = minput_driver; + else + { + driver = (MInputDriver *) msymbol_get (name, Minput_driver); + if (! driver) + MERROR (MERROR_IM, NULL); + } + + MSTRUCT_CALLOC (im, MERROR_IM); + im->language = language; + im->name = name; + im->arg = arg; + im->driver = *driver; + if ((*im->driver.open_im) (im) < 0) + { + free (im); + return NULL; + } + return im; +} + +/*=*/ + +/***en + @brief Close an input method. + + The minput_close_im () function closes the input method $IM, which + must have been created by minput_open_im (). */ + +/***ja + @brief ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼¥º¤¹¤ë + + ´Ø¿ô minput_close_im () ¤Ï¡¢ÆþÎϥ᥽¥Ã¥É $IM ¤ò¥¯¥í¡¼¥º¤¹¤ë¡£¤³¤Î + ÆþÎϥ᥽¥Ã¥É $IM ¤Ï minput_open_im () ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì + ¤Ð¤Ê¤é¤Ê¤¤¡£ */ + +void +minput_close_im (MInputMethod *im) +{ + (*im->driver.close_im) (im); + free (im); +} + +/*=*/ + +/***en + @brief Create an input context. + + The minput_create_ic () function creates an input context object + associated with input method $IM, and calls callback functions + corresponding to #Minput_preedit_start, #Minput_status_start, and + #Minput_status_draw in this order. + + @return + + If an input context is successfully created, minput_create_ic () + returns a pointer to it. Otherwise it returns @c NULL. */ + +/***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÀ¸À®¤¹¤ë + + ´Ø¿ô minput_create_ic () ¤ÏÆþÎϥ᥽¥Ã¥É $IM ¤ËÂбþ¤¹¤ëÆþÎÏ¥³¥ó¥Æ¥¯ + ¥¹¥È¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤¹¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢minput_create_ic () ¤ÏÀ¸À®¤·¤¿ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£ */ + +MInputContext * +minput_create_ic (MInputMethod *im, void *arg) +{ + MInputContext *ic; + + MSTRUCT_CALLOC (ic, MERROR_IM); + ic->im = im; + ic->arg = arg; + ic->preedit = mtext (); + ic->candidate_list = NULL; + ic->produced = mtext (); + ic->spot.x = ic->spot.y = 0; + ic->active = 1; + ic->plist = mplist (); + if ((*im->driver.create_ic) (ic) < 0) + { + M17N_OBJECT_UNREF (ic->preedit); + M17N_OBJECT_UNREF (ic->produced); + M17N_OBJECT_UNREF (ic->plist); + free (ic); + return NULL; + }; + + if (im->driver.callback_list) + { + minput__callback (ic, Minput_preedit_start); + minput__callback (ic, Minput_status_start); + minput__callback (ic, Minput_status_draw); + } + + return ic; +} + +/*=*/ + +/***en + @brief Destroy an input context. + + The minput_destroy_ic () function destroys the input context $IC, + which must have been created by minput_create_ic (). It calls + callback functions corresponding to #Minput_preedit_done, + #Minput_status_done, and #Mcandidate_done in this order. */ + +/***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÇ˲õ¤¹¤ë + + ´Ø¿ô minput_destroy_ic () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤òÇ˲õ¤¹¤ë¡£¤³ + ¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ï minput_create_ic () ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê + ¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ */ + +void +minput_destroy_ic (MInputContext *ic) +{ + if (ic->im->driver.callback_list) + { + minput__callback (ic, Minput_preedit_done); + minput__callback (ic, Minput_status_done); + minput__callback (ic, Minput_candidates_done); + } + (*ic->im->driver.destroy_ic) (ic); + M17N_OBJECT_UNREF (ic->preedit); + M17N_OBJECT_UNREF (ic->produced); + M17N_OBJECT_UNREF (ic->plist); + free (ic); +} + +/*=*/ + +/***en + @brief Filter an input key. + + The minput_filter () function filters input key $KEY according to + input context $IC, and calls callback functions corresponding to + #Minput_preedit_draw, #Minput_status_draw, and #Mcandidate_draw if + the preedit text, the status, and the current candidate are + changed respectively. + + @return + If $KEY is filtered out, this function returns 1. In that case, + the caller should discard the key. Otherwise, it returns 0, and + the caller should handle the key, for instance, by calling the + function minput_lookup () with the same $KEY. */ + +/***ja + @brief ÆþÎÏ¥­¡¼¤Î¥Õ¥£¥ë¥¿¥ê¥ó¥°¤ò¤¹¤ë + + ´Ø¿ô minput_filter () ¤ÏÆþÎÏ¥­¡¼ $KEY ¤òÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤Ë±þ + ¤¸¤Æ¥Õ¥£¥ë¥¿¥ê¥ó¥°¤¹¤ë¡£ + + ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤ÎÆþÎϥ᥽¥Ã¥É¤¬ÆþÎÏ¥­¡¼¤ò¥Õ¥£¥ë¥¿¤¹¤ì¤Ð¡¢¤³ + ¤Î´Ø¿ô¤Ï 1 ¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¸Æ¤Ó½Ð¤·Â¦¤Ï¤³¤Î¥­¡¼¤ò¼Î¤Æ¤ë¤Ù¤­¤Ç¤¢¤ë¡£ + ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤·¡¢¸Æ¤Ó½Ð¤·Â¦¤¬¡¢¤¿¤È¤¨¤ÐƱ¤¸ $KEY ¤Ç´Ø¿ô + minput_lookup () ¤ò¸Æ¤Ö¤Ê¤É¤·¤Æ¡¢¤³¤Î¥­¡¼¤ò½èÍý¤¹¤ë¡£ + + @latexonly \IPAlabel{minput_filter} @endlatexonly +*/ + +int +minput_filter (MInputContext *ic, MSymbol key, void *arg) +{ + int ret; + + if (! ic + || ! ic->active) + return 0; + ret = (*ic->im->driver.filter) (ic, key, arg); + + if (ic->im->driver.callback_list) + { + if (ic->preedit_changed) + minput__callback (ic, Minput_preedit_draw); + if (ic->status_changed) + minput__callback (ic, Minput_status_draw); + if (ic->candidates_changed) + minput__callback (ic, Minput_candidates_draw); + ic->preedit_changed = ic->status_changed = ic->candidates_changed = 0; + } + + return ret; +} + +/*=*/ + +/***en + @brief Lookup a text produced in the input context. + + The minput_lookup () function looks up a text in the input context + $IC. $KEY must be the same one provided to the previous call of + minput_filter (). + + If a text was produced by the input method, it is concatenated + to M-text $MT. + + This function calls #MInputDriver::lookup. + + @return + If $KEY was correctly handled by the input method, this function + returns 0. Otherwise, returns -1, even in that case, some text + may be produced in $MT. */ + +/***ja + @brief ÆþÎϥ᥽¥Ã¥É¤¬ºî¤Ã¤¿¥Æ¥­¥¹¥È¤Î³ÍÆÀ + + ´Ø¿ô minput_lookup () ¤ÏÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC Ãæ¤Î¥Æ¥­¥¹¥È¤ò³ÍÆÀ¤¹ + ¤ë¡£$KEY ¤Ï´Ø¿ôminput_filter () ¤Ø¤ÎľÁ°¤Î¸Æ¤Ó½Ð¤·¤ËÍѤ¤¤é¤ì¤¿¤â¤Î + ¤ÈƱ¤¸¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + ¥Æ¥­¥¹¥È¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆÀ¸À®¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢$IC->produced ¤ËÊÝ + »ý¤µ¤ì¤Æ¤¤¤ë¡£ + + ¤³¤Î´Ø¿ô¤Ï¡¢X ¥¦¥£¥ó¥É¥¦¤Î XLookupString () ¡¢ + XmbLookupString () ¡¢XwcLookupString () ¤ËÂбþ¤¹ + ¤ë¡£ + + @return + $KEY ¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆŬÀڤ˽èÍý¤Ç¤­¤Æ¤¤¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï 0 ¤ò + ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢¤³¤Î¾ì¹ç¤Ç¤â$IC->produced ¤Ë²¿¤é¤« + ¤Î¥Æ¥­¥¹¥È¤¬À¸À®¤µ¤ì¤Æ¤¤¤ë¤³¤È¤¬¤¢¤ë¡£ + + @latexonly \IPAlabel{minput_lookup} @endlatexonly */ + +int +minput_lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt) +{ + return (ic ? (*ic->im->driver.lookup) (ic, key, arg, mt) : -1); +} +/*=*/ + +/***en + @brief Set the spot of the input context. + + The minput_set_spot () function set the spot of input context $IC + to coordinate ($X, $Y) with the height $ASCENT and $DESCENT. + $FONTSIZE specfies the fontsize of a preedit text in 1/10 point. + The semantics of these values depend on the input driver. + + For instance, an input driver designed to work in CUI environment + may use $X and $Y as column and row numbers, and ignore $ASCENT + and $DESCENT. An input driver designed to work on a window system + may treat $X and $Y as pixel offsets relative to the origin of the + client window, and treat $ASCENT and $DESCENT as ascent and + descent pixels of a line at ($X . $Y). + + $MT and $POS is an M-text and a character position at the spot. + $MT may be NULL, in which case, the input method can't get + information about the text around the spot. */ + +/***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¥¹¥Ý¥Ã¥È¤òÀßÄꤹ¤ë + + ´Ø¿ô minput_set_spot () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤Î¥¹¥Ý¥Ã¥È¤ò¡¢ºÂ + ɸ ($X, $Y)¤Ë ¡¢¹â¤µ $ASCENT¡¢$DESCENT ¤ÇÀßÄꤹ¤ë¡£¤³¤ì¤é¤ÎÃÍ¤Î°Õ + Ì£¤ÏÆþÎϥɥ饤¥Ð¤Ë°Í¸¤¹¤ë¡£ + + ¤¿¤È¤¨¤Ð CUI ´Ä¶­¤ÇÆ°ºî¤¹¤ëÆþÎϥɥ饤¥Ð¤Ï $X, $Y ¤ò¤½¤ì¤¾¤ìÎó¤È¹Ô + ¤ÎÈÖ¹æ¤È¤·¤ÆÍѤ¤¡¢$ASCENT¡¢$DESCENT ¤ò̵»ë¤¹¤ë¤«¤â¤·¤ì¤Ê¤¤¡£ ¤Þ¤¿ + ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥àÍѤÎÆþÎϥɥ饤¥Ð¤Ï $X,$Y ¤ò¥¯¥é¥¤¥¢¥ó¥È¥¦¥£¥ó¥É + ¥¦¤Î¸¶ÅÀ¤«¤é¤Î¥ª¥Õ¥»¥Ã¥È¤ò¥Ô¥¯¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¤¡¢ + $ASCENT ¤È $DESCENT ¤ò ($X . $Y) ¤ÎÎó¤Î¥¢¥»¥ó¥È¤È¥Ç¥£¥»¥ó¥È¤ò¥Ô¥¯ + ¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¦¤«¤â¤·¤ì¤Ê¤¤¡£ */ + +void +minput_set_spot (MInputContext *ic, int x, int y, + int ascent, int descent, int fontsize, + MText *mt, int pos) +{ + ic->spot.x = x; + ic->spot.y = y; + ic->spot.ascent = ascent; + ic->spot.descent = descent; + ic->spot.fontsize = fontsize; + ic->spot.mt = mt; + ic->spot.pos = pos; + if (ic->im->driver.callback_list) + minput__callback (ic, Minput_set_spot); +} +/*=*/ + +/***en + @brief Toggle input method. + + The minput_toggle () function toggles the input method associated + with the input context $IC. */ + +void +minput_toggle (MInputContext *ic) +{ + if (ic->im->driver.callback_list) + minput__callback (ic, Minput_toggle); + ic->active = ! ic->active; +} + + +/*** @} */ +/*=*/ +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ +/*=*/ + +/***en + @brief Dump an input method + + The mdebug_dump_im () function prints the input method $IM in a + human readable way to the stderr. $INDENT specifies how many + columns to indent the lines but the first one. + + @return + This function returns $IM. */ + +MInputMethod * +mdebug_dump_im (MInputMethod *im, int indent) +{ + MInputMethodInfo *im_info = (MInputMethodInfo *) im->info; + char *prefix; + + prefix = (char *) alloca (indent + 1); + memset (prefix, 32, indent); + prefix[indent] = '\0'; + + fprintf (stderr, "(input-method %s %s ", msymbol_name (im->language), + msymbol_name (im->name)); + mdebug_dump_mtext (im_info->title, 0, 0); + if (im->name != Mnil) + { + MPlist *state; + + MPLIST_DO (state, im_info->states) + { + fprintf (stderr, "\n%s ", prefix); + dump_im_state (MPLIST_VAL (state), indent + 2); + } + } + fprintf (stderr, ")"); + return im; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..1e00f0e --- /dev/null +++ b/src/input.h @@ -0,0 +1,84 @@ +/* input.h -- header file for the input method module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_INPUT_H_ +#define _M17N_INPUT_H_ + +typedef struct +{ + MText *title; + MPlist *states; + MPlist *macros; + MPlist *externals; +} MInputMethodInfo; + +typedef struct MIMState MIMState; + +typedef struct MIMMap MIMMap; + +typedef struct +{ + /** The current state. */ + MIMState *state; + + /** The current map. */ + MIMMap *map; + + /** Table of typed keys. */ + int size, inc, used; + MSymbol *keys; + + /** Index of the key handled firstly in the current state. */ + int state_key_head; + + /** Index of the key not yet handled. */ + int key_head; + + /** Saved M-text when entered in the current state. */ + MText *preedit_saved; + + /** The insertion position when shifted to the current state. */ + int state_pos; + + /** List of markers. */ + MPlist *markers; + + /* List of variables. */ + MPlist *vars; + + int key_unhandled; + + /** Used by minput_win_driver (input-win.c). */ + void *win_info; +} MInputContextInfo; + +#define MINPUT_KEY_SHIFT_MODIFIER (1 << 0) +#define MINPUT_KEY_CONTROL_MODIFIER (1 << 1) +#define MINPUT_KEY_META_MODIFIER (1 << 2) +#define MINPUT_KEY_ALT_MODIFIER (1 << 3) +#define MINPUT_KEY_SUPER_MODIFIER (1 << 4) +#define MINPUT_KEY_HYPER_MODIFIER (1 << 5) + +extern void minput__callback (MInputContext *ic, MSymbol command); +extern MSymbol minput__char_to_key (int c); + +#endif /* not _M17N_INPUT_H_ */ diff --git a/src/internal-gui.h b/src/internal-gui.h new file mode 100644 index 0000000..c50d936 --- /dev/null +++ b/src/internal-gui.h @@ -0,0 +1,278 @@ +/* internal-gui.h -- common header file for the internal GUI API. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M_INTERNAL_GUI_H +#define _M_INTERNAL_GUI_H + +typedef struct MWDevice MWDevice; + +extern MSymbol Mfont; + +typedef struct MRealizedFont MRealizedFont; +typedef struct MRealizedFace MRealizedFace; +typedef struct MRealizedFontset MRealizedFontset; + +/** Information about a frame. */ + +struct MFrame +{ + M17NObject control; + + /** Pointer to a window-system dependent device object associated + with the frame. */ + MWDevice *device; + + /** The default face of the frame. */ + MFace *face; + + /** The default realized face of the frame. */ + MRealizedFace *rface; + + /** The default width of one-char space. It is a width of SPACE + character of the default face. */ + int space_width; + + /** The default ascent and descent of a line. It is ascent and + descent of ASCII font of the default face. */ + int ascent, descent; + + /** The following three members are set by mwin__open_device (). */ + + /** List of realized fonts. */ + MPlist *realized_font_list; + + /** List of realized faces. */ + MPlist *realized_face_list; + + /** List of realized fontsets. */ + MPlist *realized_fontset_list; +}; + +enum glyph_type + { + GLYPH_CHAR, + GLYPH_SPACE, + GLYPH_PAD, + GLYPH_BOX, + GLYPH_ANCHOR, + GLYPH_TYPE_MAX + }; + +struct MGlyph +{ + int pos, to; + int c; + unsigned code; + MSymbol category; + MRealizedFace *rface; + short width, ascent, descent, lbearing, rbearing; + short xoff, yoff; + unsigned enabled : 1; + unsigned left_padding : 1; + unsigned right_padding : 1; + unsigned otf_encoded : 1; + unsigned bidi_level : 6; + enum glyph_type type : 3; + int combining_code; +}; + +typedef struct MGlyph MGlyph; + +struct MGlyphString +{ + M17NObject head; + + int size, inc, used; + MGlyph *glyphs; + MText *mt; + int from, to; + short width, height, ascent, descent; + short physical_ascent, physical_descent, lbearing, rbearing; + short text_ascent, text_descent, line_ascent, line_descent; + int indent, width_limit; + + /* Members to keep temporary data while layouting. */ + short sub_width, sub_lbearing, sub_rbearing; + + MDrawControl control; + + MDrawRegion region; + + struct MGlyphString *next, *top; +}; + +#define MGLYPH(idx) \ + (gstring->glyphs + ((idx) >= 0 ? (idx) : (gstring->used + (idx)))) + +#define GLYPH_INDEX(g) \ + ((g) - gstring->glyphs) + +#define INIT_GLYPH(g) \ + (memset (&(g), 0, sizeof (g))) + +#define APPEND_GLYPH(gstring, g) \ + MLIST_APPEND1 ((gstring), glyphs, (g), MERROR_DRAW) + +#define INSERT_GLYPH(gstring, at, g) \ + do { \ + MLIST_INSERT1 ((gstring), glyphs, (at), 1, MERROR_DRAW); \ + (gstring)->glyphs[at] = g; \ + } while (0) + +#define DELETE_GLYPH(gstring, at) \ + do { \ + MLIST_DELETE1 (gstring, glyphs, at, 1); \ + } while (0) + +#define REPLACE_GLYPHS(gstring, from, to, len) \ + do { \ + int newlen = (gstring)->used - (from); \ + int diff = newlen - (len); \ + \ + if (diff < 0) \ + MLIST_DELETE1 (gstring, glyphs, (to) + newlen, -diff); \ + else if (diff > 0) \ + MLIST_INSERT1 ((gstring), glyphs, (to) + (len), diff, MERROR_DRAW); \ + memmove ((gstring)->glyphs + to, (gstring)->glyphs + (from + diff), \ + (sizeof (MGlyph)) * newlen); \ + (gstring)->used -= newlen; \ + } while (0) + +#define MAKE_COMBINING_CODE(base_y, base_x, add_y, add_x, off_y, off_x) \ + (((off_y) << 16) \ + | ((off_x) << 8) \ + | ((base_x) << 6) \ + | ((base_y) << 4) \ + | ((add_x) << 2) \ + | (add_y)) + +#define COMBINING_CODE_OFF_Y(code) (((code) >> 16) & 0xFF) +#define COMBINING_CODE_OFF_X(code) (((code) >> 8) & 0xFF) +#define COMBINING_CODE_BASE_X(code) (((code) >> 6) & 0x3) +#define COMBINING_CODE_BASE_Y(code) (((code) >> 4) & 0x3) +#define COMBINING_CODE_ADD_X(code) (((code) >> 2) & 0x3) +#define COMBINING_CODE_ADD_Y(code) ((code) & 0x3) + +#define MAKE_COMBINING_CODE_BY_CLASS(class) (0x1000000 | class) + +#define COMBINING_BY_CLASS_P(code) ((code) & 0x1000000) + +#define COMBINING_CODE_CLASS(code) ((code) & 0xFFFFFF) + +typedef struct MGlyphString MGlyphString; + +typedef struct MFontDriver MFontDriver; + +extern int mfont__init (); +extern void mfont__fini (); + +extern int mface__init (); +extern void mface__fini (); + +extern int mdraw__init (); +extern void mdraw__fini (); + +extern int mfont__fontset_init (); +extern void mfont__fontset_fini (); + +extern int minput__win_init (); +extern void minput__win_fini (); + +extern int mwin__init (); +extern void mwin__fini (); + +extern MWDevice *mwin__open_device (MFrame *frame, MPlist *plist); + +extern void mwin__close_device (MFrame *frame); + +extern void *mwin__device_get_prop (MWDevice *device, MSymbol key); + +extern int mwin__parse_font_name (char *name, MFont *font); + +extern char *mwin__build_font_name (MFont *font); + +extern void mwin__realize_face (MRealizedFace *rface); + +extern void mwin__free_realized_face (MRealizedFace *rface); + +extern void mwin__fill_space (MFrame *frame, MDrawWindow win, + MRealizedFace *rface, int reverse, + int x, int y, int width, int height, + MDrawRegion region); + +extern void mwin__draw_hline (MFrame *frame, MDrawWindow win, + MGlyphString *gstring, + MRealizedFace *rface, int reverse, + int x, int y, int width, MDrawRegion region); + +extern void mwin__draw_box (MFrame *frame, MDrawWindow win, + MGlyphString *gstring, + MGlyph *g, int x, int y, int width, + MDrawRegion region); + +extern void mwin__draw_bitmap (MFrame *frame, MDrawWindow win, + MRealizedFace *rface, int reverse, + int x, int y, + int width, int height, int row_bytes, + unsigned char *bmp, + MDrawRegion region); + +extern MDrawRegion mwin__region_from_rect (MDrawMetric *rect); + +extern void mwin__union_rect_with_region (MDrawRegion region, + MDrawMetric *rect); + +extern void mwin__intersect_region (MDrawRegion region1, MDrawRegion region2); + +extern void mwin__region_add_rect (MDrawRegion region, MDrawMetric *rect); + +extern void mwin__region_to_rect (MDrawRegion region, MDrawMetric *rect); + +extern void mwin__free_region (MDrawRegion region); + +extern void mwin__verify_region (MFrame *frame, MDrawRegion region); + +extern void mwin__dump_region (MDrawRegion region); + +extern MDrawWindow mwin__create_window (MFrame *frame, MDrawWindow parent); + +extern void mwin__destroy_window (MFrame *frame, MDrawWindow win); + +#if 0 +extern MDrawWindow mwin__event_window (void *event); + +extern void mwin__print_event (void *event, char *win_name); +#endif + +extern void mwin__map_window (MFrame *frame, MDrawWindow win); + +extern void mwin__unmap_window (MFrame *frame, MDrawWindow win); + +extern void mwin__window_geometry (MFrame *frame, MDrawWindow win, + MDrawWindow parent, MDrawMetric *geometry); + +extern void mwin__adjust_window (MFrame *frame, MDrawWindow win, + MDrawMetric *current, MDrawMetric *new); + +extern MSymbol mwin__parse_event (MFrame *frame, void *arg, int *modifiers); + +#endif /* _M_INTERNAL_GUI_H */ diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000..597abc8 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,605 @@ +/* internal.h -- common header file for the internal CORE and SHELL APIs. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_INTERNAL_H_ +#define _M17N_INTERNAL_H_ + +/** @file internal.h + @brief a documentation for internal.h + + longer version of internal.h description +*/ + +extern int mdebug_hook (); + +/** Return with code RET while setting merror_code to ERR. */ + +#define MERROR(err, ret) \ + do { \ + merror_code = (err); \ + mdebug_hook (); \ + return (ret); \ + } while (0) + + +#define MERROR_GOTO(err, label) \ + do { \ + if ((err)) \ + merror_code = (err); \ + mdebug_hook (); \ + goto label; \ + } while (0) + + +#define MWARNING(err) \ + do { \ + mdebug_hook (); \ + goto warning; \ + } while (0) + + +#define M_CHECK_CHAR(c, ret) \ + if ((c) < 0 || (c) > MCHAR_MAX) \ + MERROR (MERROR_CHAR, (ret)); \ + else + + +/** Memory allocation stuffs. */ + +/* Call a handler function for memory full situation with argument + ERR. ERR must be one of enum MErrorCode. By default, the + handler function just calls exit () with argument ERR. */ + +#define MEMORY_FULL(err) \ + do { \ + (*m17n_memory_full_handler) (err); \ + exit (err); \ + } while (0) + + +/** The macro MTABLE_MALLOC () allocates memory (by malloc) for an + array of SIZE objects. The size of each object is determined by + the type of P. Then, it sets P to the allocated memory. ERR must + be one of enum MErrorCode. If the allocation fails, the macro + MEMORY_FULL () is called with argument ERR. */ + +#define MTABLE_MALLOC(p, size, err) \ + do { \ + int bytes = sizeof (*(p)) * (size); \ + if (! ((p) = (void *) malloc (bytes))) \ + MEMORY_FULL (err); \ + } while (0) + + +/** The macro MTABLE_CALLOC() is like the macro MTABLE_MALLOC but use + calloc instead of malloc, thus the allocated memory are zero + cleared. */ + +#define MTABLE_CALLOC(p, size, err) \ + do { \ + if (! ((p) = (void *) calloc (sizeof (*(p)), size))) \ + MEMORY_FULL (err); \ + } while (0) + + +/** The macro MTABLE_REALLOC () changes the size of memory block + pointed to by P to a size suitable for an array of SIZE objects. + The size of each object is determined by the type of P. ERR must + be one of enum MErrorCode. If the allocation fails, the macro + MEMORY_FULL () is called with argument ERR. */ + +#define MTABLE_REALLOC(p, size, err) \ + do { \ + if (! ((p) = (void *) realloc ((p), sizeof (*(p)) * (size)))) \ + MEMORY_FULL (err); \ + } while (0) + + +/** The macro MTABLE_ALLOCA () allocates memory (by alloca) for an + array of SIZE objects. The size of each object is determined by + the type of P. Then, it sets P to the allocated memory. ERR must + be one of enum MErrorCode. If the allocation fails, the macro + MEMORY_FULL () is called with argument ERR. */ + +#define MTABLE_ALLOCA(p, size, err) \ + do { \ + int bytes = sizeof (*(p)) * (size); \ + if (! ((p) = (void *) alloca (bytes))) \ + MEMORY_FULL (err); \ + memset ((p), 0, bytes); \ + } while (0) + + +/** short description of MSTRUCT_MALLOC */ +/** The macro MSTRUCT_MALLOC () allocates memory (by malloc) for an + object whose size is determined by the type of P, and sets P to + the allocated memory. ERR must be one of enum MErrorCode. If + the allocation fails, the macro MEMORY_FULL () is called with + argument ERR. */ + +#define MSTRUCT_MALLOC(p, err) \ + do { \ + if (! ((p) = (void *) malloc (sizeof (*(p))))) \ + MEMORY_FULL (err); \ + } while (0) + + +#define MSTRUCT_CALLOC(p, err) MTABLE_CALLOC ((p), 1, (err)) + + +/** Extendable array. */ + +#define MLIST_RESET(list) \ + ((list)->used = 0) + + +#define MLIST_INIT1(list, mem, increment) \ + do { \ + (list)->size = (list)->used = 0; \ + (list)->inc = (increment); \ + (list)->mem = NULL; \ + } while (0) + + +#define MLIST_APPEND1(list, mem, elt, err) \ + do { \ + if ((list)->inc <= 0) \ + mdebug_hook (); \ + if ((list)->size == (list)->used) \ + { \ + (list)->size += (list)->inc; \ + MTABLE_REALLOC ((list)->mem, (list)->size, (err)); \ + } \ + (list)->mem[(list)->used++] = (elt); \ + } while (0) + + +#define MLIST_PREPEND1(list, mem, elt, err) \ + do { \ + if ((list)->inc <= 0) \ + mdebug_hook (); \ + if ((list)->size == (list)->used) \ + { \ + (list)->size += (list)->inc; \ + MTABLE_REALLOC ((list)->mem, (list)->size, (err)); \ + } \ + memmove ((list)->mem + 1, (list)->mem, \ + sizeof *((list)->mem) * ((list)->used)); \ + (list)->mem[0] = (elt); \ + (list)->used++; \ + } while (0) + + +#define MLIST_INSERT1(list, mem, idx, len, err) \ + do { \ + while ((list)->used + (len) > (list)->size) \ + { \ + (list)->size += (list)->inc; \ + MTABLE_REALLOC ((list)->mem, (list)->size, (err)); \ + } \ + memmove ((list)->mem + ((idx) + (len)), (list)->mem + (idx), \ + (sizeof *((list)->mem)) * ((list)->used - (idx))); \ + (list)->used += (len); \ + } while (0) + + +#define MLIST_DELETE1(list, mem, idx, len) \ + do { \ + memmove ((list)->mem + (idx), (list)->mem + (idx) + (len), \ + (sizeof *((list)->mem)) * ((list)->used - (idx) - (len))); \ + (list)->used -= (len); \ + } while (0) + + +#define MLIST_COPY1(list0, list1, mem, err) \ + do { \ + (list0)->size = (list0)->used = (list1)->used; \ + (list0)->inc = 1; \ + MTABLE_MALLOC ((list0)->mem, (list0)->used, (err)); \ + memcpy ((list0)->mem, (list1)->mem, \ + (sizeof (list0)->mem) * (list0)->used); \ + } while (0) + + +#define MLIST_FREE1(list, mem) \ + if ((list)->size) \ + { \ + free ((list)->mem); \ + (list)->mem = NULL; \ + (list)->size = (list)->used = 0; \ + } \ + else + + + +typedef struct +{ + void (*freer) (void *); + int size, inc, used; + unsigned *counts; +} M17NObjectRecord; + +typedef struct +{ + /**en Reference count of the object. */ + unsigned ref_count : 16; + + unsigned ref_count_extended : 1; + + /**en A flag bit used for various perpose. */ + unsigned flag : 15; + + union { + /**en If is zero, a function to free the + object. */ + void (*freer) (void *); + /**en If is nonzero, a pointer to the + struct M17NObjectRecord. */ + M17NObjectRecord *record; + } u; +} M17NObject; + + +/** Allocate a managed object OBJECT which has freer FREE_FUNC. */ + +#define M17N_OBJECT(object, free_func, err) \ + do { \ + MSTRUCT_CALLOC ((object), (err)); \ + ((M17NObject *) (object))->ref_count = 1; \ + ((M17NObject *) (object))->u.freer = free_func; \ + } while (0) + + +/**en Increment the reference count of OBJECT if the count is not + 0. */ + +#define M17N_OBJECT_REF(object) \ + do { \ + if (((M17NObject *) (object))->ref_count_extended) \ + m17n_object_ref (object); \ + else if (((M17NObject *) (object))->ref_count > 0) \ + { \ + ((M17NObject *) (object))->ref_count++; \ + if (! ((M17NObject *) (object))->ref_count) \ + { \ + ((M17NObject *) (object))->ref_count--; \ + m17n_object_ref (object); \ + } \ + } \ + } while (0) + + +#define M17N_OBJECT_REF_NTIMES(object, n) \ + do { \ + int i; \ + \ + if (((M17NObject *) (object))->ref_count_extended) \ + for (i = 0; i < n; i++) \ + m17n_object_ref (object); \ + else if (((M17NObject *) (object))->ref_count > 0) \ + { \ + int orig_ref_count = ((M17NObject *) (object))->ref_count; \ + \ + for (i = 0; i < n; i++) \ + if (! ++((M17NObject *) (object))->ref_count) \ + { \ + ((M17NObject *) (object))->ref_count = orig_ref_count; \ + for (i = 0; i < n; i++) \ + m17n_object_ref (object); \ + } \ + } \ + } while (0) + + +/***en Decrement the reference count of OBJECT if the count is greater + than 0. In that case, if the count becomes 0, free OBJECT. */ + +#define M17N_OBJECT_UNREF(object) \ + do { \ + if (object) \ + { \ + if (((M17NObject *) (object))->ref_count_extended) \ + m17n_object_unref (object); \ + else if (((M17NObject *) (object))->ref_count == 0) \ + break; \ + else if (((M17NObject *) (object))->ref_count > 1) \ + ((M17NObject *) (object))->ref_count--; \ + else \ + { \ + if (((M17NObject *) (object))->u.freer) \ + (((M17NObject *) (object))->u.freer) (object); \ + else \ + free (object); \ + } \ + } \ + } while (0) + + +typedef struct +{ + int count; + int size, inc, used; + void **objects; +} M17NObjectArray; + + +#define M17N_OBJECT_REGISTER(array, object) \ + if (mdebug__flag & MDEBUG_FINI) \ + { \ + if ((array).count == 0) \ + MLIST_INIT1 (&(array), objects, 256); \ + (array).count++; \ + MLIST_APPEND1 (&(array), objects, object, MERROR_OBJECT); \ + } \ + else + +#define M17N_OBJECT_UNREGISTER(array, object) \ + if (mdebug__flag & MDEBUG_FINI) \ + { \ + (array).count--; \ + if ((array).count >= 0) \ + { \ + int i = 0; \ + \ + while (i < (array).used && (array).objects[i] != object) i++; \ + if (i < (array).used) \ + (array).objects[i] = NULL; \ + else \ + mdebug_hook (); \ + } \ + else \ + mdebug_hook (); \ + } \ + else + + +extern void mdebug__report_object (char *name, M17NObjectArray *array); + + + +struct MTextPlist; + +struct MText +{ + M17NObject control; + + enum MTextFormat format; + + /**en Number of characters in the M-text */ + /**ja M-text Ãæ¤Îʸ»ú¿ô */ + int nchars; + + /**en Number of bytes used to represent the characters in the M-text. */ + /**ja M-text Ãæ¤Îʸ»ú¤òɽ¤ï¤¹¤¿¤á¤ËÍѤ¤¤é¤ì¤ë¥Ð¥¤¥È¿ô */ + int nbytes; + + /**en Character sequence of the M-text. */ + /**ja M-text Ãæ¤Îʸ»úÎó */ + unsigned char *data; + + /**en Number of bytes allocated for the @c data member. */ + /**ja ¥á¥ó¥Ð @c data ¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥Ð¥¤¥È¿ô */ + int allocated; + + /**en Pointer to the property list of the M-text. */ + /**ja M-text ¤Î¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤Ø¤Î¥Ý¥¤¥ó¥¿ */ + struct MTextPlist *plist; + + /**en Caches of the character position and the corresponding byte position. */ + /**ja ʸ»ú°ÌÃÖ¤ª¤è¤ÓÂбþ¤¹¤ë¥Ð¥¤¥È°ÌÃ֤Υ­¥ã¥Ã¥·¥å */ + int cache_char_pos, cache_byte_pos; +}; + +/** short description of M_CHECK_POS */ +/** longer description of M_CHECK_POS */ + +#define M_CHECK_POS(mt, pos, ret) \ + do { \ + if ((pos) < 0 || (pos) >= (mt)->nchars) \ + MERROR (MERROR_RANGE, (ret)); \ + } while (0) + + +/** short description of M_CHECK_POS_X */ +/** longer description of M_CHECK_POS_X */ + +#define M_CHECK_POS_X(mt, pos, ret) \ + do { \ + if ((pos) < 0 || (pos) > (mt)->nchars) \ + MERROR (MERROR_RANGE, (ret)); \ + } while (0) + + +/** short description of M_CHECK_RANGE */ +/** longer description of M_CHECK_RANGE */ + +#define M_CHECK_RANGE(mt, from, to, ret, ret2) \ + do { \ + if ((from) < 0 || (to) < (from) || (to) > (mt)->nchars) \ + MERROR (MERROR_RANGE, (ret)); \ + if ((from) == (to)) \ + return (ret2); \ + } while (0) + +#define M_CHECK_RANGE_X(mt, from, to, ret) \ + do { \ + if ((from) < 0 || (to) < (from) || (to) > (mt)->nchars) \ + MERROR (MERROR_RANGE, (ret)); \ + } while (0) + + +#define M_CHECK_POS_NCHARS(mt, pos, nchars, ret, ret2) \ + do { \ + int to = (pos) + (nchars); \ + \ + M_CHECK_RANGE ((mt), (pos), (to), (ret), (ret2)); \ + } while (0) + + +#define M_CHECK_READONLY(mt, ret) \ + do { \ + if ((mt)->allocated < 0) \ + MERROR (MERROR_MTEXT, (ret)); \ + } while (0) + +#define mtext_nchars(mt) ((mt)->nchars) + +#define mtext_nbytes(mt) ((mt)->nbytes) + +#define mtext_allocated(mt) ((mt)->allocated) + +#define mtext_reset(mt) (mtext_del ((mt), 0, (mt)->nchars)) + + + +enum MDebugMaskBit + { + MDEBUG_INIT = 0x01, + MDEBUG_FINI = 0x02, + MDEBUG_CHARSET = 0x04, + MDEBUG_CODING = 0x08, + MDEBUG_DATABASE = 0x10, + MDEBUG_FONT = 0x0100, + MDEBUG_FONT_FLT = 0x0200, + MDEBUG_FONT_OTF = 0x0400, + MDEBUG_INPUT = 0x0800, + MDEBUG_MAX + }; + +extern int mdebug__flag; +extern void mdebug__push_time (); +extern void mdebug__pop_time (); +extern void mdebug__print_time (); + +#define MDEBUG_PRINT(msg) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + fprintf (stderr, (msg)); \ + } while (0) + +#define MDEBUG_PRINT1(fmt, arg) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + fprintf (stderr, (fmt), (arg)); \ + } while (0) + +#define MDEBUG_PRINT2(fmt, arg1, arg2) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + fprintf (stderr, (fmt), (arg1), (arg2)); \ + } while (0) + +#define MDEBUG_PRINT3(fmt, arg1, arg2, arg3) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + fprintf (stderr, (fmt), (arg1), (arg2), (arg3)); \ + } while (0) + +#define MDEBUG_PRINT4(fmt, arg1, arg2, arg3, arg4) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + fprintf (stderr, (fmt), (arg1), (arg2), (arg3), (arg4)); \ + } while (0) + + +#define MDEBUG_PUSH_TIME() \ + do { \ + if (mdebug__flag & mdebug_mask) \ + mdebug__push_time (); \ + } while (0) + + +#define MDEBUG_POP_TIME() \ + do { \ + if (mdebug__flag & mdebug_mask) \ + mdebug__pop_time (); \ + } while (0) + + +#define MDEBUG_PRINT_TIME(tag, ARG_LIST) \ + do { \ + if (mdebug__flag & mdebug_mask) \ + { \ + fprintf (stderr, " [%s] ", tag); \ + mdebug__print_time (); \ + fprintf ARG_LIST; \ + fprintf (stderr, "\n"); \ + } \ + } while (0) + + +#define SWAP_16(c) (((c) >> 8) | (((c) & 0xFF) << 8)) + +#define SWAP_32(c) \ + (((c) >> 24) | (((c) >> 8) & 0xFF00) \ + | (((c) & 0xFF00) << 8) | (((c) & 0xFF) << 24)) + + +extern void *(*mdatabase__finder) (MSymbol tag1, MSymbol tag2, + MSymbol tag3, MSymbol tag4); +extern void *(*mdatabase__loader) (void *); + +/* Initialize/finalize function. */ + +extern int msymbol__init (); +extern void msymbol__fini (); + +extern int mplist__init (); +extern void mplist__fini (); + +extern int mtext__init (); +extern void mtext__fini (); + +extern int mtext__prop_init (); +extern void mtext__prop_fini (); + +extern int mchartable__init (); +extern void mchartable__fini (); + +extern int mcharset__init (); +extern void mcharset__fini (); + +extern int mcoding__init (); +extern void mcoding__fini (); + +extern int mdatabase__init (void); +extern void mdatabase__fini (void); + +extern int mchar__init (); +extern void mchar__fini (); + +extern int mlang__init (); +extern void mlang__fini (); + +extern int mlocale__init (); +extern void mlocale__fini (); + +extern int minput__init (); +extern void minput__fini (); + +#endif /* _M17N_INTERNAL_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/language.c b/src/language.c new file mode 100644 index 0000000..02bc214 --- /dev/null +++ b/src/language.c @@ -0,0 +1,250 @@ +/* language.c -- language module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "language.h" +#include "symbol.h" + + +/* Internal API */ + +int +mlang__init () +{ + /* ISO 639 */ + struct { + char *name, *fullname; + } lang_rec[] = + { {"ab", "Abkhazian"}, + {"aa", "Afar"}, + {"af", "Afrikaans"}, + {"sq", "Albanian"}, + {"am", "Amharic"}, + {"ar", "Arabic"}, + {"hy", "Armenian"}, + {"as", "Assamese"}, + {"ay", "Aymara"}, + {"az", "Azerbaijani"}, + {"ba", "Bashkir"}, + {"eu", "Basque"}, + {"bn", "Bengali"}, /* Bangla */ + {"dz", "Bhutani"}, + {"bh", "Bihari"}, + {"bi", "Bislama"}, + {"br", "Breton"}, + {"bg", "Bulgarian"}, + {"my", "Burmese"}, + {"be", "Byelorussian"}, /* Belarusian */ + {"km", "Cambodian"}, /* Khmer */ + {"ca", "Catalan"}, +#if 0 + {"??", "Cherokee"}, + {"??", "Chewa"}, +#endif + {"zh", "Chinese"}, + {"co", "Corsican"}, + {"hr", "Croatian"}, + {"cs", "Czech"}, + {"da", "Danish"}, +#if 0 + {"??", "Divehi"}, +#endif + {"nl", "Dutch"}, +#if 0 + {"??", "Edo"}, +#endif + {"en", "English"}, + {"eo", "Esperanto"}, + {"et", "Estonian"}, + {"fo", "Faeroese"}, + {"fa", "Farsi"}, + {"fj", "Fiji"}, + {"fi", "Finnish"}, +#if 0 + {"??", "Flemish"}, +#endif + {"fr", "French"}, + {"fy", "Frisian"}, +#if 0 + {"??", "Fulfulde"}, +#endif + {"gl", "Galician"}, + {"gd", "Gaelic(Scottish)"}, /* Scottish */ + {"gv", "Gaelic(Manx)"}, /* Manx */ + {"ka", "Georgian"}, + {"de", "German"}, + {"el", "Greek"}, + {"kl", "Greenlandic"}, + {"gn", "Guarani"}, + {"gu", "Gujarati"}, + {"ha", "Hausa"}, +#if 0 + {"??", "Hawaiian"}, + {"iw", "Hebrew"}, +#endif + {"he", "Hebrew"}, + {"hi", "Hindi"}, + {"hu", "Hungarian"}, +#if 0 + {"??", "Ibibio"}, +#endif + {"is", "Icelandic"}, +#if 0 + {"??", "Igbo"}, + {"in", "Indonesian"}, +#endif + {"id", "Indonesian"}, +#if 0 + {"ia", "Interlingua"}, + {"ie", "Interlingue"}, +#endif + {"iu", "Inuktitut"}, + {"ik", "Inupiak"}, + {"ga", "Irish"}, + {"it", "Italian"}, + {"ja", "Japanese"}, + {"jw", "Javanese"}, + {"kn", "Kannada"}, +#if 0 + {"??", "Kanuri"}, +#endif + {"ks", "Kashmiri"}, + {"kk", "Kazakh"}, + {"rw", "Kinyarwanda"}, /* Ruanda */ + {"ky", "Kirghiz"}, + {"rn", "Kirundi"}, /* Rundi */ + {"ko", "Korean"}, + {"ku", "Kurdish"}, + {"lo", "Laothian"}, + {"la", "Latin"}, + {"lv", "Latvian"}, /* Lettish */ + {"ln", "Lingala"}, + {"lt", "Lithuanian"}, + {"mk", "Macedonian"}, + {"mg", "Malagasy"}, + {"ms", "Malay"}, + {"ml", "Malayalam"}, +#if 0 + {"??", "Manipuri"}, +#endif + {"mt", "Maltese"}, + {"mi", "Maori"}, + {"mr", "Marathi"}, + {"mo", "Moldavian"}, + {"mn", "Mongolian"}, + {"na", "Nauru"}, + {"ne", "Nepali"}, + {"no", "Norwegian"}, + {"oc", "Occitan"}, + {"or", "Oriya"}, + {"om", "Oromo"}, /* Afan, Galla */ +#if 0 + {"??", "Papiamentu"}, +#endif + {"ps", "Pashto"}, /* Pushto */ + {"pl", "Polish"}, + {"pt", "Portuguese"}, + {"pa", "Punjabi"}, + {"qu", "Quechua"}, + {"rm", "Rhaeto-Romance"}, + {"ro", "Romanian"}, + {"ru", "Russian"}, +#if 0 + {"??", "Sami"}, /* Lappish */ +#endif + {"sm", "Samoan"}, + {"sg", "Sangro"}, + {"sa", "Sanskrit"}, + {"sr", "Serbian"}, + {"sh", "Serbo-Croatian"}, + {"st", "Sesotho"}, + {"tn", "Setswana"}, + {"sn", "Shona"}, + {"sd", "Sindhi"}, + {"si", "Sinhalese"}, + {"ss", "Siswati"}, + {"sk", "Slovak"}, + {"sl", "Slovenian"}, + {"so", "Somali"}, + {"es", "Spanish"}, + {"su", "Sundanese"}, + {"sw", "Swahili"}, /* Kiswahili */ + {"sv", "Swedish"}, +#if 0 + {"??", "Syriac"}, +#endif + {"tl", "Tagalog"}, + {"tg", "Tajik"}, +#if 0 + {"??", "Tamazight"}, +#endif + {"ta", "Tamil"}, + {"tt", "Tatar"}, + {"te", "Telugu"}, + {"th", "Thai"}, + {"bo", "Tibetan"}, + {"ti", "Tigrinya"}, + {"to", "Tonga"}, + {"ts", "Tsonga"}, + {"tr", "Turkish"}, + {"tk", "Turkmen"}, + {"tw", "Twi"}, + {"ug", "Uighur"}, + {"uk", "Ukrainian"}, + {"ur", "Urdu"}, + {"uz", "Uzbek"}, +#if 0 + {"??", "Venda"}, +#endif + {"vi", "Vietnamese"}, + {"vo", "Volapuk"}, + {"cy", "Welsh"}, + {"wo", "Wolof"}, + {"xh", "Xhosa"}, +#if 0 + {"??", "Yi"}, + {"ji", "Yiddish"}, +#endif + {"yi", "Yiddish"}, + {"yo", "Yoruba"}, + {"zu", "Zulu"} }; + int i; + + Mlanguage = msymbol ("language"); + msymbol_put (Mlanguage, Mtext_prop_serializer, + (void *) msymbol__serializer); + msymbol_put (Mlanguage, Mtext_prop_deserializer, + (void *) msymbol__deserializer); + for (i = 0; i < ((sizeof lang_rec) / (sizeof lang_rec[0])); i++) + msymbol_put (msymbol (lang_rec[i].name), Mlanguage, + msymbol (lang_rec[i].fullname)); + return 0; +} + +void +mlang__fini (void) +{ +} diff --git a/src/language.h b/src/language.h new file mode 100644 index 0000000..3e495d1 --- /dev/null +++ b/src/language.h @@ -0,0 +1,26 @@ +/* language.h -- header file for the language module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_LANGUAGE_H_ +#define _M17N_LANGUAGE_H_ + +#endif /* _M17N_LANGUAGE_H_ */ diff --git a/src/linkcore.c b/src/linkcore.c new file mode 100644 index 0000000..4a5da33 --- /dev/null +++ b/src/linkcore.c @@ -0,0 +1,32 @@ +/* linkcore.c -- test program for linking with m17n-core.so. + Copyright (C) 2003 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + +This file is part of the m17n library. + +The m17n library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2, or (at +your option) any later version. + +The m17n library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the m17n library; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include +#include "m17n-core.h" + +int +main () +{ + M17N_INIT (); + M17N_FINI (); + exit (0); +} diff --git a/src/linkgui.c b/src/linkgui.c new file mode 100644 index 0000000..5b70689 --- /dev/null +++ b/src/linkgui.c @@ -0,0 +1,33 @@ +/* linkgui.c -- test program for linking with m17n-X.so. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include "m17n-gui.h" +#include "m17n-misc.h" + +int +main () +{ + M17N_INIT (); + M17N_FINI (); + exit (0); +} diff --git a/src/linkshell.c b/src/linkshell.c new file mode 100644 index 0000000..7190e4d --- /dev/null +++ b/src/linkshell.c @@ -0,0 +1,32 @@ +/* linkshell.c -- test program for linking with m17n.so. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include + +int +main () +{ + M17N_INIT (); + M17N_FINI (); + exit (0); +} diff --git a/src/locale.c b/src/locale.c new file mode 100644 index 0000000..a20a23c --- /dev/null +++ b/src/locale.c @@ -0,0 +1,610 @@ +/* locale.c -- locale module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nLocale + @brief Locale objects and API for them + + The m17n library represents locale related information as objects + of type #MLocale. */ + +/***ja + @addtogroup m17nLocale + @brief ¥í¥±¡¼¥ë¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¥í¥±¡¼¥ë´ØÏ¢¾ðÊó¤ò #MLocale ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç + ɽ¸½¤¹¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#ifdef HAVE_LANGINFO_H +#include +#endif +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "coding.h" +#include "textprop.h" +#include "mlocale.h" + +static MSymbol M_locale; +static MSymbol M_xfrm; + + +/** Structure of locales. */ + +struct MLocale +{ + M17NObject control; + MSymbol name; + MSymbol language; + MSymbol territory; + MSymbol modifier; + MSymbol codeset; + MSymbol coding; +}; + + +/** The current locales of each category. */ +MLocale *mlocale__collate, *mlocale__ctype; +MLocale *mlocale__messages, *mlocale__time; + +/* These are currently not used. */ +#if 0 +MLocale *mlocale_monetary, *mlocale_numeric, ; +#endif + +/** Parse locale name NAME and return a newly created MLocale object. + If the locale is not supported by the system, return NULL. */ + +static MLocale * +make_locale (const char *name) +{ + char *current, *new, *str; + int len; + MLocale *locale; + char c; + + str = setlocale (LC_CTYPE, NULL); + len = strlen (str) + 1; + current = alloca (len); + memcpy (current, str, len); + + if (! (new = setlocale (LC_CTYPE, name))) + return NULL; + + + M17N_OBJECT (locale, NULL, MERROR_LOCALE); + locale->name = msymbol (new); + msymbol_put (locale->name, M_locale, (void *) locale); + M17N_OBJECT_UNREF (locale); + + len = strlen (new) + 1; + str = alloca (len); + memcpy (str, new, len); + + c = '\0'; + while (1) + { + char c1; + int i; + + for (i = 0; str[i]; i++) + if (str[i] == '_' || str[i] == '.' || str[i] == '@') + break; + c1 = str[i]; + str[i] = '\0'; + if (c == '\0') + /* The first field is for language. */ + locale->language = msymbol (name); + else if (c == '_') + /* The field following '_' is for territory. */ + locale->territory = msymbol (name); + else if (c == '.') + /* The field following '.' is for codeset. */ + locale->codeset = msymbol (name); + else + /* The other field is for modifier. */ + locale->modifier = msymbol (name); + if (! c1) + break; + c = c1; + name += i + 1; + } + +#ifdef HAVE_NL_LANGINFO +#ifdef CODESET + /* If we can use nl_langinfo () to retrieve a codeset name, respect + it over the codeset name extracted from the locale name. */ + locale->codeset = msymbol (nl_langinfo (CODESET)); +#endif +#endif + + /* If the locale name specifies a codeset, get the corresponding + coding system. */ + if (locale->codeset != Mnil) + { + locale->coding = mconv_resolve_coding (locale->codeset); + if (locale->coding == Mnil) + locale->coding = Mcoding_us_ascii; + } + else + locale->coding = Mcoding_us_ascii; + + setlocale (LC_CTYPE, current); + return locale; +} + + +/** Decode the byte sequence at BUF of length SIZE bytes by the coding + system associated with LOCALE, and return a generated M-text. */ + +static MText * +decode_locale (unsigned char *buf, int size, MLocale *locale) +{ + return mconv_decode_buffer (locale->coding, buf, size); +} + + +/** Encode the M-text MT by the coding system associated with LOCALE, + and store the resulting bytes in the memory area at BUF of *SIZE + bytes. If the area is too short, allocate a new and wider area. + Store the length of the generated bytes in the place pointed by + SIZE, and return the address of those bytes. */ + +static unsigned char * +encode_locale (MText *mt, unsigned char *buf, int *size, MLocale *locale) +{ + int nbytes = mconv_encode_buffer (locale->coding, mt, buf, *size - 1); + + if (nbytes < 0) + { + buf = NULL; + *size *= 2; + do { + MTABLE_REALLOC (buf, *size, MERROR_LOCALE); + nbytes = mconv_encode_buffer (mlocale__ctype->coding, mt, buf, + *size - 1); + } while (nbytes < 0); + } + buf[nbytes] = '\0'; + *size = nbytes; + return buf; +} + + +/** Structure of transformed strings. The function mtext_coll () + caches this object in an M-text as a text property. */ + +typedef struct { + /* Common header for a managed object. */ + M17NObject control; + + /* Locale corresponding to . */ + MLocale *locale; + + /** Result of strxfrm. */ + char *str; +} MXfrm; + + +static void +free_xfrm (void *object) +{ + MXfrm *xfrm = (MXfrm *) object; + + M17N_OBJECT_UNREF (xfrm->locale); + free (xfrm->str); +} + +static char * +get_xfrm (MText *mt) +{ + MTextProperty *prop = mtext_get_property (mt, 0, M_xfrm); + MXfrm *xfrm; + int size; + unsigned char *buf, *newbuf; + int request; + + if (prop) + { + if (prop->end == mt->nchars) + { + xfrm = (MXfrm *) prop->val; + if (xfrm->locale == mlocale__ctype) + return xfrm->str; + } + mtext_detach_property (prop); + } + + size = mt->nbytes; + buf = alloca (size); + newbuf = encode_locale (mt, buf, &size, mlocale__ctype); + M17N_OBJECT (xfrm, free_xfrm, MERROR_MTEXT); + xfrm->str = malloc (size); + request = strxfrm (xfrm->str, (char *) newbuf, size); + if (request >= size) + { + xfrm->str = realloc (xfrm->str, request); + strxfrm (xfrm->str, (char *) newbuf, size); + } + if (buf != newbuf) + free (newbuf); + prop = mtext_property (M_xfrm, xfrm, MTEXTPROP_VOLATILE_WEAK); + mtext_attach_property (mt, 0, mt->nchars, prop); + M17N_OBJECT_UNREF (prop); + return xfrm->str; +} + + +/* Internal API */ + +int +mlocale__init () +{ + M_locale = msymbol_as_managing_key (" locale"); + + Mlanguage = msymbol ("language"); + Mterritory = msymbol ("territory"); + Mcodeset = msymbol ("codeset"); + + mlocale__collate = mlocale_set (LC_COLLATE, NULL); + M17N_OBJECT_REF (mlocale__collate); + mlocale__ctype = mlocale_set (LC_CTYPE, NULL); + M17N_OBJECT_REF (mlocale__ctype); + mlocale__messages = mlocale_set (LC_MESSAGES, NULL); + M17N_OBJECT_REF (mlocale__messages); + mlocale__time = mlocale_set (LC_TIME, NULL); + M17N_OBJECT_REF (mlocale__time); + + M_xfrm = msymbol_as_managing_key (" xfrm"); + return 0; +} + +void +mlocale__fini () +{ + M17N_OBJECT_UNREF (mlocale__collate); + M17N_OBJECT_UNREF (mlocale__ctype); + M17N_OBJECT_UNREF (mlocale__messages); + M17N_OBJECT_UNREF (mlocale__time); +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ +/*** @addtogroup m17nLocale */ +/*** @{ */ + +/*=*/ +/***en The symbol whose name is "language". */ +/***ja ¥·¥ó¥Ü¥ë "language" */ +MSymbol Mlanguage; + +/*=*/ +/***en The symbol whose name is "territory". */ +/***ja ¥·¥ó¥Ü¥ë "territory" */ +MSymbol Mterritory; + +/*=*/ +/***en The symbol whose name is "modifier". */ +/***ja ¥·¥ó¥Ü¥ë "modifier" */ +MSymbol Mmodifier; + +/*=*/ +/***en The symbol whose name is "codeset". */ +/***ja ¥·¥ó¥Ü¥ë "codeset" */ +MSymbol Mcodeset; + +/*=*/ + +/***en + @brief Set the current locale. + + The mlocale_set () function sets or query a part of the current + locale. The part is specified by $CATEGORY which must be a valid + first argument to setlocale (). + + If $LOCALE is not NULL, the locale of the specified part is set to + $LOCALE. If $LOCALE is not supported by the system, the current + locale is not changed. + + If $LOCALE is NULL, the current locale of the specified part is + queried. + + @return + If the call is successful, mlocale_set () returns an opaque locale + object that corresponds to the locale. The name of the locale can + be acquired by the function mlocale_get_prop (). + + Otherwise, it returns NULL. */ + +/***ja + @brief ¸½ºß¤Î¥í¥±¡¼¥ë¤òÀßÄꤹ¤ë. + + ´Ø¿ô mlocale_set () ¤Ï $LOCALE_NAME ¤ò¸½ºß¤Î¥í¥±¡¼¥ë¤È¤¹¤ë¡£¤³¤Î´Ø + ¿ô¤Ï¥·¥¹¥Æ¥à´Ø¿ô setlocale () ¤ò¸Æ¤Ó¡¢³°ÉôÊÑ¿ô @c + mlocale_current ¤òÀßÄꤹ¤ë¡£ + + @return + ¥·¥¹¥Æ¥à¤¬ $LOCALE_NAME ¤ò¥µ¥Ý¡¼¥È¤¹¤ë¤Ê¤é¤Ð mlocale_set () ¤Ï 0 + ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ + ¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_LOCALE */ + +MLocale * +mlocale_set (int category, const char *name) +{ + char *new; + MLocale *locale; + + new = setlocale (category, name); + if (! new) + return NULL; + + locale = (MLocale *) msymbol_get (msymbol (new), M_locale); + if (! locale) + locale = make_locale (new); + if (! locale) + return NULL; + if (name && (category == LC_ALL || category == LC_COLLATE)) + { + M17N_OBJECT_UNREF (mlocale__collate); + M17N_OBJECT_REF (locale); + mlocale__collate = locale; + } + else if (name && (category == LC_ALL || category == LC_CTYPE)) + { + M17N_OBJECT_UNREF (mlocale__ctype); + M17N_OBJECT_REF (locale); + mlocale__ctype = locale; + } + if (name && (category == LC_ALL || category == LC_MESSAGES)) + { + M17N_OBJECT_UNREF (mlocale__messages); + M17N_OBJECT_REF (locale); + mlocale__messages = locale; + } + if (name && (category == LC_ALL || category == LC_TIME)) + { + M17N_OBJECT_UNREF (mlocale__time); + M17N_OBJECT_REF (locale); + mlocale__time = locale; + } + return locale; +} + +/*=*/ + +/***en + @brief Get the value of a locale property. + + The mlocale_get_prop () function returns the value of a property + $KEY of local $LOCALE. $KEY must be #Mname, #Mlanguage, + #Mterritory, #Mcodeset, #Mmodifier, or #Mcoding. */ + +/***ja + @brief ¥í¥±¡¼¥ë¤Î¥×¥í¥Ñ¥Æ¥£ÃͤòÆÀ¤ë + + ´Ø¿ô mlocale_get_prop () ¤Ï¡¢¥í¥±¡¼¥ë $LOCALE ¤Î $KEY ¥×¥í¥Ñ¥Æ¥£¤Î + ÃͤòÊÖ¤¹¡£ $KEY ¤Ï #Mname ¡¢ #Mlanguage ¡¢ #Mterritory ¡¢ + #Mcodeset ¡¢ #Mmodifier ¤â¤·¤¯¤Ï #Mcoding ¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê + ¤¤¡£ */ + +MSymbol +mlocale_get_prop (MLocale *locale, MSymbol key) +{ + if (key == Mcoding) + return locale->coding; + if (key == Mname) + return locale->name; + if (key == Mlanguage) + return locale->language; + if (key == Mterritory) + return locale->territory; + if (key == Mcodeset) + return locale->codeset; + if (key == Mmodifier) + return locale->modifier; + return Mnil; +} + +/*=*/ +/***en + @brief Format date and time + + The mtext_ftime () function formats the broken-down time $TM + according to the format specification $FORMAT and append the + result to the M-text $MT. The formating is done according to the + locale $LOCALE (if not NULL) or the current locale (LC_TIME). + + The meaning of the arguments $TM and $FORMAT are the same as those + of strftime (). + + @seealso + strftime () + +*/ + +int +mtext_ftime (MText *mt, const char *format, const struct tm *tm, + MLocale *locale) +{ + int bufsize; + unsigned char *buf; + size_t nbytes, nchars; + char *current_locale = NULL; + + if (locale) + { + char *str = setlocale (LC_TIME, NULL); + int len = strlen (str) + 1; + + current_locale = alloca (len); + memcpy (current_locale, str, len); + mlocale_set (LC_TIME, msymbol_name (locale->name)); + } + + bufsize = 1024; + while (1) + { + MTABLE_ALLOCA (buf, bufsize, MERROR_MTEXT); + buf[0] = 1; + nbytes = strftime ((char *) buf, bufsize, format, tm); + if (nbytes > 0 + || ! buf[0]) + break; + bufsize *= 2; + } + + if (nbytes > 0) + { + MText *work = decode_locale (buf, nbytes, mlocale__time); + + if (work) + { + nchars = work->nchars; + mtext_cat (mt, work); + M17N_OBJECT_UNREF (work); + } + else + nchars = 0; + } + else + nchars = 0; + + if (current_locale) + mlocale_set (LC_TIME, current_locale); + + return nchars; +} + +/*=*/ + +/***en + @brief Get an environment variable + + The mtext_getenv () function searches the environment list for a + string that matches the string pointed to by $NAME. + + If there is a match, the function decodes the value according to + the current locale (LC_CTYPE) into an M-text, and return that + M-text. + + If there is no match, the function returns NULL. */ + +MText * +mtext_getenv (const char *name) +{ + char *p = getenv (name); + + if (!p) + return NULL; + return decode_locale ((unsigned char *) p, strlen (p), mlocale__ctype); +} + +/*=*/ + +/***en + @brief Change or add an environment variable. + + The mtext_putenv () function adds or changed the value of + environment variables according to M-text $MT. It simply calls + the function putenv with an argument generated by encoding $MT + according to the current locale (LC_CTYPE). + + @return + This function returns zero on success, or -1 if an error + occurs. */ + +int +mtext_putenv (MText *mt) +{ + unsigned char buf[1024]; + int size = 1024; + unsigned char *newbuf; + int result; + + newbuf = encode_locale (mt, buf, &size, mlocale__ctype); + result = putenv ((char *) newbuf); + if (buf != newbuf) + free (newbuf); + return result; +} + +/*=*/ + +/***en + @brief Compare two M-texts using the current locale. + + The mtext_coll () function compares the two M-texts $MT1 and $MT2. + It returns an integer less than, equal to, or greater than zero if + $MT1 is found, respectively, to be less than, to match, or to be + greater than $MT2. The comparison is based on texts as + appropriate for the current locale (LC_COLLATE). + + This function makes use of information that is automatically + cached in the M-texts as a text property. So, the second call of + this function with $MT1 or $MT2 finishes faster than the first + call. */ + +int +mtext_coll (MText *mt1, MText *mt2) +{ + char *str1, *str2; + + if (mt1->nchars == 0) + return (mt2->nchars == 0 ? 0 : -1); + else if (mt2->nchars == 0) + return 1; + + str1 = get_xfrm (mt1); + str2 = get_xfrm (mt2); + return strcoll (str1, str2); +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-X.c b/src/m17n-X.c new file mode 100644 index 0000000..974dd22 --- /dev/null +++ b/src/m17n-X.c @@ -0,0 +1,2156 @@ +/* m17n-X.c -- implementation of the GUI API on X Windows. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-X.h" +#include "m17n-misc.h" +#include "internal.h" +#include "internal-gui.h" +#include "symbol.h" +#include "input.h" +#include "font.h" +#include "fontset.h" +#include "face.h" + +typedef struct +{ + /* Common header for the m17n object. */ + M17NObject control; + + Display *display; + + /* If nonzero, is opened by this library. Thus it should + be closed on freeing this structure. */ + int auto_display; + + /** List of available fonts on the display (except for iso8859-1 and + iso10646-1 fonts). Keys are font registries, values are + (MFontList *). */ + MPlist *font_registry_list; + + MPlist *iso8859_1_family_list; + + MPlist *iso10646_1_family_list; + + /* List of information about each font. Keys are font registries, + values are (MFontInfo *). */ + MPlist *realized_font_list; + + /** Modifier bit masks of the display. */ + int meta_mask; + int alt_mask; + int super_mask; + int hyper_mask; +} MDisplayInfo; + +/* Anchor of the chain of MDisplayInfo objects. */ +static MPlist *display_info_list; + +struct MWDevice +{ + /* Common header for the m17n object. */ + M17NObject control; + + MDisplayInfo *display_info; + + int screen_num; + + Drawable drawable; + + unsigned depth; + + Colormap cmap; + + unsigned long foreground, background; + + /** List of pointers to realized faces on the frame. */ + MPlist *realized_face_list; + + /** List of pointers to realized fontsets on the frame. */ + MPlist *realized_fontset_list; + +}; + +static MPlist *device_list; + +static MSymbol M_iso8859_1, M_iso10646_1; + +#define FRAME_DISPLAY(frame) (frame->device->display_info->display) +#define FRAME_SCREEN(frame) (frame->device->screen_num) + +static void +free_display_info (void *object) +{ + MDisplayInfo *disp_info = (MDisplayInfo *) object; + MPlist *plist; + + for (plist = disp_info->font_registry_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + { + MFontList *registry_list = mplist_value (plist); + + if (registry_list->fonts) + free (registry_list->fonts); + free (registry_list); + } + M17N_OBJECT_UNREF (disp_info->font_registry_list); + + for (plist = disp_info->iso8859_1_family_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + { + MFontList *family_list = mplist_value (plist); + + if (family_list->fonts) + free (family_list->fonts); + free (family_list); + } + M17N_OBJECT_UNREF (disp_info->iso8859_1_family_list); + + for (plist = disp_info->iso10646_1_family_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + { + MFontList *family_list = mplist_value (plist); + + if (family_list->fonts) + free (family_list->fonts); + free (family_list); + } + M17N_OBJECT_UNREF (disp_info->iso10646_1_family_list); + + for (plist = disp_info->realized_font_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + mfont__free_realized ((MRealizedFont *) mplist_value (plist)); + M17N_OBJECT_UNREF (disp_info->realized_font_list); + + if (disp_info->auto_display) + XCloseDisplay (disp_info->display); + + free (object); +} + +static void +free_device (void *object) +{ + MWDevice *device = (MWDevice *) object; + MPlist *plist; + + for (plist = device->realized_fontset_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + mfont__free_realized_fontset ((MRealizedFontset *) mplist_value (plist)); + M17N_OBJECT_UNREF (device->realized_fontset_list); + + for (plist = device->realized_face_list; + mplist_key (plist) != Mnil; plist = mplist_next (plist)) + mface__free_realized ((MRealizedFace *) mplist_value (plist)); + M17N_OBJECT_UNREF (device->realized_face_list); + + XFreePixmap (device->display_info->display, device->drawable); + M17N_OBJECT_UNREF (device->display_info); + free (object); +} + + +static void +find_modifier_bits (MDisplayInfo *disp_info) +{ + Display *display = disp_info->display; + XModifierKeymap *mods; + KeyCode meta_l = XKeysymToKeycode (display, XK_Meta_L); + KeyCode meta_r = XKeysymToKeycode (display, XK_Meta_R); + KeyCode alt_l = XKeysymToKeycode (display, XK_Alt_L); + KeyCode alt_r = XKeysymToKeycode (display, XK_Alt_R); + KeyCode super_l = XKeysymToKeycode (display, XK_Super_L); + KeyCode super_r = XKeysymToKeycode (display, XK_Super_R); + KeyCode hyper_l = XKeysymToKeycode (display, XK_Hyper_L); + KeyCode hyper_r = XKeysymToKeycode (display, XK_Hyper_R); + int i, j; + + mods = XGetModifierMapping (display); + /* We skip the first three sets for Shift, Lock, and Control. The + remaining sets are for Mod1, Mod2, Mod3, Mod4, and Mod5. */ + for (i = 3; i < 8; i++) + for (j = 0; j < mods->max_keypermod; j++) + { + KeyCode code = mods->modifiermap[i * mods->max_keypermod + j]; + + if (! code) + continue; + if (code == meta_l || code == meta_r) + disp_info->meta_mask |= (1 << i); + else if (code == alt_l || code == alt_r) + disp_info->alt_mask |= (1 << i); + else if (code == super_l || code == super_r) + disp_info->super_mask |= (1 << i); + else if (code == hyper_l || code == hyper_r) + disp_info->hyper_mask |= (1 << i); + } + + /* If meta keys are not in any modifier, use alt keys as meta + keys. */ + if (! disp_info->meta_mask) + { + disp_info->meta_mask = disp_info->alt_mask; + disp_info->alt_mask = 0; + } + /* If both meta and alt are assigned to the same modifier, give meta + keys priority. */ + if (disp_info->meta_mask & disp_info->alt_mask) + disp_info->alt_mask &= ~disp_info->meta_mask; + + XFreeModifiermap (mods); +} + +unsigned long +get_color (Display *display, Colormap cmap, + MSymbol color_name, MSymbol default_name, + unsigned long default_pixel) +{ + XColor exact_def; + + if (XParseColor (display, cmap, msymbol_name (color_name), &exact_def) + && XAllocColor (display, cmap, &exact_def)) + return exact_def.pixel; + + if (XParseColor (display, cmap, msymbol_name (default_name), &exact_def) + && XAllocColor (display, cmap, &exact_def)) + return exact_def.pixel; + + return default_pixel; +} + + +/** X font handler */ + +/** Indices to each field of split font name. */ + +enum xlfd_field_idx + { + XLFD_FOUNDRY, + XLFD_FAMILY, + XLFD_WEIGHT, + XLFD_SLANT, + XLFD_SWIDTH, + XLFD_ADSTYLE, + XLFD_PIXEL, + XLFD_POINT, + XLFD_RESX, + XLFD_RESY, + XLFD_SPACING, + XLFD_AVGWIDTH, + XLFD_REGISTRY, + XLFD_ENCODING, + /* anchor */ + XLFD_FIELD_MAX + }; + +/** Split the fontname NAME into each XLFD field destructively. Set + each element of the table pointed by PROPERTY_IDX to a pointer to + the corresponding font property name. Store the point size and + the resolution-Y of the font to the place pointed by POINT and + RESY respectively. + + If NAME does not contain all XLFD fields, the unspecified fields is + treated as wild cards. */ + +static int +split_font_name (char *name, char **field, + unsigned short *size, unsigned short *resy) +{ + int i; + char *p; + + for (i = 0, p = name; *p; p++) + { + *p = tolower (*p); + if (*p == '-' && i < XLFD_FIELD_MAX) + { + field[i] = p + 1; + if (i != XLFD_ENCODING) + *p = '\0'; + i++; + } + } + if (i < XLFD_REGISTRY) + return -1; + for (; i < XLFD_FIELD_MAX; i++) + field[i] = "*"; + + if (*(field[XLFD_RESY]) == '*') + *resy = 0; + else + *resy = atoi (field[XLFD_RESY]); + if (*(field[XLFD_PIXEL]) == '*') + { + if (*(field[XLFD_POINT]) != '*') + *size = atoi (field[XLFD_POINT]) * *resy / 72; + else + *size = 0; + } + else if (*(field[XLFD_PIXEL]) == '[') + { + /* The pixel size field specifies a transformation matrix of the + form "[A B C D]". The XLFD spec says that the scalar value N + for the pixel size is equivalent to D. */ + char *p0 = field[XLFD_PIXEL] + 1, *p1; + double d; + + for (i = 0; i < 4; i++, p0 = p1) + d = strtod (p0, &p1); + *size = d * 10; + } + else + *size = atoi (field[XLFD_PIXEL]) * 10; + if (*size == 0 && *(field[XLFD_POINT]) != '*') + { + *size = atoi (field[XLFD_POINT]); + if (*resy) + *size = *size * *resy / 72; + else + *size = *size * 100 / 72; + } + + return 0; +} + +static int +build_font_name (MFont *font, char *name, int limit) +{ + MSymbol prop[7]; + char *str[7]; + int len, i; + unsigned short size, resy; + + prop[0] = (MSymbol) mfont_get_prop (font, Mfoundry); + prop[1] = (MSymbol) mfont_get_prop (font, Mfamily); + prop[2] = (MSymbol) mfont_get_prop (font, Mweight); + prop[3] = (MSymbol) mfont_get_prop (font, Mstyle); + prop[4] = (MSymbol) mfont_get_prop (font, Mstretch); + prop[5] = (MSymbol) mfont_get_prop (font, Madstyle); + prop[6] = (MSymbol) mfont_get_prop (font, Mregistry); + for (len = 0, i = 0; i < 7; i++) + { + if (prop[i] != Mnil) + { + str[i] = msymbol_name (prop[i]); + len += strlen (str[i]); + } + else + { + str[i] = "*"; + len++; + } + } + if ((len + + 12 /* 12 dashes */ + + 3 /* 3 asterisks */ + + 30 /* 3 integers (each 10 digits) */ + + 1) /* '\0' terminal */ + > limit) + return -1; + + size = (int) mfont_get_prop (font, Msize); + if ((size % 10) < 5) + size /= 10; + else + size = size / 10 + 1; + resy = (int) mfont_get_prop (font, Mresolution); + + sprintf (name, "-%s-%s-%s-%s-%s-%s-%d-*-%d-%d-*-*-%s", + str[0], str[1], str[2], str[3], str[4], str[5], + size, resy, resy, str[6]); + return 0; +} + +static MFontList * +build_font_list (MFrame *frame, MSymbol family, MSymbol registry, + MPlist *plist) +{ + char pattern[1024]; + MFontList *font_list; + char **fontnames; + int nfonts; + int i, j; + + MSTRUCT_CALLOC (font_list, MERROR_WIN); + + if (family == Mnil) + { + sprintf (pattern, "-*-*-*-*-*-*-*-*-*-*-*-*-%s", + msymbol_name (registry)); + font_list->tag = registry; + } + else + { + sprintf (pattern, "-*-%s-*-*-*-*-*-*-*-*-*-*-%s", + msymbol_name (family), msymbol_name (registry)); + font_list->tag = family; + } + + fontnames = XListFonts (FRAME_DISPLAY (frame), pattern, 0x8000, &nfonts); + if (nfonts > 0) + { + MTABLE_MALLOC (font_list->fonts, nfonts, MERROR_WIN); + for (i = j = 0; i < nfonts; i++) + if (mwin__parse_font_name (fontnames[i], font_list->fonts + j) >= 0 + && (font_list->fonts[j].property[MFONT_SIZE] != 0 + || font_list->fonts[j].property[MFONT_RESY] == 0)) + { + font_list->fonts[j].property[MFONT_TYPE] = MFONT_TYPE_WIN + 1; + j++; + } + XFreeFontNames (fontnames); + font_list->nfonts = j; + } + mplist_add (plist, font_list->tag, font_list); + return (nfonts > 0 ? font_list : NULL); +} + + +static MRealizedFont *xfont_select (MFrame *, MFont *, MFont *, int); +static int xfont_open (MRealizedFont *); +static void xfont_close (MRealizedFont *); +static void xfont_find_metric (MRealizedFont *, MGlyph *); +static unsigned xfont_encode_char (MRealizedFont *, int, unsigned); +static void xfont_render (MDrawWindow, int, int, MGlyphString *, + MGlyph *, MGlyph *, int, MDrawRegion); + +MFontDriver xfont_driver = + { xfont_select, xfont_open, xfont_close, + xfont_find_metric, xfont_encode_char, xfont_render }; + +/* The X font driver function SELECT. */ + +static MRealizedFont * +xfont_select (MFrame *frame, MFont *spec, MFont *request, int limited_size) +{ + MSymbol registry = FONT_PROPERTY (spec, MFONT_REGISTRY); + MRealizedFont *rfont; + MFontList *font_list = NULL; + int i; + MFont *best_font; + int best_score, score; + + if (registry == Mnil + || ! strchr (MSYMBOL_NAME (registry), '-')) + return NULL; + + /* We handles iso8859-1 and iso10646-1 fonts specially because there + exists so many such fonts. */ + if (registry == M_iso8859_1 || registry == M_iso10646_1) + { + MPlist *family_list + = (registry == M_iso8859_1 + ? frame->device->display_info->iso8859_1_family_list + : frame->device->display_info->iso10646_1_family_list); + MSymbol family = FONT_PROPERTY (spec, MFONT_FAMILY); + + if (family != Mnil) + { + font_list = (MFontList *) mplist_get (family_list, family); + if (! font_list) + font_list = build_font_list (frame, family, registry, family_list); + } + if (! font_list) + { + family = FONT_PROPERTY (request, MFONT_FAMILY); + font_list = (MFontList *) mplist_get (family_list, family); + if (! font_list) + font_list = build_font_list (frame, family, registry, family_list); + } + } + if (! font_list) + { + MPlist *registry_list + = frame->device->display_info->font_registry_list; + + font_list = (MFontList *) mplist_get (registry_list, registry); + if (! font_list) + font_list = build_font_list (frame, Mnil, registry, registry_list); + } + if (! font_list) + return NULL; + + for (i = 0, best_score = -1, best_font = NULL; i < font_list->nfonts; i++) + if ((best_score = mfont__score (font_list->fonts + i, spec, request, + limited_size)) >= 0) + break; + if (best_score < 0) + return NULL; + best_font = font_list->fonts + i; + for (; best_score > 0 && i < font_list->nfonts ; i++) + { + score = mfont__score (font_list->fonts + i, spec, request, + limited_size); + if (score >= 0 && score < best_score) + { + best_font = font_list->fonts + i; + best_score = score; + } + } + + MSTRUCT_CALLOC (rfont, MERROR_WIN); + rfont->frame = frame; + rfont->spec = *spec; + rfont->request = *request; + rfont->font = *best_font; + if (best_font->property[MFONT_SIZE] == 0) + rfont->font.property[MFONT_SIZE] = request->property[MFONT_SIZE]; + rfont->score = best_score; + rfont->driver = &xfont_driver; + return rfont; +} + +typedef struct +{ + M17NObject control; + MFrame *frame; + XFontStruct *f; +} MXFontInfo; + +static void +close_xfont (void *object) +{ + MXFontInfo *xfont = (MXFontInfo *) object; + + if (xfont->f) + XFreeFont (FRAME_DISPLAY (xfont->frame), xfont->f); + free (object); +} + + +/* The X font driver function OPEN. */ + +static int +xfont_open (MRealizedFont *rfont) +{ + char name[1024]; + MXFontInfo *xfont; + MFrame *frame = rfont->frame; + int mdebug_mask = MDEBUG_FONT; + + /* This never fail to generate a valid fontname because open_spec + should correspond to a font available on the system. */ + build_font_name (&rfont->font, name, 1024); + M17N_OBJECT (xfont, close_xfont, MERROR_WIN); + rfont->info = xfont; + xfont->frame = frame; + xfont->f = XLoadQueryFont (FRAME_DISPLAY (frame), name); + if (! xfont->f) + { + rfont->status = -1; + MDEBUG_PRINT1 (" [XFONT] x %s\n", name); + return -1; + } + MDEBUG_PRINT1 (" [XFONT] o %s\n", name); + rfont->status = 1; + rfont->ascent = xfont->f->ascent; + rfont->descent = xfont->f->descent; + return 0; +} + + +/* The X font driver function CLOSE. */ + +static void +xfont_close (MRealizedFont *rfont) +{ + M17N_OBJECT_UNREF (rfont->info); +} + +/* The X font driver function FIND_METRIC. */ + +static void +xfont_find_metric (MRealizedFont *rfont, MGlyph *g) +{ + XCharStruct *pcm = NULL; + MXFontInfo *xfont = (MXFontInfo *) rfont->info; + XFontStruct *f = xfont->f; + int byte1, byte2; + + if (g->code == MCHAR_INVALID_CODE) + { + g->lbearing = f->max_bounds.lbearing; + g->rbearing = f->max_bounds.rbearing; + g->width = f->max_bounds.width; + g->ascent = f->ascent; + g->descent = f->descent; + return; + } + + byte1 = g->code >> 8; + byte2 = g->code & 0xFF; + + if (f->per_char != NULL) + { + if (f->min_byte1 == 0 && f->max_byte1 == 0) + { + if (byte1 == 0 + && byte2 >= f->min_char_or_byte2 + && byte2 <= f->max_char_or_byte2) + pcm = f->per_char + byte2 - f->min_char_or_byte2; + } + else + { + if (byte1 >= f->min_byte1 + && byte1 <= f->max_byte1 + && byte2 >= f->min_char_or_byte2 + && byte2 <= f->max_char_or_byte2) + { + pcm = (f->per_char + + ((f->max_char_or_byte2-f->min_char_or_byte2 + 1) + * (byte1 - f->min_byte1)) + + (byte2 - f->min_char_or_byte2)); + } + } + } + + if (pcm) + { + g->lbearing = pcm->lbearing; + g->rbearing = pcm->rbearing; + g->width = pcm->width; + g->ascent = pcm->ascent; + g->descent = pcm->descent; + } + else + { + /* If the per_char pointer is null, all glyphs between the first + and last character indexes inclusive have the same + information, as given by both min_bounds and max_bounds. */ + g->lbearing = 0; + g->rbearing = f->max_bounds.width; + g->width = f->max_bounds.width; + g->ascent = f->ascent; + g->descent = f->descent; + } +} + + +/* The X font driver function ENCODE_CHAR. */ + +static unsigned +xfont_encode_char (MRealizedFont *rfont, int c, unsigned code) +{ + MXFontInfo *xfont; + XFontStruct *f; + unsigned min_byte1, max_byte1, min_byte2, max_byte2; + int all_chars_exist; + + if (rfont->status < 0) + return -1; + if (rfont->status == 0) + { + if (xfont_open (rfont) < 0) + return -1; + } + xfont = (MXFontInfo *) rfont->info; + f = xfont->f; + all_chars_exist = (! f->per_char || f->all_chars_exist == True); + min_byte1 = f->min_byte1; + max_byte1 = f->max_byte1; + min_byte2 = f->min_char_or_byte2; + max_byte2 = f->max_char_or_byte2; + + if (min_byte1 == 0 && max_byte1 == 0) + { + XCharStruct *pcm; + + if (all_chars_exist) + return ((code >= min_byte2 && code <= max_byte2) + ? code : MCHAR_INVALID_CODE); + pcm = f->per_char + (code - min_byte2); + return ((pcm->width > 0 || pcm->rbearing != pcm->lbearing) + ? code : MCHAR_INVALID_CODE); + } + else + { + unsigned byte1 = code >> 8, byte2 = code & 0xFF; + XCharStruct *pcm; + + if (all_chars_exist) + return ((byte1 >= min_byte1 && byte1 <= max_byte1 + && byte2 >= min_byte2 && byte2 <= max_byte2) + ? code : MCHAR_INVALID_CODE); + pcm = f->per_char + ((byte1 - min_byte1) * (max_byte2 - min_byte2 + 1) + + (byte2 - min_byte2)); + return ((pcm->width > 0 || pcm->rbearing != pcm->lbearing) + ? code : MCHAR_INVALID_CODE); + } +} + +static GC +set_region (Display *display, MRealizedFace *rface, GC gc, MDrawRegion region) +{ + GC gc1; + XRectangle xrect; + + XClipBox (region, &xrect); + gc1 = ((GC *) rface->info)[MFACE_GC_SCRATCH]; + XCopyGC (display, gc, GCFont | GCForeground | GCBackground, gc1); + XSetRegion (display, gc1, region); + return gc1; +} + +/* The X font driver function RENDER. */ + +static void +xfont_render (MDrawWindow win, int x, int y, MGlyphString *gstring, + MGlyph *from, MGlyph *to, int reverse, MDrawRegion region) +{ + MRealizedFace *rface = from->rface; + Display *display; + XChar2b *code; + GC *gcs = rface->info; + GC gc = gcs[reverse ? MFACE_GC_INVERSE : MFACE_GC_NORMAL]; + MGlyph *g; + int i; + + if (from == to) + return; + + /* It is assured that the all glyphs in the current range use the + same realized face. */ + display = FRAME_DISPLAY (rface->frame); + + if (region) + gc = set_region (display, rface, gc, region); + + if (from->code == MCHAR_INVALID_CODE) + { + int x0 = x; + + for (; from < to; from++) + { + XDrawRectangle (display, (Window) win, gc, + x0, y - gstring->ascent + 1, from->width - 1, + gstring->ascent + gstring->descent - 2); + x0 += from->width; + } + return; + } + + code = (XChar2b *) alloca (sizeof (XChar2b) * (to - from)); + for (i = 0, g = from; g < to; i++, g++) + { + code[i].byte1 = g->code >> 8; + code[i].byte2 = g->code & 0xFF; + } + + g = from; + while (g < to) + { + if (g->type == GLYPH_PAD) + x += g++->width; + else if (g->type == GLYPH_SPACE) + for (; g < to && g->type == GLYPH_SPACE; g++) + x += g->width; + else if (! g->rface->rfont) + { + if ((g->c >= 0x200B && g->c <= 0x200F) + || (g->c >= 0x202A && g->c <= 0x202E)) + x += g++->width; + else + { + /* As a font is not found for this character, draw an + empty box. */ + int box_width = g->width; + int box_height = gstring->ascent + gstring->descent; + + if (box_width > 4) + box_width -= 2; + if (box_height > 4) + box_height -= 2; + XDrawRectangle (display, (Window) win, gc, + x, y - gstring->ascent, box_width, box_height); + x += g++->width; + } + } + else if (g->xoff != 0 || g->yoff != 0 || g->right_padding) + { + XDrawString16 (display, (Window) win, gc, + x + g->xoff, y + g->yoff, code + (g - from), 1); + x += g->width; + g++; + } + else + { + int orig_x = x; + int code_idx = g - from; + + for (i = 0; + g < to && g->type == GLYPH_CHAR && g->xoff == 0 && g->yoff == 0; + i++, g++) + x += g->width; + XDrawString16 (display, (Window) win, gc, orig_x, y, + code + code_idx, i); + } + } +} + + + +/* XIM (X Input Method) handler */ + +typedef struct MInputXIMMethodInfo +{ + Display *display; + XIM xim; + MSymbol language; + MSymbol coding; +} MInputXIMMethodInfo; + +typedef struct MInputXIMContextInfo +{ + XIC xic; + Window win; + MConverter *converter; +} MInputXIMContextInfo; + +static int +xim_open_im (MInputMethod *im) +{ + MInputXIMArgIM *arg = (MInputXIMArgIM *) im->arg; + MLocale *saved, *this; + char *save_modifier_list; + XIM xim; + MInputXIMMethodInfo *im_info; + + saved = mlocale_set (LC_CTYPE, NULL); + this = mlocale_set (LC_CTYPE, arg->locale ? arg->locale : ""); + if (! this) + /* The specified locale is not supported. */ + MERROR (MERROR_LOCALE, -1); + if (mlocale_get_prop (this, Mcoding) == Mnil) + { + /* Unable to decode the output of XIM. */ + mlocale_set (LC_CTYPE, msymbol_name (mlocale_get_prop (saved, Mname))); + MERROR (MERROR_LOCALE, -1); + } + + if (arg->modifier_list) + save_modifier_list = XSetLocaleModifiers (arg->modifier_list); + else + save_modifier_list = XSetLocaleModifiers (""); + if (! save_modifier_list) + { + /* The specified locale is not supported by X. */ + mlocale_set (LC_CTYPE, msymbol_name (mlocale_get_prop (saved, Mname))); + MERROR (MERROR_LOCALE, -1); + } + + xim = XOpenIM (arg->display, arg->db, arg->res_name, arg->res_class); + if (! xim) + { + /* No input method is available in the current locale. */ + XSetLocaleModifiers (save_modifier_list); + mlocale_set (LC_CTYPE, msymbol_name (mlocale_get_prop (saved, Mname))); + MERROR (MERROR_WIN, -1); + } + + MSTRUCT_MALLOC (im_info, MERROR_WIN); + im_info->display = arg->display; + im_info->xim = xim; + im_info->language = mlocale_get_prop (this, Mlanguage); + im_info->coding = mlocale_get_prop (this, Mcoding); + im->info = im_info; + + XSetLocaleModifiers (save_modifier_list); + mlocale_set (LC_CTYPE, msymbol_name (mlocale_get_prop (saved, Mname))); + + return 0; +} + +static void +xim_close_im (MInputMethod *im) +{ + MInputXIMMethodInfo *im_info = (MInputXIMMethodInfo *) im->info; + + XCloseIM (im_info->xim); + free (im_info); +} + +int +xim_create_ic (MInputContext *ic) +{ + MInputXIMArgIC *arg = (MInputXIMArgIC *) ic->arg; + MInputXIMMethodInfo *im_info = (MInputXIMMethodInfo *) ic->im->info; + MInputXIMContextInfo *ic_info; + XIC xic; + + if (! arg->input_style) + { + /* By default, use Root style. */ + arg->input_style = XIMPreeditNothing | XIMStatusNothing; + arg->preedit_attrs = NULL; + arg->status_attrs = NULL; + } + + if (! arg->preedit_attrs && ! arg->status_attrs) + xic = XCreateIC (im_info->xim, + XNInputStyle, arg->input_style, + XNClientWindow, arg->client_win, + XNFocusWindow, arg->focus_win, + NULL); + else if (arg->preedit_attrs && ! arg->status_attrs) + xic = XCreateIC (im_info->xim, + XNInputStyle, arg->input_style, + XNClientWindow, arg->client_win, + XNFocusWindow, arg->focus_win, + XNPreeditAttributes, arg->preedit_attrs, + NULL); + else if (! arg->preedit_attrs && arg->status_attrs) + xic = XCreateIC (im_info->xim, + XNInputStyle, arg->input_style, + XNClientWindow, arg->client_win, + XNFocusWindow, arg->focus_win, + XNStatusAttributes, arg->status_attrs, + NULL); + else + xic = XCreateIC (im_info->xim, + XNInputStyle, arg->input_style, + XNClientWindow, arg->client_win, + XNFocusWindow, arg->focus_win, + XNPreeditAttributes, arg->preedit_attrs, + XNStatusAttributes, arg->status_attrs, + NULL); + if (! xic) + MERROR (MERROR_WIN, -1); + + MSTRUCT_MALLOC (ic_info, MERROR_WIN); + ic_info->xic = xic; + ic_info->win = arg->focus_win; + ic_info->converter = mconv_buffer_converter (im_info->coding, NULL, 0); + ic->info = ic_info; + return 0; +} + +void +xim_destroy_ic (MInputContext *ic) +{ + MInputXIMContextInfo *ic_info = (MInputXIMContextInfo *) ic->info; + + XDestroyIC (ic_info->xic); + mconv_free_converter (ic_info->converter); + free (ic_info); + ic->info = NULL; +} + +static int +xim_filter (MInputContext *ic, MSymbol key, void *event) +{ + MInputXIMContextInfo *ic_info = (MInputXIMContextInfo *) ic->info; + + return (XFilterEvent ((XEvent *) event, ic_info->win) == True); +} + + +static int +xim_lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt) +{ + MInputXIMMethodInfo *im_info = (MInputXIMMethodInfo *) ic->im->info; + MInputXIMContextInfo *ic_info = (MInputXIMContextInfo *) ic->info; + XKeyPressedEvent *ev = (XKeyPressedEvent *) arg; + KeySym keysym; + Status status; + char *buf; + int len; + + buf = (char *) alloca (512); + len = XmbLookupString (ic_info->xic, ev, buf, 512, &keysym, &status); + if (status == XBufferOverflow) + { + buf = (char *) alloca (len); + len = XmbLookupString (ic_info->xic, ev, buf, len, &keysym, &status); + } + + mtext_reset (ic->produced); + if (len == 0) + return 1; + + mconv_reset_converter (ic_info->converter); + mconv_rebind_buffer (ic_info->converter, (unsigned char *) buf, len); + mconv_decode (ic_info->converter, ic->produced); + mtext_put_prop (ic->produced, 0, mtext_nchars (ic->produced), + Mlanguage, (void *) im_info->language); + mtext_cpy (mt, ic->produced); + mtext_reset (ic->produced); + return 0; +} + + + +#if 1 +int +x_error_handler (Display *display, XErrorEvent *error) +{ + mdebug_hook (); + return 0; +} + +int +x_io_error_handler (Display *display) +{ + mdebug_hook (); + return 0; +} +#endif + + + +int +mwin__init () +{ + Mdisplay = msymbol ("display"); + Mscreen = msymbol ("screen"); + Mdrawable = msymbol ("drawable"); + Mdepth = msymbol ("depth"); + Mwidget = msymbol ("widget"); + M_iso8859_1 = msymbol ("iso8859-1"); + M_iso10646_1 = msymbol ("iso10646-1"); + + display_info_list = mplist (); + device_list = mplist (); + + mfont__driver_list[MFONT_TYPE_WIN] = &xfont_driver; + + Mxim = msymbol ("xim"); + msymbol_put (Mxim, Minput_driver, &minput_xim_driver); + + return 0; +} + +void +mwin__fini () +{ + M17N_OBJECT_UNREF (display_info_list); + M17N_OBJECT_UNREF (device_list); +} + +typedef struct +{ + String font; + String foreground; + String background; + Boolean reverse_video; +} AppData, *AppDataPtr; + + +int +mwin__parse_font_name (char *name, MFont *font) +{ + char *field[XLFD_FIELD_MAX]; + unsigned short size, resy; + MSymbol attrs[MFONT_PROPERTY_MAX]; + char *copy = (char *) alloca (512); + int i, len; + char *p, *last = NULL; + + len = strlen (name) + 1; + for (i = 0, p = name; *p; p++) + { + if (*p == '-') + i++; + else if (p > name && *p == '*' && p[-1] == '-') + last = p + 1; + } + if (i == 14) + memcpy (copy, name, len); + else if (last) + { + memcpy (copy, name, last - name); + for (; i < 14; i++) + strcat (copy, "-*"); + strcat (copy, last); + } + + if (split_font_name (copy, field, &size, &resy) < 0) + return -1; + attrs[MFONT_FOUNDRY] + = *(field[XLFD_FOUNDRY]) != '*' ? msymbol (field[XLFD_FOUNDRY]) : Mnil; + attrs[MFONT_FAMILY] + = *(field[XLFD_FAMILY]) != '*' ? msymbol (field[XLFD_FAMILY]) : Mnil; + attrs[MFONT_WEIGHT] + = *(field[XLFD_WEIGHT]) != '*' ? msymbol (field[XLFD_WEIGHT]) : Mnil; + attrs[MFONT_STYLE] + = *(field[XLFD_SLANT]) != '*' ? msymbol (field[XLFD_SLANT]) : Mnil; + attrs[MFONT_STRETCH] + = *(field[XLFD_SWIDTH]) != '*' ? msymbol (field[XLFD_SWIDTH]) : Mnil; + attrs[MFONT_ADSTYLE] + = *(field[XLFD_ADSTYLE]) != '*' ? msymbol (field[XLFD_ADSTYLE]) : Mnil; + attrs[MFONT_REGISTRY] + = *(field[XLFD_REGISTRY]) != '*' ? msymbol (field[XLFD_REGISTRY]) : Mnil; + mfont__set_spec (font, attrs, size, resy); + return 0; +} + + +char * +mwin__build_font_name (MFont *font) +{ + char name[1024]; + + if (build_font_name (font, name, 1024) < 0) + return NULL; + return strdup (name); +} + +/** Return an MWDevice object corresponding to a display specified in + PLIST. + + It searches device_list for a device matching the display. If + found, return the found object. Otherwise, return a newly created + object. */ + +MWDevice * +mwin__open_device (MFrame *frame, MPlist *param) +{ + Display *display = NULL; + Screen *screen = NULL; + int screen_num; + Drawable drawable = 0; + Widget widget = NULL; + Colormap cmap = 0; + int auto_display = 0; + MDisplayInfo *disp_info = NULL; + MWDevice *device = NULL; + MSymbol key; + XWindowAttributes attr; + unsigned depth = 0; + MPlist *plist; + + if (param) + for (plist = param; (key = mplist_key (plist)) != Mnil; + plist = mplist_next (plist)) + { + if (key == Mdisplay) + display = (Display *) mplist_value (plist); + else if (key == Mscreen) + screen = mplist_value (plist); + else if (key == Mdrawable) + drawable = (Drawable) mplist_value (plist); + else if (key == Mdepth) + depth = (unsigned) mplist_value (plist); + else if (key == Mwidget) + widget = (Widget) mplist_value (plist); + else if (key == Mcolormap) + cmap = (Colormap) mplist_value (plist); + } + + if (widget) + { + display = XtDisplay (widget); + screen_num = XScreenNumberOfScreen (XtScreen (widget)); + depth = DefaultDepth (display, screen_num); + } + else if (drawable) + { + Window root_window; + int x, y; + unsigned width, height, border_width; + + if (! display) + MERROR (MERROR_WIN, NULL); + XGetGeometry (display, drawable, &root_window, + &x, &y, &width, &height, &border_width, &depth); + XGetWindowAttributes (display, root_window, &attr); + screen_num = XScreenNumberOfScreen (attr.screen); + } + else + { + if (screen) + display = DisplayOfScreen (screen); + else + { + if (! display) + { + display = XOpenDisplay (NULL); + if (! display) + MERROR (MERROR_WIN, NULL); + auto_display = 1; + } + screen = DefaultScreenOfDisplay (display); + } + screen_num = XScreenNumberOfScreen (screen); + if (! depth) + depth = DefaultDepth (display, screen_num); + } + + if (! cmap) + cmap = DefaultColormap (display, screen_num); + + for (plist = display_info_list; mplist_key (plist) != Mnil; + plist = mplist_next (plist)) + { + disp_info = (MDisplayInfo *) mplist_value (plist); + if (disp_info->display == display) + break; + } + + if (mplist_key (plist) != Mnil) + M17N_OBJECT_REF (disp_info); + else + { + M17N_OBJECT (disp_info, free_display_info, MERROR_WIN); + disp_info->display = display; + disp_info->auto_display = auto_display; + disp_info->font_registry_list = mplist (); + disp_info->iso8859_1_family_list = mplist (); + disp_info->iso10646_1_family_list = mplist (); + disp_info->realized_font_list = mplist (); + find_modifier_bits (disp_info); + mplist_add (display_info_list, Mt, disp_info); + } + + for (plist = device_list; mplist_key (plist) != Mnil; + plist = mplist_next (plist)) + { + device = (MWDevice *) mplist_value (plist); + if (device->display_info == disp_info + && device->depth == depth + && device->cmap == cmap) + break; + } + + if (mplist_key (plist) != Mnil) + M17N_OBJECT_REF (device); + else + { + M17N_OBJECT (device, free_device, MERROR_WIN); + device->display_info = disp_info; + device->screen_num = screen_num; + /* A drawable on which to create GCs. */ + device->drawable = XCreatePixmap (display, + RootWindow (display, screen_num), + 1, 1, depth); + device->depth = depth; + device->cmap = cmap; + device->realized_face_list = mplist (); + device->realized_fontset_list = mplist (); + device->foreground = BlackPixel (display, screen_num); + device->background = WhitePixel (display, screen_num); + } + + frame->realized_font_list = disp_info->realized_font_list; + frame->realized_face_list = device->realized_face_list; + frame->realized_fontset_list = device->realized_fontset_list; + + if (widget) + { + AppData app_data; + XtResource resources[] = { + { XtNfont, XtCFont, XtRString, sizeof (String), + XtOffset (AppDataPtr, font), XtRString, + "-misc-fixed-medium-r-normal--*-120-*-*-*-*-iso8859-1" }, + { XtNforeground, XtCForeground, XtRString, sizeof (String), + XtOffset (AppDataPtr, foreground), XtRString, "black" }, + { XtNbackground, XtCBackground, XtRString, sizeof (String), + XtOffset (AppDataPtr, background), XtRString, "white" }, + { XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean), + XtOffset (AppDataPtr, reverse_video), XtRImmediate, (caddr_t) FALSE } + }; + MFace *face = NULL; + MFont font; + char **names; + int nfonts; + + XtGetApplicationResources (widget, &app_data, + resources, XtNumber (resources), NULL, 0); + names = XListFonts (display, app_data.font, 1, &nfonts); + if (nfonts == 1) + { + if (mwin__parse_font_name (names[0], &font) >= 0) + face = mface_from_font (&font); + else + { + /* The font name does not conform to XLFD. Try to open the + font and get XA_FONT property. */ + XFontStruct *xfont = XLoadQueryFont (display, names[0]); + + if (xfont) + { + unsigned long value; + char *name; + + if (XGetFontProperty (xfont, XA_FONT, &value) + && (name = ((char *) + XGetAtomName (display, (Atom) value)))) + { + if (mwin__parse_font_name (name, &font) >= 0) + face = mface_from_font (&font); + } + XFreeFont (display, xfont); + } + } + XFreeFontNames (names); + } + + if (app_data.reverse_video == True) + { + if (! face) + face = mface (); + mface_put_prop (face, Mvideomode, Mreverse); + } + if (face) + { + mplist_push (param, Mface, face); + M17N_OBJECT_UNREF (face); + } + device->foreground + = get_color (display, cmap, msymbol (app_data.foreground), Mnil, + device->foreground); + device->background + = get_color (display, cmap, msymbol (app_data.background), Mnil, + device->background); + } + XSetErrorHandler (x_error_handler); + /* XSetIOErrorHandler (x_io_error_handler); */ + + return device; +} + +void +mwin__close_device (MFrame *frame) +{ + M17N_OBJECT_UNREF (frame->device); +} + +void * +mwin__device_get_prop (MWDevice *device, MSymbol key) +{ + if (key == Mdisplay) + return (void *) device->display_info->display; + if (key == Mscreen) + return (void *) ScreenOfDisplay(device->display_info->display, + device->screen_num); + if (key == Mcolormap) + return (void *) device->cmap; + if (key == Mdepth) + return (void *) device->depth; + return NULL; +} + +struct { + int size, inc, used; + GC *gc_table; +} gc_list; + +#define REGISTER_GC(gc) \ + do { \ + if (! gc_list.size) \ + MLIST_INIT1 (&gc_list, gc_table, 100); \ + MLIST_APPEND1 (&gc_list, gc_table, gc, MERROR_WIN); \ + } while (0) + + +#define UNREGISTER_GC(gc) \ + do { \ + int j; \ + for (j = 0; j < gc_list.used; j++) \ + if (gc_list.gc_table[j] == gc) \ + gc_list.gc_table[j] = (GC) NULL; \ + } while (0) + + +void +mwin__realize_face (MRealizedFace *rface) +{ + MFrame *frame = rface->frame; + MWDevice *device = frame->device; + Display *display = FRAME_DISPLAY (frame); + XGCValues values; + int mask = GCForeground | GCBackground; + MSymbol foreground = rface->face.property[MFACE_FOREGROUND]; + MSymbol background = rface->face.property[MFACE_BACKGROUND]; + MFaceHLineProp *hline = rface->hline; + MFaceBoxProp *box = rface->box; + MFaceHookFunc func = (MFaceHookFunc) rface->face.property[MFACE_HOOK_FUNC]; + MSymbol default_foreground + = (MSymbol) mface_get_prop (frame->face, Mforeground); + MSymbol default_background + = (MSymbol) mface_get_prop (frame->face, Mbackground); + GC *gcs; + unsigned long pixel; + + MTABLE_CALLOC (gcs, MFACE_GCS, MERROR_WIN); + + values.foreground = get_color (display, device->cmap, foreground, + default_foreground, device->foreground); + values.background = get_color (display, device->cmap, background, + default_background, device->background); + if (rface->face.property[MFACE_VIDEOMODE] == Mreverse) + pixel = values.foreground, + values.foreground = values.background, + values.background = pixel; + + if (rface->rfont + && rface->rfont->font.property[MFONT_TYPE] - 1 == MFONT_TYPE_WIN) + { + values.font = ((MXFontInfo *) (rface->rfont->info))->f->fid; + mask |= GCFont; + } + + gcs[MFACE_GC_NORMAL] = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_NORMAL]); + + gcs[MFACE_GC_SCRATCH] = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_SCRATCH]); + + pixel = values.foreground; + values.foreground = values.background; + values.background = pixel; + gcs[MFACE_GC_INVERSE] = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_INVERSE]); + values.background = values.foreground; + values.foreground = pixel; + + mask &= ~GCFont; + + if (rface == rface->ascii_rface) + { + /* This realized face is for ASCII. Setup GCs for hline and + box. */ + if (hline && hline->color != foreground) + { + values.foreground + = get_color (display, device->cmap, hline->color, + default_foreground, device->foreground); + gcs[MFACE_GC_HLINE] + = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_HLINE]); + values.foreground = pixel; + } + + if (box) + { + if (box->color_top) + { + values.foreground + = get_color (display, device->cmap, box->color_top, + default_foreground, device->foreground); + gcs[MFACE_GC_BOX_TOP] + = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_BOX_TOP]); + } + + if (box->color_left + && box->color_left != box->color_top) + { + values.foreground + = get_color (display, device->cmap, box->color_left, + default_foreground, device->foreground); + gcs[MFACE_GC_BOX_LEFT] + = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_BOX_LEFT]); + } + + if (box->color_right + && box->color_right != box->color_top) + { + values.foreground + = get_color (display, device->cmap, box->color_right, + default_foreground, device->foreground); + gcs[MFACE_GC_BOX_RIGHT] + = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_BOX_RIGHT]); + } + + if (box->color_bottom + && box->color_bottom != box->color_top) + { + values.foreground + = get_color (display, device->cmap, box->color_bottom, + default_foreground, device->foreground); + gcs[MFACE_GC_BOX_BOTTOM] + = XCreateGC (display, device->drawable, mask, &values); + REGISTER_GC (gcs[MFACE_GC_BOX_BOTTOM]); + } + } + } + else + { + /* This realized face is not for ASCII. GCs for hline and box + are shared with that of the corresponding ASCII face. */ + GC *ascii_gcs = rface->ascii_rface->info; + int i; + + for (i = MFACE_GC_HLINE; i < MFACE_GCS; i++) + gcs[i] = ascii_gcs[i]; + } + + rface->info = gcs; + if (func) + (func) (&(rface->face), rface->info, rface->face.property[MFACE_HOOK_ARG]); +} + + +void +mwin__free_realized_face (MRealizedFace *rface) +{ + GC *gcs = rface->info; + enum face_gc limit + = rface == rface->ascii_rface ? MFACE_GCS : MFACE_GC_HLINE; + int i; + + for (i = 0; i < limit; i++) + if (gcs[i]) + { + UNREGISTER_GC (gcs[i]); + XFreeGC (FRAME_DISPLAY (rface->frame), gcs[i]); + } + free (gcs); +} + + +void +mwin__fill_space (MFrame *frame, MDrawWindow win, MRealizedFace *rface, + int reverse, + int x, int y, int width, int height, MDrawRegion region) +{ + GC *gcs = rface->info; + GC gc = gcs[reverse ? MFACE_GC_NORMAL : MFACE_GC_INVERSE]; + + if (region) + gc = set_region (FRAME_DISPLAY (frame), rface, gc, region); + + XFillRectangle (FRAME_DISPLAY (frame), (Window) win, gc, + x, y, width, height); +} + + +void +mwin__draw_hline (MFrame *frame, MDrawWindow win, MGlyphString *gstring, + MRealizedFace *rface, int reverse, + int x, int y, int width, MDrawRegion region) +{ + enum MFaceHLineType type = rface->hline->type; + GC *gcs = rface->info; + GC gc; + int i; + + y = (type == MFACE_HLINE_BOTTOM + ? y + gstring->text_descent - rface->hline->width + : type == MFACE_HLINE_UNDER + ? y + 1 + : type == MFACE_HLINE_STRIKE_THROUGH + ? y - ((gstring->ascent + gstring->descent) / 2) + : y - gstring->text_ascent); + if (reverse) + gc = gcs[MFACE_GC_INVERSE]; + else if (gcs[MFACE_GC_HLINE]) + gc = gcs[MFACE_GC_HLINE]; + else + gc = gcs[MFACE_GC_NORMAL]; + + if (region) + gc = set_region (FRAME_DISPLAY (frame), rface, gc, region); + + for (i = 0; i < rface->hline->width; i++) + XDrawLine (FRAME_DISPLAY (frame), (Window) win, gc, + x, y + i, x + width - 1, y + i); +} + + +void +mwin__draw_box (MFrame *frame, MDrawWindow win, MGlyphString *gstring, + MGlyph *g, int x, int y, int width, MDrawRegion region) +{ + Display *display = FRAME_DISPLAY (frame); + MRealizedFace *rface = g->rface; + MFaceBoxProp *box = rface->box; + GC *gcs = rface->info; + GC gc_top, gc_left, gc_right, gc_btm; + int y0, y1; + int i; + + y0 = y - (gstring->text_ascent + + rface->box->inner_vmargin + rface->box->width); + y1 = y + (gstring->text_descent + + rface->box->inner_vmargin + rface->box->width - 1); + + gc_top = gcs[MFACE_GC_BOX_TOP]; + if (! gc_top) + gc_top = gcs[MFACE_GC_NORMAL]; + if (region) + gc_top = set_region (FRAME_DISPLAY (frame), rface, gc_top, region); + gc_btm = gcs[MFACE_GC_BOX_BOTTOM]; + if (! gc_btm) + gc_btm = gc_top; + + if (g->type == GLYPH_BOX) + { + int x0, x1; + + if (g->left_padding) + x0 = x + box->outer_hmargin, x1 = x + g->width - 1; + else + x0 = x, x1 = x + g->width - box->outer_hmargin - 1; + + /* Draw the top side. */ + for (i = 0; i < box->width; i++) + XDrawLine (display, (Window) win, gc_top, x0, y0 + i, x1, y0 + i); + + /* Draw the bottom side. */ + if (region) + gc_btm = set_region (display, rface, gc_btm, region); + for (i = 0; i < box->width; i++) + XDrawLine (display, (Window) win, gc_btm, x0, y1 - i, x1, y1 - i); + + if (g->left_padding > 0) + { + /* Draw the left side. */ + gc_left = gcs[MFACE_GC_BOX_LEFT]; + if (! gc_left) + gc_left = gc_top; + else if (region) + gc_left = set_region (display, rface, gc_left, region); + for (i = 0; i < rface->box->width; i++) + XDrawLine (display, (Window) win, gc_left, + x0 + i, y0 + i, x0 + i, y1 - i); + } + else + { + /* Draw the right side. */ + gc_right = gcs[MFACE_GC_BOX_RIGHT]; + if (! gc_right) + gc_right = gc_top; + else if (region) + gc_right = set_region (display, rface, gc_right, region); + for (i = 0; i < rface->box->width; i++) + XDrawLine (display, (Window) win, gc_right, + x1 - i, y0 + i, x1 - i, y1 - i); + } + } + else + { + /* Draw the top side. */ + for (i = 0; i < box->width; i++) + XDrawLine (display, (Window) win, gc_top, + x, y0 + i, x + width - 1, y0 + i); + + /* Draw the bottom side. */ + if (region) + gc_btm = set_region (display, rface, gc_btm, region); + for (i = 0; i < box->width; i++) + XDrawLine (display, (Window) win, gc_btm, + x, y1 - i, x + width - 1, y1 - i); + } +} + + +void +mwin__draw_bitmap (MFrame *frame, MDrawWindow win, MRealizedFace *rface, + int reverse, int x, int y, + int width, int height, int row_bytes, unsigned char *bmp, + MDrawRegion region) +{ + Display *display = FRAME_DISPLAY (frame); + int i, j; + GC *gcs = rface->info; + GC gc = gcs[reverse ? MFACE_GC_INVERSE : MFACE_GC_NORMAL]; + + if (region) + gc = set_region (FRAME_DISPLAY (frame), rface, gc, region); + + for (i = 0; i < height; i++, bmp += row_bytes) + for (j = 0; j < width; j++) + if (bmp[j / 8] & (1 << (7 - (j % 8)))) + XDrawPoint (display, (Window) win, gc, x + j, y + i); +} + + +MDrawRegion +mwin__region_from_rect (MDrawMetric *rect) +{ + MDrawRegion region1 = XCreateRegion (); + MDrawRegion region2 = XCreateRegion (); + XRectangle xrect; + + xrect.x = rect->x; + xrect.y = rect->y; + xrect.width = rect->width; + xrect.height = rect->height; + XUnionRectWithRegion (&xrect, region1, region2); + XDestroyRegion (region1); + return region2; +} + +void +mwin__union_rect_with_region (MDrawRegion region, MDrawMetric *rect) +{ + MDrawRegion region1 = XCreateRegion (); + XRectangle xrect; + + xrect.x = rect->x; + xrect.y = rect->y; + xrect.width = rect->width; + xrect.height = rect->height; + + XUnionRegion (region, region, region1); + XUnionRectWithRegion (&xrect, region1, region); + XDestroyRegion (region1); +} + +void +mwin__intersect_region (MDrawRegion region1, MDrawRegion region2) +{ + MDrawRegion region = XCreateRegion (); + + XUnionRegion (region1, region1, region); + XIntersectRegion (region, region2, region1); + XDestroyRegion (region); +} + +void +mwin__region_add_rect (MDrawRegion region, MDrawMetric *rect) +{ + MDrawRegion region1 = XCreateRegion (); + XRectangle xrect; + + xrect.x = rect->x; + xrect.y = rect->y; + xrect.width = rect->width; + xrect.height = rect->height; + XUnionRectWithRegion (&xrect, region1, region); + XDestroyRegion (region1); +} + +void +mwin__region_to_rect (MDrawRegion region, MDrawMetric *rect) +{ + XRectangle xrect; + + XClipBox (region, &xrect); + rect->x = xrect.x; + rect->y = xrect.y; + rect->width = xrect.width; + rect->height = xrect.height; +} + +void +mwin__free_region (MDrawRegion region) +{ + XDestroyRegion (region); +} + +void +mwin__dump_region (MDrawRegion region) +{ + XRectangle rect; + XClipBox (region, &rect); + fprintf (stderr, "(%d %d %d %d)\n", rect.x, rect.y, rect.width, rect.height); +} + +void +mwin__verify_region (MFrame *frame, MDrawRegion region) +{ + set_region (FRAME_DISPLAY (frame), frame->rface, + ((GC *) frame->rface->info)[MFACE_GC_NORMAL], region); +} + +MDrawWindow +mwin__create_window (MFrame *frame, MDrawWindow parent) +{ + MWDevice *device = frame->device; + Display *display = FRAME_DISPLAY (frame); + int screen = FRAME_SCREEN (frame); + Window win; + XWMHints wm_hints = { InputHint, False }; + XClassHint class_hints = { "M17N-IM", "m17n-im" }; + XSetWindowAttributes attrs; + unsigned long mask; + MSymbol background = mface_get_prop (frame->face, Mbackground); + + attrs.background_pixel = get_color (display, device->cmap, + background, background, + WhitePixel (display, screen)); + attrs.backing_store = Always; + attrs.override_redirect = True; + attrs.save_under = True; + mask = CWBackPixel | CWBackingStore | CWOverrideRedirect | CWSaveUnder; + if (! parent) + parent = (MDrawWindow) RootWindow (display, screen); + win = XCreateWindow (display, (Window) parent, 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, CopyFromParent, + mask, &attrs); + XSetWMProperties (display, (Window) win, NULL, NULL, NULL, 0, + NULL, &wm_hints, &class_hints); + XSelectInput (display, (Window) win, StructureNotifyMask | ExposureMask); + return (MDrawWindow) win; +} + +void +mwin__destroy_window (MFrame *frame, MDrawWindow win) +{ + XDestroyWindow (FRAME_DISPLAY (frame), (Window) win); +} + +#if 0 +MDrawWindow +mwin__event_window (void *event) +{ + return ((MDrawWindow) ((XEvent *) event)->xany.window); +} + +void +mwin__print_event (void *arg, char *win_name) +{ + char *event_name; + XEvent *event = (XEvent *) arg; + + switch (event->xany.type) + { + case 2: event_name = "KeyPress"; break; + case 3: event_name = "KeyRelease"; break; + case 4: event_name = "ButtonPress"; break; + case 5: event_name = "ButtonRelease"; break; + case 6: event_name = "MotionNotify"; break; + case 7: event_name = "EnterNotify"; break; + case 8: event_name = "LeaveNotify"; break; + case 9: event_name = "FocusIn"; break; + case 10: event_name = "FocusOut"; break; + case 11: event_name = "KeymapNotify"; break; + case 12: event_name = "Expose"; break; + case 13: event_name = "GraphicsExpose"; break; + case 14: event_name = "NoExpose"; break; + case 15: event_name = "VisibilityNotify"; break; + case 16: event_name = "CreateNotify"; break; + case 17: event_name = "DestroyNotify"; break; + case 18: event_name = "UnmapNotify"; break; + case 19: event_name = "MapNotify"; break; + case 20: event_name = "MapRequest"; break; + case 21: event_name = "ReparentNotify"; break; + case 22: event_name = "ConfigureNotify"; break; + case 23: event_name = "ConfigureRequest"; break; + case 24: event_name = "GravityNotify"; break; + case 25: event_name = "ResizeRequest"; break; + case 26: event_name = "CirculateNotify"; break; + case 27: event_name = "CirculateRequest"; break; + case 28: event_name = "PropertyNotify"; break; + case 29: event_name = "SelectionClear"; break; + case 30: event_name = "SelectionRequest"; break; + case 31: event_name = "SelectionNotify"; break; + case 32: event_name = "ColormapNotify"; break; + case 33: event_name = "ClientMessage"; break; + case 34: event_name = "MappingNotify"; break; + default: event_name = "unknown"; + } + + fprintf (stderr, "%s: %s\n", win_name, event_name); +} +#endif + +void +mwin__map_window (MFrame *frame, MDrawWindow win) +{ + XMapRaised (FRAME_DISPLAY (frame), (Window) win); +} + +void +mwin__unmap_window (MFrame *frame, MDrawWindow win) +{ + XUnmapWindow (FRAME_DISPLAY (frame), (Window) win); +} + +void +mwin__window_geometry (MFrame *frame, MDrawWindow win, MDrawWindow parent_win, + MDrawMetric *geometry) +{ + Display *display = FRAME_DISPLAY (frame); + XWindowAttributes attr; + Window parent = (Window) parent_win, root; + + XGetWindowAttributes (display, (Window) win, &attr); + geometry->x = attr.x + attr.border_width; + geometry->y = attr.y + attr.border_width; + geometry->width = attr.width; + geometry->height = attr.height; + + if (! parent) + parent = RootWindow (display, FRAME_SCREEN (frame)); + while (1) + { + Window this_parent, *children; + unsigned n; + + XQueryTree (display, (Window) win, &root, &this_parent, &children, &n); + if (children) + XFree (children); + if (this_parent == parent || this_parent == root) + break; + win = (MDrawWindow) this_parent; + XGetWindowAttributes (display, (Window) win, &attr); + geometry->x += attr.x + attr.border_width; + geometry->y += attr.y + attr.border_width; + } +} + +void +mwin__adjust_window (MFrame *frame, MDrawWindow win, + MDrawMetric *current, MDrawMetric *new) +{ + Display *display = FRAME_DISPLAY (frame); + unsigned int mask = 0; + XWindowChanges values; + + if (current->width != new->width) + { + mask |= CWWidth; + if (new->width <= 0) + new->width = 1; + values.width = current->width = new->width; + } + if (current->height != new->height) + { + mask |= CWHeight; + if (new->height <= 0) + new->height = 1; + values.height = current->height = new->height; + } + if (current->x != new->x) + { + mask |= CWX; + values.x = current->x = new->x; + } + if (current->y != new->y) + { + mask |= CWY; + current->y = new->y; + values.y = current->y = new->y; + } + if (mask) + XConfigureWindow (display, (Window) win, mask, &values); +} + +MSymbol +mwin__parse_event (MFrame *frame, void *arg, int *modifiers) +{ + XEvent *event = (XEvent *) arg; + MDisplayInfo *disp_info = frame->device->display_info; + int len; + char buf[512]; + KeySym keysym; + MSymbol key; + + *modifiers = 0; + if (event->xany.type != KeyPress + /* && event->xany.type != KeyRelease */ + ) + return Mnil; + len = XLookupString ((XKeyEvent *) event, (char *) buf, 512, &keysym, NULL); + if (len > 1) + return Mnil; + if (len == 1) + { + int c = buf[0]; + + if ((c == ' ' || c == 127) && ((XKeyEvent *) event)->state & ShiftMask) + *modifiers |= MINPUT_KEY_SHIFT_MODIFIER; + if (((XKeyEvent *) event)->state & ControlMask) + { + c &= 0x1F; + if (((XKeyEvent *) event)->state & ShiftMask) + *modifiers |= MINPUT_KEY_SHIFT_MODIFIER; + } + if (((XKeyEvent *) event)->state & disp_info->meta_mask) + c |= 0x80; + key = minput__char_to_key (c); + } + else if (keysym >= XK_Shift_L && keysym <= XK_Hyper_R) + return Mnil; + else + { + char *str = XKeysymToString (keysym); + + if (! str) + return Mnil; + key = msymbol (str); + if (((XKeyEvent *) event)->state & ShiftMask) + *modifiers |= MINPUT_KEY_SHIFT_MODIFIER; + if (((XKeyEvent *) event)->state & ControlMask) + *modifiers |= MINPUT_KEY_CONTROL_MODIFIER; + } + if (((XKeyEvent *) event)->state & disp_info->meta_mask) + *modifiers |= MINPUT_KEY_META_MODIFIER; + if (((XKeyEvent *) event)->state & disp_info->alt_mask) + *modifiers |= MINPUT_KEY_ALT_MODIFIER; + if (((XKeyEvent *) event)->state & disp_info->super_mask) + *modifiers |= MINPUT_KEY_SUPER_MODIFIER; + if (((XKeyEvent *) event)->state & disp_info->hyper_mask) + *modifiers |= MINPUT_KEY_HYPER_MODIFIER; + + return key; +} + + +MText * +mwin__get_selection_text (MFrame *frame) +{ + return NULL; +} + + +void +mwin__dump_gc (MFrame *frame, MRealizedFace *rface) +{ + unsigned long valuemask = GCForeground | GCBackground | GCClipMask; + XGCValues values; + Display *display = FRAME_DISPLAY (frame); + GC *gcs = rface->info; + int i; + + for (i = 0; i <= MFACE_GC_SCRATCH; i++) + { + XGetGCValues (display, gcs[i], valuemask, &values); + fprintf (stderr, "GC%d: fore/#%lX back/#%lX", i, + values.foreground, values.background); + fprintf (stderr, "\n"); + } +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + +/* External API */ + +/*=*/ +/*** @addtogroup m17nFrame */ +/*** @{ */ +/*=*/ + +/***en + @name Variables: Keys of frame parameter (X specific). + + These are the symbols to use as parameter keys for the function + mframe () (which see). They are also keys of a frame property + (except for #Mwidget). */ +/*=*/ +/*** @{ */ +/* Keywords for mwin__open_device (). */ +MSymbol Mdisplay, Mscreen, Mdrawable, Mdepth, Mwidget, Mcolormap; + +/*** @} */ +/*** @} */ + +/*=*/ +/*** @addtogroup m17nInputMethodWin */ +/*=*/ +/*** @{ */ + +/***en + @brief Input driver for XIM. + + The input driver #minput_xim_driver is for the foreign input + method of name #Mxim. It uses XIM (X Input Methods) as a + background input engine. + + As the symbol #Mxim has property #Minput_driver whose value is + a pointer to this driver, the input method of language #Mnil + and name #Mxim uses this driver. + + Therefore, for such input methods, the driver dependent arguments + to the functions whose name begin with minput_ must be as follows. + + The argument $ARG of the function minput_open_im () must be a + pointer to the structure #MInputXIMArgIM. See the documentation + of #MInputXIMArgIM for more detail. + + The argument $ARG of the function minput_create_ic () must be a + pointer to the structure #MInputXIMArgIC. See the documentation + of #MInputXIMArgIC for more detail. + + The argument $ARG of the function minput_filter () must be a + pointer to the structure @c XEvent. The argument $KEY is ignored. + + The argument $ARG of the function minput_lookup () must be the + same one as that of the function minput_filter (). The argument + $KEY is ignored. */ + +/***ja + @brief XIMÍÑÆþÎϥɥ饤¥Ð + + ÆþÎϥɥ饤¥Ð #minput_xim_driver ¤Ï #Mxim ¤ò̾Á°¤È¤·¤Æ»ý¤Ä³°Éô + ÆþÎϥ᥽¥Ã¥ÉÍѤǤ¢¤ê¡¢ XIM (X Input Methods) ¤ò¥Ð¥Ã¥¯¥°¥é¥¦¥ó¥É¤Î + ÆþÎÏ¥¨¥ó¥¸¥ó¤È¤·¤Æ»ÈÍѤ¹¤ë¡£ + + ¥·¥ó¥Ü¥ë #Mxim ¤Ï¤³¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÃͤȤ¹¤ë¥×¥í¥Ñ¥Æ¥£ + #Minput_driver ¤ò»ý¤Ä¤¿¤á¡¢LANGUAGE ¤¬ #Mnil ¤Ç̾Á°¤¬ #Mxim ¤Ç + ¤¢¤ëÆþÎϥ᥽¥Ã¥É¤Ï¤³¤Î¥É¥é¥¤¥Ð¤òÍøÍѤ¹¤ë¡£ + + ¤·¤¿¤¬¤Ã¤Æ¡¢¤½¤ì¤é¤ÎÆþÎϥ᥽¥Ã¥ÉÍѤˤϡ¢minput_ ¤Ç»Ï¤Þ¤ë̾Á°¤ò»ý¤Ä + °Ê²¼¤Î´Ø¿ô·²¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô¤Ï¼¡¤Î¤è¤¦¤Ê¤â¤Î¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é + ¤Ê¤¤¡£ + + ´Ø¿ô minput_open_im () ¤Î°ú¿ô $ARG ¤Ï¹½Â¤ÂÎ #MInputXIMArgIM ¤Ø¤Î + ¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï #MInputXIMArgIM ¤Î¥É + ¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + ´Ø¿ô minput_create_ic () ¤Î°ú¿ô $ARG ¤Ï¹½Â¤ÂÎ #MInputXIMArgIC ¤Ø + ¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï #MInputXIMArgIC ¤Î + ¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£ + + ´Ø¿ô minput_filter () ¤Î°ú¿ô %ARG ¤Ï¹½Â¤ÂÎ @c XEvent ¤Ø¤Î¥Ý¥¤¥ó¥¿ + ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£°ú¿ô $KEY ¤Ï̵»ë¤µ¤ì¤ë¡£ + + ´Ø¿ô minput_lookup () ¤Î°ú¿ô $ARG ¤Ï´Ø¿ô function minput_filter () + ¤Î°ú¿ô $ARG ¤ÈƱ¤¸¤â¤Î¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ °ú¿ô $KEY ¤Ï¡¢Ìµ»ë¤µ¤ì + ¤ë¡£ */ + +MInputDriver minput_xim_driver = + { xim_open_im, xim_close_im, xim_create_ic, xim_destroy_ic, + xim_filter, xim_lookup, NULL }; + +/*=*/ + +/***en + @brief Symbol of the name "xim". + + The variable Mxim is a symbol of name "xim". It is a name of the + input method driver #minput_xim_driver. */ + +MSymbol Mxim; + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-X.h b/src/m17n-X.h new file mode 100644 index 0000000..8f3c670 --- /dev/null +++ b/src/m17n-X.h @@ -0,0 +1,161 @@ +/* m17n-X.h -- header file for the GUI API on X Windows. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_X_H_ +#define _M17N_X_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* For drawing. */ + +extern MSymbol Mdisplay; +extern MSymbol Mscreen; +extern MSymbol Mdrawable; +extern MSymbol Mwidget; +extern MSymbol Mdepth; +extern MSymbol Mcolormap; + +/* For inputting. */ + +extern MInputDriver minput_xim_driver; +extern MSymbol Mxim; + +/*** @ingroup m17nInputMethodWin */ +/***en + @brief Structure pointed to by the argument $ARG of the function + input_open_im (). + + The type #MInputXIMArgIM is the structure pointed to by the + argument $ARG of the function minput_open_im () for the foreign + input method of name #Mxim. */ + +/***ja + @brief ´Ø¿ô minput_open_im () ¤Î°ú¿ô $ARG ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ë¹½Â¤ÂÎ + + + #MInputXIMArgIM ·¿¤Ï¡¢´Ø¿ô minput_open_im () ¤¬Ì¾Á° #Mxim ¤ò»ý + ¤Ä³°ÉôÆþÎϥ᥽¥Ã¥É¤òÀ¸À®¤¹¤ëºÝ¤Ë°ú¿ô $ARG ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ë¹½Â¤ÂÎ¤Ç + ¤¢¤ë¡£ */ + +typedef struct +{ + /***en The meaning of the following four members are the same as + arguments to XOpenIM (). */ + /***ja °Ê²¼¤Î£´¤Ä¤Î¥á¥ó¥Ð¤Î°ÕÌ£¤Ï¡¢XOpenIM () ¤Î°ú¿ô¤Î°ÕÌ£¤ÈƱ¤¸¤Ç¤¢ + ¤ë¡£ */ + + /***en Display of the client. */ + /***ja ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Ç¥£¥¹¥×¥ì¥¤ */ + Display *display; + + /***en Pointer to the X resource database. */ + /***ja X ¥ê¥½¡¼¥¹¡¦¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿ */ + XrmDatabase db; + + /***en Full class name of the application. */ + /***ja ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î´°Á´¤Ê¥¯¥é¥¹Ì¾ */ + char *res_class; + + /***en Full resource name of the application. */ + /***ja ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î´°Á´¤Ê¥ê¥½¡¼¥¹Ì¾ */ + char *res_name; + + /***en Locale name under which an XIM is opened. */ + /***ja XIM¤¬¥ª¡¼¥×¥ó¤µ¤ì¤¿¥í¥±¡¼¥ë̾ */ + char *locale; + + /***en Arguments to XSetLocaleModifiers (). */ + /***ja XSetLocaleModifiers () ¤Î°ú¿ô */ + char *modifier_list; +} MInputXIMArgIM; + /*=*/ + +/*** @ingroup m17nInputMethodWin */ +/***en + @brief Structure pointed to by the argument $ARG of the function + minput_create_ic. + + The type #MInputXIMArgIC is the structure pointed to by the + argument $ARG of the function minput_create_ic () for the foreign + input method of name #Mxim. */ + +/***ja + @brief ´Ø¿ô minput_create_ic () ¤Î°ú¿ô $ARG ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ë¹½Â¤ÂÎ + + #MInputXIMArgIC ·¿¤Ï¡¢´Ø¿ô minput_create_ic () ¤¬Ì¾Á° #Mxim ¤ò + »ý¤Ä³°ÉôÆþÎϥ᥽¥Ã¥ÉÍѤ˸ƤФì¤ëºÝ¤Ë¡¢°ú¿ô $ARG ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ë¹½ + ¤ÂΤǤ¢¤ë¡£ */ + +typedef struct +{ + /***en Used as the arguments of @c XCreateIC following @c + XNInputStyle. If this is zero, ( @c XIMPreeditNothing | @c + XIMStatusNothing) is used, and and + are set to @c NULL. */ + /***ja @c XCreateIC ¤Î @c XNInputStyle ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ + ¥¼¥í¤Ê¤é¤Ð¡¢ ( @c XIMPreeditNothing | @c XIMStatusNothing) ¤¬ÍÑ + ¤¤¤é¤ì¡¢ ¤È ¤Ï @c NULL ¤ËÀßÄꤵ¤ì + ¤ë¡£ */ + + XIMStyle input_style; + /***en Used as the argument of @c XCreateIC following @c XNClientWindow. */ + /***ja @c XCreateIC ¤Î @c XNClientWindow ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ */ + + + Window client_win; + /***en Used as the argument of @c XCreateIC following @c XNFocusWindow. */ + /***ja @c XCreateIC ¤Î @c XNFocusWindow ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ */ + + Window focus_win; + /***en If non- @c NULL, used as the argument of @c XCreateIC following + @c XNPreeditAttributes. */ + /***ja @c NULL¤Ç¤Ê¤±¤ì¤Ð¡¢ @c XCreateIC following ¤Î@c + XNPreeditAttributes ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ */ + + XVaNestedList preedit_attrs; + /***en If non-NULL, used as the argument of @c XCreateIC following + @c XNStatusAttributes. */ + /***ja @c NULL¤Ç¤Ê¤±¤ì¤Ð¡¢ @c XCreateIC following ¤Î @c + XNStatusAttributes ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£ */ + + XVaNestedList status_attrs; +} MInputXIMArgIC; +/*=*/ + +#ifdef __cplusplus +} +#endif + +#endif /* not _M17N_X_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-core.c b/src/m17n-core.c new file mode 100644 index 0000000..eeee07f --- /dev/null +++ b/src/m17n-core.c @@ -0,0 +1,758 @@ +/* m17n-core.c -- body of the CORE API. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nIntro + @brief Introduction to the m17n library + + API LEVELS + + The API of the m17n library is divided into these four. + +
    +
  1. CORE API + + It provides basic modules to handle M-texts. They don't require + the m17n database. To use this API, an application program must + include and be linked by -lm17n-core. + +
  2. SHELL API + + It provides modules that utilize the m17n database. They load + various kinds of data from the database on demand. To use this + API, an application program must include and be linked by + -lm17n-core -lm17n. With that, CORE API is also available. + +
  3. GUI API + + It provides GUI modules such as drawing and inputting M-texts on a + window system. The API itself is independent on a window system, + but the m17n library must be configured to use a specific window + system. Currently, we support only the X Window System. To use + this API, an application program must include and + , and be linked by -lm17n-core -lm17n -lm17n-X. With + that, CORE and SHELL APIs are also available. + +
  4. MISC API + + It provides miscellaneous functions to support error handling and + debugging. This API can't be used by itself, but with one or more + APIs listed above. To use the API, an application program must + include in addition to one of , + , and . + +
+ + See also the section @ref m17n-config "m17n-config(1)". + + ENVIRONMENT VARIABLE + + The m17n library pays attention to these environment variables. + +
    +
  • @c M17NDIR + + Name of a directory that contains data of the m17n database. See + @ref m17nDatabase for the detail. + +
  • @c MDEBUG_XXXX + + Environment variables whose name start by "MDEBUG_" controls + printing of debug information. See @ref m17nDebug for the detail. + +
+ + API NAMING CONVENTION + + The library exports functions, variables, macros, and types. All + of them start by the letter 'm' or 'M' followed by an object name + (e.g. "symbol" and "plist", but "mtext" object is given the name + "text" to avoid double 'm' at the head) or a module name + (e.g. draw, input). + +
    + +
  • functions -- mobject () or mobject_xxx () + + They start with 'm' followed by lower case object name. For + example, msymbol (), mtext_ref_char (), mdraw_text (). + +
  • non-symbol variables -- mobject, or mobject_xxx + + The naming convention is the same as functions (e.g. mface_large). + +
  • symbol variables -- Mname + + Variables of type MSymbol start with 'M' followed by their names + (e.g. Mlanguage (name is "langauge"), Miso_2022 (name is + "iso-2022"). + +
  • macross -- MOBJECT_XXX + + They start by 'M' followed by upper case object names. + +
  • types -- MObject or MObjectXxx + + They start by 'M' followed by capitalized object names (e.g. + MConverter, MInputDriver). + +
+ + */ + +/***ja + @addtogroup m17nIntro + + @e m17n¥é¥¤¥Ö¥é¥ê ¤Ï C ¸À¸ìÍÑ¿¸À¸ì¾ðÊó½èÍý¥é¥¤¥Ö¥é¥ê¤Ç¤¢¤ë¡£ + ¤³¤Î¥é¥¤¥Ö¥é¥ê¤Ï¡¢Â¿¸À¸ìʸ½ñ¤ò°·¤¦¤¿¤á¤ËɬÍפʰʲ¼¤Î´ðËܵ¡Ç½¤ò¥¢¥× + ¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤ËÄ󶡤¹¤ë¡£ + + @li ¿¸À¸ì¥Æ¥­¥¹¥ÈÍÑ¥ª¥Ö¥¸¥§¥¯¥È¤È¤·¤Æ¤Î¹½Â¤ÂÎ @e M-text + + @li M-text ¤ò°·¤¦¤¿¤á¤Î¿¤¯¤Î´Ø¿ô¡¦¥Þ¥¯¥í·² + + @li ¼ï¡¹¤Î¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥¨¥ó¥³¡¼¥É¤µ¤ì¤¿¥Æ¥­¥¹¥È¤È M-text ´Ö¤ÎÊÑ´¹ + ¤ò¹Ô¤Ê¤¦¥Ç¥³¡¼¥À¡¿¥¨¥ó¥³¡¼¥À + + @li Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤Ë²Ã¤¨¡¢¤½¤ì¤ÈƱ¤¸¿ô¤ÎÈó Unicode ʸ»ú¤ò°· + ¤¨¤ëµðÂç¤Êʸ»ú¶õ´Ö + + @li ʸ»úËè¤Î¾ðÊó¤ò¸úΨÎɤ¯³ÊǼ¤¹¤ë¹½Â¤ÂÎ @e ʸ»ú¥Æ¡¼¥Ö¥ë (CharTable) + + @li ¥ª¥×¥·¥ç¥ó¡§M-text¤Î¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¾å¤Ç¤Îɽ¼¨¡¦ÆþÎÏ¥·¥¹¥Æ¥à¡£ + ¡Ê¤³¤Î¤¿¤á¤Î API ¤Ï m17n-win.h ¤Ë´Þ¤Þ¤ì¤Æ¤¤¤ë¡£¡Ë */ +/*=*/ +/*** @{ */ +#ifdef FOR_DOXYGEN +/***en + The #M17NLIB_MAJOR_VERSION macro gives the major version number + of the m17n library. */ + +/***ja + ¥Þ¥¯¥í #M17NLIB_MAJOR_VERSION ¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤Î¥á¥¸¥ã¡¼¥Ð¡¼¥¸¥ç + ¥óÈÖ¹æ¤òÍ¿¤¨¤ë¡£ */ + +#define M17NLIB_MAJOR_VERSION + +/*=*/ + +/***en + The #M17NLIB_MINOR_VERSION macro gives the minor version number + of the m17n library. */ + +/***ja + ¥Þ¥¯¥í #M17NLIB_MINOR_VERSION ¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤Î¥Þ¥¤¥Ê¡¼¥Ð¡¼¥¸¥ç + ¥óÈÖ¹æ¤òÍ¿¤¨¤ë¡£ */ + +#define M17NLIB_MINOR_VERSION + +/*=*/ + +/***en + The #M17NLIB_VERSION_NAME macro gives the version name of the + m17n library as a string. */ + +/***ja + ¥Þ¥¯¥í #M17NLIB_VERSION_NAME ¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤Î¥Ð¡¼¥¸¥ç¥ó̾¤ò + C-string ¤Î·Á¤ÇÍ¿¤¨¤ë¡£ */ + +#define M17NLIB_VERSION_NAME + +/*=*/ + +/***en + @brief Initialize the m17n library. + + The macro M17N_INIT () initializes the m17n library. This + function must be called before any m17n functions are used. + + If the initialization was successful, the external variable @c + merror_code is set to 0. Otherwise it is set to -1. */ + +/***ja + @brief m17n ¥é¥¤¥Ö¥é¥ê¤Î½é´ü²½ + + ¥Þ¥¯¥í M17N_INIT () ¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤ò½é´ü²½¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é + ¥ê¤ò»È¤¦¤È¤­¤Ï¡¢ºÇ½é¤Ë¤³¤Î´Ø¿ô¤ò¸Æ¤Ð¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + ÊÑ¿ô #merror_code ¤Ï¡¢½é´ü²½¤¬À®¸ù¤¹¤ì¤Ð 0 ¤Ë¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 + ¤ËÀßÄꤵ¤ì¤ë¡£ */ + +#define M17N_INIT() + +/*=*/ + +/***en + @brief Finalize the m17n library. + + The macro M17N_FINI () finalizes the m17n library. It frees all the + memory area used by the m17n library. Once this function is + called, no m17n functions should be used until the + macro M17N_INIT () is called again. */ + +/***ja + @brief m17n ¥é¥¤¥Ö¥é¥ê¤Î½ªÎ» */ + +#define M17N_FINI() +#endif /* FOR_DOXYGEN */ +/*=*/ +/*** @} */ +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include +#include + +#include "m17n-core.h" +#include "m17n-misc.h" +#include "internal.h" + +static int core_initialized; + +static void +default_error_handler (enum MErrorCode err) +{ + exit (err); +} + +static struct timeval time_stack[16]; +static int time_stack_index; + +static int report_header_printed; + + +/* Internal API */ + +void +mdebug__report_object (char *name, M17NObjectArray *array) +{ + if (! (mdebug__flag & MDEBUG_FINI)) + return; + if (! report_header_printed) + { + fprintf (stderr, "%16s %7s %7s %7s\n", + "object", "created", "freed", "alive"); + fprintf (stderr, "%16s %7s %7s %7s\n", + "------", "-------", "-----", "-----"); + report_header_printed = 1; + } + fprintf (stderr, "%16s %7d %7d %7d\n", name, + array->used, array->used - array->count, array->count); +} + + +void *(*mdatabase__finder) (MSymbol tag1, MSymbol tag2, + MSymbol tag3, MSymbol tag4); +void *(*mdatabase__loader) (void *); + +int mdebug__flag; + +void +mdebug__push_time () +{ + struct timezone tz; + + gettimeofday (time_stack + time_stack_index++, &tz); +} + +void +mdebug__pop_time () +{ + time_stack_index--; +} + +void +mdebug__print_time () +{ + struct timeval tv; + struct timezone tz; + long diff; + + gettimeofday (&tv, &tz); + diff = ((tv.tv_sec - time_stack[time_stack_index - 1].tv_sec) * 1000000 + + (tv.tv_usec - time_stack[time_stack_index - 1].tv_usec)); + fprintf (stderr, "%8ld ms.", diff); + time_stack[time_stack_index - 1] = tv; +} + +#define SET_DEBUG_FLAG(env_name, mask) \ + do { \ + char *env_value = getenv (env_name); \ + \ + if (env_value && env_value[0] == '1') \ + mdebug__flag |= (mask); \ + } while (0) + + + +/* External API */ + +/* The following two are actually not exposed to a user but concealed + by the macro M17N_INIT (). */ + +void +m17n_init_core (void) +{ + int mdebug_mask = MDEBUG_INIT; + + if (core_initialized) + return; + + merror_code = 0; + m17n_memory_full_handler = default_error_handler; + + mdebug__flag = 0; + SET_DEBUG_FLAG ("MDEBUG_INIT", MDEBUG_INIT); + SET_DEBUG_FLAG ("MDEBUG_FINI", MDEBUG_FINI); + SET_DEBUG_FLAG ("MDEBUG_CHARSET", MDEBUG_CHARSET); + SET_DEBUG_FLAG ("MDEBUG_CODING", MDEBUG_CODING); + SET_DEBUG_FLAG ("MDEBUG_DATABASE", MDEBUG_DATABASE); + SET_DEBUG_FLAG ("MDEBUG_FONT", MDEBUG_FONT); + SET_DEBUG_FLAG ("MDEBUG_FONT_FLT", MDEBUG_FONT_FLT); + SET_DEBUG_FLAG ("MDEBUG_FONT_OTF", MDEBUG_FONT_OTF); + SET_DEBUG_FLAG ("MDEBUG_INPUT", MDEBUG_INPUT); + + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + if (msymbol__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize symbol module.")); + if (mplist__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize plist module.")); + if (mtext__init () < 0) + goto err; + if (mtext__prop_init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize mtext module.")); + if (mchartable__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize chartable module.")); + + mdatabase__finder = NULL; + mdatabase__loader = NULL; + core_initialized = 1; + + err: + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the core modules.")); + MDEBUG_POP_TIME (); + + report_header_printed = 0; +} + +void +m17n_fini_core (void) +{ + int mdebug_mask = MDEBUG_FINI; + + if (core_initialized) + { + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize chartable module.")); + mchartable__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize textprop module.")); + mtext__prop_fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize mtext module.")); + mtext__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize symbol module.")); + msymbol__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize plist module.")); + mplist__fini (); + core_initialized = 0; + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize the core modules.")); + MDEBUG_POP_TIME (); + } +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ +/*=*/ + +/***en + @addtogroup m17nObject + @brief Managed objects are objects managed by the reference count. + + There are some types of m17n objects that are managed by their + reference count. Those objects are called @e managed @e objects. + When created, the reference count of a managed object is + initialized to one. The m17n_object_ref () function increments + the reference count of a managed object by one, and the + m17n_object_unref () function decrements by one. A managed + object is automatically freed when its reference count becomes + zero. + + A property whose key is a managing key can have only a managed + object as its value. Such functions as msymbol_put () and + mplist_put () pay special attention to such a property. + + In addition to the predefined managed object types, users can + define their own managed object types. See the documentation of + the m17n_object () for the details. */ + +/*** @{ */ +/*=*/ +/***en + @brief Allocate a managed object. + + The m17n_object () function allocates a new managed object of + $SIZE bytes and sets its reference count to 1. $FREER is the + function that is used to free the object when the reference count + becomes 0. If $FREER is NULL, the object is freed by the free () + function. + + The heading bytes of the allocated object is occupied by + #M17NObjectHead. That area is reserved for the m17n library and + application programs should never touch it. + + @return + This function returns a newly allocated object. + + @errors + This function never fails. */ + +#if EXAMPLE_CODE +typedef struct +{ + M17NObjectHead head; + int mem1; + char *mem2; +} MYStruct; + +void +my_freer (void *obj) +{ + free (((MYStruct *) obj)->mem2); + free (obj); +} + +void +my_func (MText *mt, MSymbol key, int num, char *str) +{ + MYStruct *st = m17n_object (sizeof (MYStruct), my_freer); + + st->mem1 = num; + st->mem2 = strdup (str); + /* KEY must be a managing key. */ + mtext_put_prop (mt, 0, mtext_len (mt), key, st); + /* This sets the reference count of ST back to 1. */ + m17n_object_unref (st); +} +#endif + +void * +m17n_object (int size, void (*freer) (void *)) +{ + M17NObject *obj = malloc (size); + + obj->ref_count = 1; + obj->u.freer = freer; + return obj; +} + +/*=*/ + +/***en + @brief Increment the reference count of a managed object. + + The m17n_object_ref () function increments the reference count of + the managed object pointed to by $OBJECT. + + @return + This function returns the resulting reference count if it fits in + a 16-bit unsigned integer (i.e. less than 0x10000). Otherwise, it + return -1. + + @errors + This function never fails. */ + +int +m17n_object_ref (void *object) +{ + M17NObject *obj = (M17NObject *) object; + M17NObjectRecord *record; + unsigned *count; + + if (! obj->ref_count_extended) + { + if (++obj->ref_count) + return (int) obj->ref_count; + MSTRUCT_MALLOC (record, MERROR_OBJECT); + record->freer = obj->u.freer; + MLIST_INIT1 (record, counts, 1); + MLIST_APPEND1 (record, counts, 0, MERROR_OBJECT); + obj->u.record = record; + obj->ref_count_extended = 1; + } + else + record = obj->u.record; + + count = record->counts; + while (*count == 0xFFFFFFFF) + *(count++) = 0; + (*count)++; + if (*count == 0xFFFFFFFF) + MLIST_APPEND1 (record, counts, 0, MERROR_OBJECT); + return -1; +} + +/*=*/ + +/***en + @brief Decrement the reference count of a managed object. + + The m17n_object_unref () function decrements the reference count + of the managed object pointed to by $OBJECT. When the reference + count becomes zero, the object is freed by its freer function. + + @return + This function returns the resulting reference count if it fits in + a 16-bit unsigned integer (i.e. less than 0x10000). Otherwise, it + returns -1. Thus, the return value zero means that $OBJECT is + freed. + + @errors + This function never fails. */ + +int +m17n_object_unref (void *object) +{ + M17NObject *obj = (M17NObject *) object; + M17NObjectRecord *record; + unsigned *count; + + if (! obj->ref_count_extended) + { + if (! --obj->ref_count) + { + if (obj->u.freer) + (obj->u.freer) (object); + else + free (object); + return 0; + } + return (int) obj->ref_count; + } + + record = obj->u.record; + count = record->counts; + while (! *count) + *(count++) = 0xFFFFFFFF; + (*count)--; + if (! record->counts[0]) + { + obj->ref_count_extended = 0; + obj->ref_count--; + obj->u.freer = record->freer; + MLIST_FREE1 (record, counts); + free (record); + } + return -1; +} + +/*=*/ + +/*** @} */ + +/***en + @addtogroup m17nError Error handling + @brief Error handling of the m17n library. + + There are two types of errors that may happen in a function of + the m17n library. + + The first type is argument errors. When a library function is + called with invalid arguments, it returns a value that indicates + error and at the same time sets the external variable @e + merror_code to a non-zero integer. + + The second type is memory allocation errors. When the required + amount of memory is not available on the system, m17n library + functions call a function pointed to by the external variable @c + m17n_memory_full_handler. The default value of the variable is a + pointer to the default_error_handle () function, which just calls + exit (). */ + +/***ja + @addtogroup m17nError ¥¨¥é¡¼½èÍý + @brief m17n ¥é¥¤¥Ö¥é¥ê¤Î¥¨¥é¡¼½èÍý + + m17n ¥é¥¤¥Ö¥é¥ê¤Î´Ø¿ô¤Ç¤Ï¡¢£²¤Ä¤Î¼ïÎà¤Î¥¨¥é¡¼¤¬µ¯¤³¤êÆÀ¤ë¡£ + + °ì¤Ä¤Ï°ú¿ô¤Î¥¨¥é¡¼¤Ç¤¢¤ë¡£¥é¥¤¥Ö¥é¥ê¤Î´Ø¿ô¤¬ÂÅÅö¤Ç¤Ê¤¤°ú¿ô¤È¤È¤â¤Ë + ¸Æ¤Ð¤ì¤¿¾ì¹ç¡¢¤½¤Î´Ø¿ô¤Ï¥¨¥é¡¼¤ò°ÕÌ£¤¹¤ëÃͤòÊÖ¤·¡¢Æ±»þ¤Ë³°ÉôÊÑ¿ô + #merror_code ¤Ë¥¼¥í¤Ç¤Ê¤¤À°¿ô¤ò¥»¥Ã¥È¤¹¤ë¡£ + + ¤â¤¦°ì¤Ä¤Î¼ïÎà¤Ï¥á¥â¥ê³äÅö¤Æ¥¨¥é¡¼¤Ç¤¢¤ë¡£¥·¥¹¥Æ¥à¤¬É¬ÍפÊÎ̤Υá¥â + ¥ê¤ò³äÅö¤Æ¤ë¤³¤È¤¬¤Ç¤­¤Ê¤¤¾ì¹ç¡¢¥é¥¤¥Ö¥é¥ê´Ø¿ô¤Ï³°ÉôÊÑ¿ô @c + m17n_memory_full_handler ¤¬»Ø¤¹´Ø¿ô¤ò¸Æ¤Ö¡£¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢Ã±¤Ë + exit () ¤ò¸Æ¤Ö¤³¤È¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ +*/ + +/*** @{ */ + +/*=*/ + +/***en @brief External variable to hold error code of the m17n library + + The external variable #merror_code holds an error code of the + m17n library. When a library function is called with an invalid + argument, it sets this variable to one of @c enum #MErrorCode. + + This variable initially has the value 0. */ + +/***ja @brief m17n ¥é¥¤¥Ö¥é¥ê¤Î¥¨¥é¡¼¥³¡¼¥É¤òÊÝ»ý¤¹¤ë³°ÉôÊÑ¿ô + + ³°ÉôÊÑ¿ô #merror_code ¤Ï¡¢m17n ¥é¥¤¥Ö¥é¥ê¤Î¥¨¥é¡¼¥³¡¼¥É¤òÊÝ»ý¤¹¤ë¡£ + ¥é¥¤¥Ö¥é¥ê´Ø¿ô¤¬ÂÅÅö¤Ç¤Ê¤¤°ú¿ô¤È¤È¤â¤Ë¸Æ¤Ð¤ì¤¿ºÝ¤Ë¤Ï¡¢ + ¤³¤ÎÊÑ¿ô¤ò @c enum #MErrorCode ¤Î°ì¤Ä¤Ë¥»¥Ã¥È¤¹¤ë¡£ + + ¤³¤ÎÊÑ¿ô¤Î½é´üÃͤϣ°¤Ç¤¢¤ë¡£ */ + +enum MErrorCode merror_code; + +/*=*/ + +/***en @brief Memory allocation error handler + + The external variable #m17n_memory_full_handler holds a pointer + to the function to call when a library function failed to allocate + memory. $ERR is one of @c enum #MErrorCode indicating in which + function the error occurred. + + This variable initially points a function that simply calls the + exit () function with $ERR as an argument. + + An application program that needs a different error handling can + change this variable to point a proper function. */ + +/***ja @brief ¥á¥â¥ê³äÅö¤Æ¥¨¥é¡¼¥Ï¥ó¥É¥é + + ÊÑ¿ô #m17n_memory_full_handler ¤Ï¡¢¥é¥¤¥Ö¥é¥ê´Ø¿ô¤¬¥á¥â¥ê³äÅö¤Æ + ¤Ë¼ºÇÔ¤·¤¿ºÝ¤Ë¸Æ¤Ö¤Ù¤­´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£$ERR ¤Ï @c enum + #MErrorCode ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ê¡¢¤É¤Î¥é¥¤¥Ö¥é¥ê´Ø¿ô¤Ç¥¨¥é¡¼¤¬µ¯¤Ã¤¿ + ¤«¤ò¼¨¤¹¡£ + + @anchor test + + ½é´üÀßÄê¤Ç¤Ï¡¢¤³¤ÎÊÑ¿ô¤Ïñ¤Ë exit () ¤ò $ERR ¤ò°ú¿ô¤È¤·¤Æ + ¸Æ¤Ö´Ø¿ô¤ò»Ø¤·¤Æ¤¤¤ë¡£ + + ¤³¤ì¤È¤Ï°Û¤Ê¤ë¥¨¥é¡¼½èÍý¤òɬÍפȤ¹¤ë¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Ï¡¢¤³¤ÎÊÑ¿ô¤ò + ŬÅö¤Ê´Ø¿ô¤ËÀßÄꤹ¤ë¤³¤È¤Ç¡¢ÌÜŪ¤òãÀ®¤Ç¤­¤ë¡£ */ + +void (*m17n_memory_full_handler) (enum MErrorCode err); + +/*** @} */ + +/*=*/ + +/***en + @addtogroup m17nDebug + @brief Support for m17n library users to debug their programs. + + The m17n library provides the following facilities to support the + library users to debug their programs. + +
    + +
  • Environment variables to control printing of various + information. + +
      + +
    • MDEBUG_INIT -- If set to 1, print information about the + library initialization on the call of M17N_INIT (). + +
    • MDEBUG_FINI -- If set to 1, print counts of objects that are + not yet freed on the call of M17N_FINI (). + +
    • MDEBUG_CHARSET -- If set to 1, print information about + charsets being loaded from the m17n database. + +
    • MDEBUG_CODING -- If set to 1, print information about coding + systems being loaded from the m17n database. + +
    • MDEBUG_DATABASE -- If set to 1, print information about + data being loaded from the m17n database. + +
    • MDEBUG_FONT -- If set to 1, print information about fonts + being selected and opened. + +
    • MDEBUG_FONT_FLT -- If set to 1, print information about which + command of Font Layout Table are being executed. + +
    • MDEBUG_FONT_OTF -- If set to 1, print information about which + feature of OpenType Layout Table are being executed. + +
    • MDEBUG_INPUT -- If set to 1, print information about how an + input method is running. + +
    • MDEBUG_ALL -- Setting this variable to 1 is equivalent to + setting all the above variables to 1. + +
    + +
  • Functions to print various objects in a human readable way. + See the documentation of mdebug_dump_XXXX () functions. + +
  • The hook function called on an error. See the documentation + of mdebug_hook (). + +
+*/ + +/*=*/ +/*** @{ */ +/*=*/ + +/***en + @brief Hook function called on an error. + + The mdebug_hook () function is called when an error happens. It + returns -1q without doing anything. It is useful to set a break + point on this function in a debugger. */ + +int +mdebug_hook () +{ + return -1; +} + +/*=*/ + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-core.h b/src/m17n-core.h new file mode 100644 index 0000000..f401456 --- /dev/null +++ b/src/m17n-core.h @@ -0,0 +1,529 @@ +/* m17n-core.h -- header file for the CORE API of the m17n library. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_CORE_H_ +#define _M17N_CORE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * Header file for m17n library. + */ + +/* (C1) Introduction */ + +/***en @defgroup m17nIntro Introduction */ +/***ja @defgroup m17nIntro ¤Ï¤¸¤á¤Ë */ +/*=*/ + +#define M17NLIB_MAJOR_VERSION 1 +#define M17NLIB_MINOR_VERSION 0 +#define M17NLIB_VERSION_NAME "1.0" + +extern void m17n_init_core (void); +#define M17N_INIT() m17n_init_core () +extern void m17n_fini_core (void); +#define M17N_FINI() m17n_fini_core () + +/***en @defgroup m17nCore CORE API */ +/***ja @defgroup m17nCore CORE API */ +/*=*/ +/*** @ingroup m17nCore */ +/***en @defgroup m17nObject Managed Object */ +/***ja @defgroup m17nObject ´ÉÍý²¼¥ª¥Ö¥¸¥§¥¯¥È */ +/*=*/ + +/*** @ingroup m17nObject */ +/***en + @brief The first member of a managed object. + + When an application program defines a new structure for managed + objects, its first member must be of the type @c struct + #M17NObjectHead. Its contents are used by the m17n library, and + application programs should never touch them. */ + +typedef struct +{ + void *filler[2]; +} M17NObjectHead; + +/*=*/ + +/* Return a newly allocated managed object. */ +extern void *m17n_object_setup (int size, void (*freer) (void *)); + +/* Increment the reference count of managed object OBJECT. */ +extern int m17n_object_ref (void *object); + +/* Decrement the reference count of managed object OBJECT. */ +extern int m17n_object_unref (void *object); + +/*=*/ + +/* (C2) Symbol handling */ + +/*** @ingroup m17nCore */ +/***en @defgroup m17nSymbol Symbol */ +/***ja @defgroup m17nSymbol ¥·¥ó¥Ü¥ë */ +/*=*/ + +/*** + @ingroup m17nSymbol */ +/***en + @brief Type of symbols. + + The type #MSymbol is for a @e symbol object. Its internal + structure is concealed from application programs. */ + +/***ja + @brief ¥·¥ó¥Ü¥ë¤Î·¿ + + #MSymbol ¤Ï¥·¥ó¥Ü¥ë¥ª¥Ö¥¸¥§¥¯¥È¤Î·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£ */ + +typedef struct MSymbol *MSymbol; + +/*=*/ + +/* Predefined symbols. */ +extern MSymbol Mnil; +extern MSymbol Mt; +extern MSymbol Mstring; +extern MSymbol Msymbol; +extern MSymbol Mtext; + +/* Return a symbol of name NAME. */ +extern MSymbol msymbol (const char *name); + +/* Return a managing key of name NAME. */ +extern MSymbol msymbol_as_managing_key (const char *name); + +/* Return a symbol of name NAME if it already exists. */ +extern MSymbol msymbol_exist (const char *name); + +/* Return the name of SYMBOL. */ +extern char *msymbol_name (MSymbol symbol); + +/* Give SYMBOL KEY property with value VALUE. */ +extern int msymbol_put (MSymbol symbol, MSymbol key, void *val); + +/*** Return KEY property value of SYMBOL. */ +extern void *msymbol_get (MSymbol symbol, MSymbol key); + +/* + * (2-1) Property List + */ + +/*** @ingroup m17nCore */ +/***en @defgroup m17nPlist Property List */ +/***ja @defgroup m17nPlist ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¡¦¥ª¥Ö¥¸¥§¥¯¥È */ +/*=*/ + +/*** + @ingroup m17nPlist */ +/***en + @brief Type of property list objects. + + The type #MPlist is for a property list object. Its internal + structure is concealed from application programs. */ + +typedef struct MPlist MPlist; + +/*=*/ + +extern MSymbol Mplist, Minteger; + +extern MPlist *mplist (); + +extern MPlist *mplist_copy (MPlist *plist); + +extern MPlist *mplist_add (MPlist *plist, MSymbol key, void *val); + +extern MPlist *mplist_push (MPlist *plist, MSymbol key, void *val); + +extern void *mplist_pop (MPlist *plist); + +extern MPlist *mplist_put (MPlist *plist, MSymbol key, void *val); + +extern void *mplist_get (MPlist *plist, MSymbol key); + +extern MPlist *mplist_find_by_key (MPlist *plist, MSymbol key); + +extern MPlist *mplist_find_by_value (MPlist *plist, void *val); + +extern MPlist *mplist_next (MPlist *plist); + +extern MPlist *mplist_set (MPlist *plist, MSymbol key, void *val); + +extern int mplist_length (MPlist *plist); + +extern MSymbol mplist_key (MPlist *plist); + +extern void *mplist_value (MPlist *plist); + +/* (S1) Characters */ + +/*=*/ +/*** @ingroup m17nCore */ +/***en @defgroup m17nCharacter Character */ +/***ja @defgroup m17nCharacter ʸ»ú */ +/*=*/ + +#define MCHAR_MAX 0x3FFFFF +/*#define MCHAR_MAX 0x7FFFFFFF*/ + +extern MSymbol Mscript; +extern MSymbol Mname; +extern MSymbol Mcategory; +extern MSymbol Mcombining_class; +extern MSymbol Mbidi_category; +extern MSymbol Msimple_case_folding; +extern MSymbol Mcomplicated_case_folding; + +extern MSymbol mchar_define_property (char *name, MSymbol type); + +extern void *mchar_get_prop (int c, MSymbol key); + +extern int mchar_put_prop (int c, MSymbol key, void *val); + +/* (C3) Handling chartable */ + +/*** @ingroup m17nCore */ +/***en @defgroup m17nChartable Chartable */ +/***ja @defgroup m17nChartable ʸ»ú¥Æ¡¼¥Ö¥ë */ +/*=*/ +extern MSymbol Mchar_table; + +/*** + @ingroup m17nChartable */ +/***en + @brief Type of chartables. + + The type #MCharTable is for a @e chartable objects. Its + internal structure is concealed from application programs. */ + +/***ja + @brief ʸ»ú¥Æ¡¼¥Ö¥ë¤Î·¿ + + #MCharTable ·¿¤Ï @e ʸ»ú¥Æ¡¼¥Ö¥ë ¥ª¥Ö¥¸¥§¥¯¥ÈÍѤι½Â¤ÂΤǤ¢¤ë¡£ + ÆâÉô¹½Â¤¤Ï¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ */ + +typedef struct MCharTable MCharTable; +/*=*/ + +extern MCharTable *mchartable (MSymbol key, void *default_value); + +extern void *mchartable_lookup (MCharTable *table, int c); + +extern int mchartable_set (MCharTable *table, int c, void *val); + +extern int mchartable_set_range (MCharTable *table, int from, int to, + void *val); + +extern int mchartable_map (MCharTable *table, void *ignore, + void (*func) (int from, int to, + void *val, void *arg), + void *func_arg); + +extern void mchartable_range (MCharTable *table, int *from, int *to); + +/* + * (5) Handling M-text. + * "M" of M-text stands for: + * o Multilingual + * o Metamorphic + * o More than string + */ + +/*** @ingroup m17nCore */ +/***en @defgroup m17nMtext M-text */ +/***ja @defgroup m17nMtext M-text */ +/*=*/ + +/* + * (5-1) M-text basics + */ + +/*** @ingroup m17nMtext */ +/***en + @brief Type of @e M-texts. + + The type #MText is for an @e M-text object. Its internal + structure is concealed from application programs. */ + +/***ja + @brief @e MText Íѹ½Â¤ÂÎ + + #Mtext ¹½Â¤ÂÎ¤Ï @e M-text ¥ª¥Ö¥¸¥§¥¯¥È¤ËÍѤ¤¤é¤ì¤ë¡£ÆâÉô¹½Â¤¤Ï¥¢ + ¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ + + @latexonly \IPAlabel{MText} @endlatexonly + @latexonly \IPAlabel{MText->MPlist} @endlatexonly */ + +typedef struct MText MText; + +/*=*/ + +extern MText *mtext (); + +/*=*/ + +/***en + @brief Enumeration for specifying the format of an M-text. + + The enum #MTextFormat is used as an argument of the + mtext_from_data () function to specify the format of data from + which an M-text is created. */ + +enum MTextFormat + { + MTEXT_FORMAT_US_ASCII, + MTEXT_FORMAT_UTF_8, + MTEXT_FORMAT_UTF_16LE, + MTEXT_FORMAT_UTF_16BE, + MTEXT_FORMAT_UTF_32LE, + MTEXT_FORMAT_UTF_32BE, + MTEXT_FORMAT_MAX + }; +/*=*/ + +extern MText *mtext_from_data (void *data, int nitems, + enum MTextFormat format); + + +/*=*/ + +/* + * (5-2) Functions to manipulate M-texts. They correspond to string + * manipulating functions in libc. + * In the following functions, mtext_XXX() corresponds to strXXX(). + */ + +extern int mtext_len (MText *mt); + +extern int mtext_ref_char (MText *mt, int pos); + +extern int mtext_set_char (MText *mt, int pos, int c); + +extern MText *mtext_copy (MText *mt1, int pos, MText *mt2, int from, int to); + +extern int mtext_compare (MText *mt1, int from1, int to1, + MText *mt2, int from2, int to2); + +extern int mtext_case_compare (MText *mt1, int from1, int to1, + MText *mt2, int from2, int to2); + +extern int mtext_character (MText *mt, int from, int to, int c); + +extern int mtext_del (MText *mt, int from, int to); + +extern int mtext_ins (MText *mt1, int pos, MText *mt2); + +extern int mtext_ins_char (MText *mt, int pos, int c, int n); + +extern MText *mtext_cat_char (MText *mt, int c); + +extern MText *mtext_duplicate (MText *mt, int from, int to); + +extern MText *mtext_dup (MText *mt); + +extern MText *mtext_cat (MText *mt1, MText *mt2); + +extern MText *mtext_ncat (MText *mt1, MText *mt2, int n); + +extern MText *mtext_cpy (MText *mt1, MText *mt2); + +extern MText *mtext_ncpy (MText *mt1, MText *mt2, int n); + +extern int mtext_chr (MText *mt, int c); + +extern int mtext_rchr (MText *mt, int c); + +extern int mtext_cmp (MText *mt1, MText *mt2); + +extern int mtext_ncmp (MText *mt1, MText *mt2, int n); + +extern int mtext_spn (MText *mt1, MText *mt2); + +extern int mtext_cspn (MText *mt1, MText *mt2); + +extern int mtext_pbrk (MText *mt1, MText *mt2); + +extern int mtext_text (MText *mt1, int pos, MText *mt2); + +extern int mtext_search (MText *mt1, int from, int to, MText *mt2); + +extern MText *mtext_tok (MText *mt, MText *delim, int *pos); + +extern int mtext_casecmp (MText *mt1, MText *mt2); + +extern int mtext_ncasecmp (MText *mt1, MText *mt2, int n); + +/* + * (5-3) Text properties + */ + +/*** @ingroup m17nCore */ +/***en @defgroup m17nTextProperty Text Property */ +/***ja @defgroup m17nTextProperty ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ */ +/*=*/ +/*** @ingroup m17nTextProperty */ +/***en + @brief Flag bits to control text property. + + The mtext_property () funciton accepts logical OR of these flag + bits as an argument. They control the behaviour of the created + text property as described in the documentation of each flag + bit. */ + +enum MTextPropertyControl + { + /***en If this flag bit is on, an M-text inserted at the start + position or at the middle of the text property inherits the + text property. */ + MTEXTPROP_FRONT_STICKY = 0x01, + + /***en If this flag bit is on, an M-text inserted at the end + position or at the middle of the text property inherits the + text property. */ + MTEXTPROP_REAR_STICKY = 0x02, + + /***en If this flag bit is on, the text property is removed if a + text in its region is modified. */ + MTEXTPROP_VOLATILE_WEAK = 0x04, + + /***en If this flag bit is on, the text property is removed if a + text or the other text property in its region is modified. */ + MTEXTPROP_VOLATILE_STRONG = 0x08, + + /***en If this flag bit is on, the text property is not + automatically merged with the others. */ + MTEXTPROP_NO_MERGE = 0x10, + + MTEXTPROP_CONTROL_MAX = 0x1F + }; + +/*=*/ +extern MSymbol Mtext_prop_serializer; +extern MSymbol Mtext_prop_deserializer; + + +/*** @ingroup m17nTextProperty */ +/***en + @brief Type of serializer functions. + + This is the type of serializer functions. If the key of a symbol + property is #Msymbol_prop_serializer, the value must be of this + type. + + @seealso Mtext_prop_serialize (), Mtext_prop_serializer +*/ + +typedef MPlist *(*MTextPropSerializeFunc) (void *val); + +/*** @ingroup m17nTextProperty */ +/***en + @brief Type of deserializer functions. + + This is the type of deserializer functions. If the key of a + symbol property is #Msymbol_prop_deserializer, the value must be + of this type. + + @seealso Mtext_prop_deserialize (), Mtext_prop_deserializer +*/ +typedef void *(*MTextPropDeserializeFunc) (MPlist *plist); + +extern void *mtext_get_prop (MText *mt, int pos, MSymbol key); + +extern int mtext_get_prop_values (MText *mt, int pos, MSymbol key, + void **values, int num); + +extern int mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys); + +extern int mtext_put_prop (MText *mt, int from, int to, + MSymbol key, void *val); + +extern int mtext_put_prop_values (MText *mt, int from, int to, + MSymbol key, void **values, int num); + +extern int mtext_push_prop (MText *mt, int from, int to, + MSymbol key, void *val); + +extern int mtext_pop_prop (MText *mt, int from, int to, + MSymbol key); + +extern int mtext_change_prop (MText *mt, int from, int to, + MSymbol key, + int (*func) (int, void ***, int *)); + +extern int mtext_prop_range (MText *mt, MSymbol key, int pos, + int *from, int *to, int deeper); + +/*=*/ +typedef struct MTextProperty MTextProperty; + +/*=*/ + +extern MTextProperty *mtext_property (MSymbol key, void *val, + int control_bits); + +extern MText *mtext_property_mtext (MTextProperty *prop); + +extern MSymbol mtext_property_key (MTextProperty *prop); + +extern void *mtext_property_value (MTextProperty *prop); + +extern int mtext_property_start (MTextProperty *prop); + +extern int mtext_property_end (MTextProperty *prop); + +extern MTextProperty *mtext_get_property (MText *mt, int pos, MSymbol key); + +extern int mtext_get_properties (MText *mt, int pos, MSymbol key, + MTextProperty **props, int num); + +extern int mtext_attach_property (MText *mt, int from, int to, + MTextProperty *prop); + +extern int mtext_detach_property (MTextProperty *prop); + +extern int mtext_push_property (MText *mt, int from, int to, + MTextProperty *prop); + +extern MText *mtext_serialize (MText *mt, int from, int to, + MPlist *property_list); + +extern MText *mtext_deserialize (MText *mt); + +#ifdef __cplusplus +} +#endif + +#endif /* _M17N_CORE_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-gui.c b/src/m17n-gui.c new file mode 100644 index 0000000..2c3c011 --- /dev/null +++ b/src/m17n-gui.c @@ -0,0 +1,423 @@ +/* m17n-gui.c -- body of the GUI API. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nGUI + @brief GUI support for a window system + + This section defines the m17n GUI API concerning M-text drawing + and inputting under a window system. + + All the definitions here are independent of window systems. An + actual library file, however, can depend on a specific window + system. For instance, the library file m17n-X.so is an example of + implementation of the m17n GUI API for the X Window System. + + Actually the GUI API is mainly for toolkit libraries or to + implement XOM, not for direct use from application programs. +*/ + +/***ja + @addtogroup m17nGUI + @brief ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à´ØÏ¢¤Î¿¸À¸ìÂбþ + + ¤³¤Î¥»¥¯¥·¥ç¥ó¤Ï¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¾å¤Ç¤Î M-text ¤Îɽ¼¨¤ÈÆþÎϤò°·¤¦ + m17n-win API ¤òÄêµÁ¤¹¤ë¡£ + + ¤³¤³¤Ç¤Î¤¹¤Ù¤Æ¤ÎÄêµÁ¤Ï¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤È¤ÏÆÈΩ¤Ç¤¢¤ë¡£¤·¤«¤·¼ÂÁõ + ¤Ï¸ÄÊ̤Υ¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¾ì¹ç¤¬¤¢¤ë¡£m17n-X ¥é¥¤¥Ö¥é¥ê + ¤Ï X ¥¦¥£¥ó¥É¥¦ÍѤμÂÁõÎã¤Ç¤¢¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include "config.h" + +#include +#include +#include + +#include "m17n-gui.h" +#include "m17n-misc.h" +#include "internal.h" +#include "internal-gui.h" +#include "font.h" +#include "fontset.h" +#include "face.h" + +static int win_initialized; + +static void +free_frame (void *object) +{ + MFrame *frame = (MFrame *) object; + + M17N_OBJECT_UNREF (frame->face); + mwin__close_device ((MFrame *) object); + free (object); +} + + +/* Internal API */ + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +void +m17n_init_win (void) +{ + int mdebug_mask = MDEBUG_INIT; + + if (win_initialized) + return; + m17n_init (); + if (merror_code < 0) + return; + + Mfont = msymbol ("font"); + Mfont_width = msymbol ("font-width"); + Mfont_ascent = msymbol ("font-ascent"); + Mfont_descent = msymbol ("font-descent"); + + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + if (mfont__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize font module.")); + if (mwin__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize win module.")); + if (mfont__fontset_init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize fontset module.")); + if (mface__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize face module.")); + if (mdraw__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize draw module.")); + if (minput__win_init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize input-win module.")); + mframe_default = NULL; + win_initialized = 1; + + err: + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the m17n GUI module.")); + MDEBUG_POP_TIME (); + return; +} + +void +m17n_fini_win (void) +{ + int mdebug_mask = MDEBUG_FINI; + + if (win_initialized) + { + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize input-gui module.")); + minput__win_fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize draw module.")); + mdraw__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize face module.")); + mface__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize fontset module.")); + mfont__fontset_fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize window module.")); + mwin__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize font module.")); + mfont__fini (); + mframe_default = NULL; + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize the gui modules.")); + MDEBUG_POP_TIME (); + win_initialized = 0; + } + m17n_fini (); +} + +/*** @addtogroup m17nFrame */ +/***en + @brief A @e frame is an object corresponding to the physical device. + + A @e frame is an object of the type #MFrame to hold various + information about each physical display/input device. Almost all + m17n GUI functions require a pointer to a frame as an + argument. */ + +/***ja + @brief ¥Õ¥ì¡¼¥à¤È¤ÏʪÍýŪ¥Ç¥Ð¥¤¥¹¤ËÂбþ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ë + + ¥Õ¥ì¡¼¥à¤È¤Ï #MFrame ·¿¤Î¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ê¡¢¸Ä¡¹¤ÎʪÍýŪ¤Êɽ¼¨¡¿ + ÆþÎϥǥХ¤¥¹¤Î¾ðÊó¤ò³ÊǼ¤¹¤ë¤¿¤á¤ËÍѤ¤¤é¤ì¤ë¡£¤Û¤È¤ó¤É¤¹¤Ù¤Æ¤Î + m17n-win API ¤Ï¡¢°ú¿ô¤È¤·¤Æ¥Õ¥ì¡¼¥à¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÍ׵᤹¤ë¡£ */ + +/*** @{ */ +/*=*/ + +/***en + @name Variables: Keys of frame property (common). + */ +/*** @{ */ +/*=*/ +MSymbol Mfont; +MSymbol Mfont_width; +MSymbol Mfont_ascent; +MSymbol Mfont_descent; + +/*=*/ +/*** @} */ +/*=*/ + +/***en + @brief Create a new frame. + + The mframe () function creates a new frame with parameters listed + in $PLIST. + + The recognized keys in $PLIST are window system dependent. + + The following key is always recognized. + +
    + +
  • #Mface, the value type must be (MFace *). + + The value is used as the default face of the frame. + +
+ + In addition, in the m17n-X library, the following keys are + recognized. They are to specify the root window and the depth of + drawables that can be used with the frame. + +
    + +
  • #Mdrawable, the value type must be Drawable + + A parameter of key #Mdisplay must also be specified. The + created frame can be used for drawables whose root window and + depth are the same as those of the specified drawable on the + specified display. + + When this parameter is specified, the parameter of key #Mscreen + is ignored. + +
  • #Mwidget, the value type must be Widget. + + The created frame can be used for drawables whose root window and + depth are the same as those of the specified widget. + + If a parameter of key #Mface is not specified, the default face + is created from the resources of the widget. + + When this parameter is specified, the parameters of key #Mdisplay, + #Mscreen, #Mdrawable, #Mdepth are ignored. + +
  • #Mdepth, the value type must be unsigned. + + The created frame can be used for drawables of the specified + depth. + +
  • #Mscreen, the value type must be (Screen *). + + The created frame can be used for drawables whose root window is + the same as the root window of the specified screen, and depth is + the same at the default depth of the screen. + + When this parameter is specified, parameter of key #Mdisplay is + ignored. + +
  • #Mdisplay, the value type must be (Display *). + + The created frame can be used for drawables whose root window is + the same as the root window for the default screen of the display, + and depth is the same as the default depth of the screen. + +
  • #Mcolormap, the value type must be (Colormap). + + The created frame uses the specified colormap. + +
+ + @return + If the operation was successful, mframe () returns a pointer to a + newly created frame. Otherwise, it returns @c NULL. */ + +/***ja + @brief ¿·¤·¤¤¥Õ¥ì¡¼¥à¤òºî¤ë + + ´Ø¿ô mframe () ¤Ï¿·¤·¤¤¥Õ¥ì¡¼¥à¤òºî¤ë¡£°ìÈ̤ˡ¢°ú¿ô $ARGC ¤È $ARGV + ¤Ï³Æ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î main () ´Ø¿ô¤ËÍ¿¤¨¤é¤ì¤ë¤â¤Î¤ÈƱ¤¸ + ¤â¤Î¤Ç¤¢¤ë¡£¤Ä¤Þ¤ê¥³¥Þ¥ó¥É¥é¥¤¥ó°ú¿ô¤ò´Þ¤ó¤Ç¤¤¤ëɬÍפ¬¤¢¤ë¡£»ÈÍÑ¤Ç + ¤­¤ë°ú¿ô¤Ï¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¡£ + + m17n-X ¥é¥¤¥Ö¥é¥ê¤Ë¤ª¤±¤ë¤³¤Î´Ø¿ô¤Ï¡¢XOpenDisplay (NULL) + ¤ò»È¤Ã¤Æ¥Ç¥Õ¥©¥ë¥È¤Î¥Ç¥£¥¹¥×¥ì¥¤¤ò³«¤­¡¢¼¡¤¤¤Ç DefaultScreen + (DISPLAY) ¤ò»È¤Ã¤Æ¥Ç¥Õ¥©¥ë¥È¤Î¥¹¥¯¥ê¡¼¥ó¤òÆÀ¤¿¸å¡¢ºî¤é¤ì¤¿¥Õ¥ì¡¼¥à + ¤Ë¤³¤Îξ¼Ô¤ò´ØÏ¢ÉÕ¤±¤ë¡£ + + m17n-X ¥é¥¤¥Ö¥é¥ê¤Ï°Ê²¼¤Î°ú¿ô¤ò¼õ¤±ÉÕ¤±¤ë¡£ + + @li @c -fn @e font : ¥Õ¥ì¡¼¥à¤Î¥Ç¥Õ¥©¥ë¥È¤Î¥Õ¥©¥ó¥È¤ò @e font + ¤Ë¥»¥Ã¥È¤¹¤ë + @li @c -fg @e color : ¥Õ¥ì¡¼¥à¤Î¥Ç¥Õ¥©¥ë¥È¤ÎÁ°·Ê¿§¤ò @e color + ¤Ë¥»¥Ã¥È¤¹¤ë + @li @c -bg @e color : ¥Õ¥ì¡¼¥à¤Î¥Ç¥Õ¥©¥ë¥È¤ÎÇØ·Ê¿§¤ò @e color + ¤Ë¥»¥Ã¥È¤¹¤ë + @li @c -rv : Á°·Ê¿§¤ÈÇØ·Ê¿§¤ò¸ò´¹¤¹¤ë + + @return + À®¸ù¤¹¤ì¤Ð mframe() ¤Ï¿·¤·¤¤¥Õ¥ì¡¼¥à¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤± + ¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ */ + +MFrame * +mframe (MPlist *plist) +{ + MFrame *frame; + MSymbol key; + + M17N_OBJECT (frame, free_frame, MERROR_FRAME); + frame->device = mwin__open_device (frame, plist); + if (! frame->device) + { + free (frame); + MERROR (MERROR_WIN, NULL); + } + + frame->face = mface_copy (mface__default); + if (plist) + for (; (key = mplist_key (plist)) != Mnil; plist = mplist_next (plist)) + if (key == Mface) + mface_merge (frame->face, (MFace *) mplist_value (plist)); + + frame->rface = mface__realize (frame, NULL, 0, Mnil, Mnil, 0); + if (! frame->rface->rfont) + MERROR (MERROR_WIN, NULL); + frame->space_width = frame->rface->space_width; + frame->ascent = frame->rface->ascent; + frame->descent = frame->rface->descent; + + if (! mframe_default) + mframe_default = frame; + + return frame; +} + +/*=*/ + +/***en + @brief Return property value of frame. + + The mframe_get_prop () function returns a value of property $KEY + of frame $FRAME. The valid keys and the corresponding return + values are as follows. + +@verbatim + + key type of value meaning of value + --- ------------- ---------------- + Mface MFace * The default face. + + Mfont MFont * The default font. + + Mfont_width int Width of the default font. + + Mfont_ascent int Ascent of the default font. + + Mfont_descent int Descent of the default font. + +@endverbatim + + In the m17n-X library, the followings are also accepted. + +@verbatim + + key type of value meaning of value + --- ------------- ---------------- + Mdisplay Display * Display associated with the frame. + + Mscreen int Screen number of a screen associated + with the frame. + + Mcolormap Colormap Colormap of the frame. + + Mdepth unsigned Depth of the frame. +@endverbatim +*/ + +/***ja + @brief ¥Õ¥ì¡¼¥à¤ÎMWDevice¤òÊÖ¤¹ + + ´Ø¿ô mframe_device () ¤Ï¥Õ¥ì¡¼¥à $FRAME ¤Ë³ÊǼ¤µ¤ì¤Æ¤¤¤ë @c + MWDevice ¹½Â¤ÂΤؤΥݥ¤¥¿¤òÊÖ¤¹¡£#MWDevice ¤Î·Á¼°¤Ï¥¦¥£¥ó¥É¥¦¥· + ¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¡£ */ + +void * +mframe_get_prop (MFrame *frame, MSymbol key) +{ + if (key == Mface) + return frame->face; + if (key == Mfont) + return &frame->rface->rfont->font; + if (key == Mfont_width) + return (void *) (frame->space_width); + if (key == Mfont_ascent) + return (void *) (frame->ascent); + if (key == Mfont_descent) + return (void *) (frame->descent); + return mwin__device_get_prop (frame->device, key); +} + +/*=*/ + +/***en + @brief The default frame. + + The external variable #mframe_default contains a pointer to the + default frame that is created by the first call of mframe (). */ + +/***ja + ¥Ç¥Õ¥©¥ë¥È¤Î¥Õ¥ì¡¼¥à + + ³°ÉôÊÑ¿ô #mframe_default ¤Ï¡¢¥Ç¥Õ¥©¥ë¥È¤Î¥Õ¥ì¡¼¥à¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò + »ý¤Ä¡£¥Ç¥Õ¥©¥ë¥È¤Î¥Õ¥ì¡¼¥à¤Ï¡¢ºÇ½é¤Ë mframe () ¤¬¸Æ¤Ó½Ð¤µ¤ì¤¿¤È¤­¤Ë + ºî¤é¤ì¤ë¡£ */ + +MFrame *mframe_default; + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-gui.h b/src/m17n-gui.h new file mode 100644 index 0000000..ba9adf6 --- /dev/null +++ b/src/m17n-gui.h @@ -0,0 +1,751 @@ +/* m17n-gui.h -- header file for the GUI API of the m17n library. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_GUI_H_ +#define _M17N_GUI_H_ + +#ifndef _M17N_H_ +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void m17n_init_win (void); +#undef M17N_INIT +#define M17N_INIT() m17n_init_win () + +extern void m17n_fini_win (void); +#undef M17N_FINI +#define M17N_FINI() m17n_fini_win () + +/***en @defgroup m17nGUI GUI API */ +/***ja @defgroup m17nGUI GUI API */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nFrame Frame */ +/***ja @defgroup m17nFrame ¥Õ¥ì¡¼¥à */ +/*=*/ + +/*** @ingroup m17nFrame */ +/***en + @brief Type of frames. + + The type #MFrame is for a @e frame object. Each frame holds + various information about the corresponding physical display/input + device. + + The internal structure of the type #MFrame is concealed from + application code, and its contents depend on the window system in + use. In the m17n-X library, it contains the information about @e + display and @e screen in the X Window System. */ + +/***ja + @brief ¥Õ¥ì¡¼¥àÍѹ½Â¤ÂÎ + + #MFrame ·¿¤Ï¡¢¥Õ¥ì¡¼¥à¥ª¥Ö¥¸¥§¥¯¥ÈÍѤι½Â¤ÂΤǤ¢¤ë¡£¸Ä¡¹¤Î¥Õ¥ì¡¼ + ¥à¤Ï¡¢¤½¤ì¤¬Âбþ¤¹¤ëʪÍý¥Ç¥Ð¥¤¥¹¤Î³Æ¼ï¾ðÊó¤òÊÝ»ý¤¹¤ë¡£ + + #MFrame ·¿¤ÎÆâÉô¹½Â¤¤Ï¡¢»ÈÍѤ¹¤ë¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤·¡¢¤Þ¤¿ + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£m17n-X ¥é¥¤¥Ö¥é¥ê¤Ë¤ª¤± + ¤ë¥Õ¥ì¡¼¥à¤Ï¡¢X ¥¦¥£¥ó¥É¥¦¤Î display ¤È screen ¤Ë´Ø¤¹¤ë¾ðÊó¤ò»ý¤Ä¡£ + */ + +typedef struct MFrame MFrame; + +/*=*/ + +extern MSymbol Mfont; +extern MSymbol Mfont_width; +extern MSymbol Mfont_ascent; +extern MSymbol Mfont_descent; +extern MFrame *mframe_default; + +extern MFrame *mframe (MPlist *plist); + +extern void *mframe_get_prop (MFrame *frame, MSymbol key); + +/* end of frame module */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nFont Font */ +/***ja @defgroup m17nFont ¥Õ¥©¥ó¥È */ +/*=*/ + +/*** @ingroup m17nFont */ +/***en + @brief Type of fonts. + + The type #MFont is the structure defining fonts. It contains + information about the following properties of a font: foundry, + family, weight, style, stretch, adstyle, registry, size, and + resolution. + + This structure is used both for specifying a font in a fontset + and for storing information about available system fonts. + + The internal structure is concealed from application code. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤Î¹½Â¤ + + #MFont ·¿¤Ï¥Õ¥©¥ó¥È»ØÄêÍѤι½Â¤ÂΤǤ¢¤ê¡¢¥Õ¥©¥ó¥È¤Î¥×¥í + ¥Ñ¥Æ¥£¤È¤·¤Æ family, weight, style, stretch, adstyle, registry, + size, resolution ¤ò»ý¤Ä¡£ + + ¤³¤Î¹½Â¤ÂΤϥե©¥ó¥È¥»¥Ã¥ÈÆâ¤Î¥Õ¥©¥ó¥È¤ò»ØÄꤹ¤ë¾ì¹ç¤È¡¢»ÈÍѲÄǽ¤Ê + ¥·¥¹¥Æ¥à¥Õ¥©¥ó¥È¤Î¾ðÊó¤ò³ÊǼ¤¹¤ë¾ì¹ç¤ÎξÊý¤ÇÍѤ¤¤é¤ì¤ë¡£ + + ÆâÉô¹½Â¤¤Ï¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ */ + +/*** + @seealso + mfont (), mfont_from_name (), mfont_find (). */ + +typedef struct MFont MFont; + +/*=*/ + +extern MSymbol Mfont; + +extern MPlist *mfont_freetype_path; + +extern MFont *mfont (); + +extern MFont *mfont_from_name (char *name); + +extern MFont *mfont_copy (MFont *font); + +extern char *mfont_name (MFont *font); + +extern MFont *mfont_from_spec (char *family, char *weight, char *slant, + char *swidth, char *adstyle, char *registry, + unsigned short point, unsigned short res); + +extern MSymbol Mfoundry; +extern MSymbol Mfamily; +extern MSymbol Mweight; +extern MSymbol Mstyle; +extern MSymbol Mstretch; +extern MSymbol Madstyle; +extern MSymbol Mregistry; +extern MSymbol Msize; +extern MSymbol Mresolution; + +extern void *mfont_get_prop (MFont *font, MSymbol key); + +extern int mfont_put_prop (MFont *font, MSymbol key, void *val); + +extern int mfont_set_encoding (MFont *font, + MSymbol encoding_name, MSymbol repertory_name); + + +/*=*/ + +/***en + @brief Find a font. + + The mfont_find () function returns a pointer to the available font + that matches best with the specification $SPEC in frame $FRAME. + + $SCORE, if not NULL, must point to a place to store the score + value which indicates how well the found font matches $SPEC. The + smaller score means a better match. + + $LIMITED_SIZE, if nonzero, forces the font selector to find a + font not greater than the #Msize property of $SPEC. */ + +/***ja + @brief ¥Õ¥©¥ó¥È¤òõ¤¹ + + ´Ø¿ô mfont_find () ¤Ï¡¢¥Õ¥ì¡¼¥à $FRAME ¾å¤Ç¥Õ¥©¥ó¥ÈÄêµÁ $SPEC ¤Ë¤â¤Ã + ¤È¤â¶á¤¤¥Õ¥©¥ó¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ */ + +extern MFont *mfont_find (MFrame *frame, MFont *spec, + int *score, int limited_size); + +extern MSymbol *mfont_selection_priority (); + +extern int mfont_set_selection_priority (MSymbol *keys); + +/* end of font module */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nFontset Fontset */ +/***ja @defgroup m17nFontset ¥Õ¥©¥ó¥È¥»¥Ã¥È */ +/*=*/ + +typedef struct MFontset MFontset; + +extern MFontset *mfontset (char *name); + +extern MSymbol mfontset_name (MFontset *fontset); + +extern MFontset *mfontset_copy (MFontset *fontset, char *name); + +extern int mfontset_modify_entry (MFontset *fontset, + MSymbol language, MSymbol script, + MSymbol charset, + MFont *spec, MSymbol layouter_name, + int how); + +/* end of fontset module */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nFace Face */ +/***ja @defgroup m17nFace ¥Õ¥§¡¼¥¹ */ +/*=*/ + +/*** @ingroup m17nFace */ +/***en + @brief Type of faces. + + The type #MFace is the structure of face objects. The internal + structure is concealed from application code. */ + +/***ja + @brief ¥Õ¥§¡¼¥¹Íѹ½Â¤ÂÎ + + #MFace ·¿¤Ï¥Õ¥§¡¼¥¹¥ª¥Ö¥¸¥§¥¯¥È¤Î¤¿¤á¤Î¹½Â¤ÂΤǤ¢¤ë¡£ÆâÉô¹½Â¤¤Ï + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ */ + +typedef struct MFace MFace; +/*=*/ + +extern MSymbol Mforeground; +extern MSymbol Mbackground; +extern MSymbol Mvideomode; +extern MSymbol Mnormal; +extern MSymbol Mreverse; +extern MSymbol Mhline; +extern MSymbol Mbox; +extern MSymbol Mfontset; +extern MSymbol Mratio; +extern MSymbol Mhook_func; +extern MSymbol Mhook_arg; + +/* Predefined faces. */ +extern MFace *mface_normal_video; +extern MFace *mface_reverse_video; +extern MFace *mface_underline; +extern MFace *mface_medium; +extern MFace *mface_bold; +extern MFace *mface_italic; +extern MFace *mface_bold_italic; +extern MFace *mface_xx_small; +extern MFace *mface_x_small; +extern MFace *mface_small; +extern MFace *mface_normalsize; +extern MFace *mface_large; +extern MFace *mface_x_large; +extern MFace *mface_xx_large; +extern MFace *mface_black; +extern MFace *mface_white; +extern MFace *mface_red; +extern MFace *mface_green; +extern MFace *mface_blue; +extern MFace *mface_cyan; +extern MFace *mface_yellow; +extern MFace *mface_magenta; + +/* etc */ +extern MSymbol Mface; + +extern MFace *mface (); + +extern MFace *mface_copy (MFace *face); + +extern MFace *mface_merge (MFace *dst, MFace *src); + +extern MFace *mface_from_font (MFont *font); + +/*=*/ + +/*** @ingroup m17nFace */ +/***en + @brief Type of horizontal line spec of face. + + The type #MFaceHLineProp is to specify the detail of #Mhline + property of a face. The value of the property must be a pointer + to an object of this type. */ + +typedef struct +{ + /***en Type of the horizontal line. */ + enum MFaceHLineType + { + MFACE_HLINE_BOTTOM, + MFACE_HLINE_UNDER, + MFACE_HLINE_STRIKE_THROUGH, + MFACE_HLINE_OVER, + MFACE_HLINE_TOP + } type; + + /***en Width of the line in pixels. */ + unsigned width; + + /***en Color of the line. If the value is Mnil, foreground color of + a merged face is used. */ + MSymbol color; +} MFaceHLineProp; +/*=*/ + +/*** @ingroup m17nFace */ +/***en + @brief Type of box spec of face. + + The type #MFaceBoxProp is to specify the detail of #Mbox property + of a face. The value of the property must be a pointer to an + object of this type. */ + +typedef struct +{ + /***en Width of the box line in pixels. */ + unsigned width; + + MSymbol color_top; + MSymbol color_bottom; + MSymbol color_left; + MSymbol color_right; + + unsigned inner_hmargin; + unsigned inner_vmargin; + unsigned outer_hmargin; + unsigned outer_vmargin; + +} MFaceBoxProp; +/*=*/ + +/*** @ingroup m17nFace */ +/***en + @brief Type of hook function of face. + + The type #MFaceHookFunc is to specify the #Mhook property of a + face. The value of the property must be function of this + type. */ +typedef void *(*MFaceHookFunc) (MFace *face, void *arg, void *info); +/*=*/ + +extern void *mface_get_prop (MFace *face, MSymbol key); + +extern int mface_put_prop (MFace *face, MSymbol key, void *val); + +extern void mface_update (MFrame *frame, MFace *face); + +/* end of face module */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nDraw Drawing */ +/***ja @defgroup m17nDraw ɽ¼¨ */ +/*=*/ + +/*** @ingroup m17nDraw */ +/***en + @brief Window system dependent type for a window. + + The type MDrawWindow is for a window; a rectangular area that + works in several ways like a miniature screen. + + What it actually points depends on a window system. A program + that uses the m17n-X library must coerce the type @c Drawable to + this type. */ + +/***ja ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¡¢¥¦¥£¥ó¥É¥¦¤òɽ¤¹¥ª¥Ö¥¸¥§¥¯¥ÈÍѤη¿¡£ + + m17n X ¥é¥¤¥Ö¥é¥ê¤Ç¤Ï¡¢@c Window ·¿¤ÈƱ¤¸. */ + +typedef void *MDrawWindow; +/*=*/ + +/*** @ingroup m17nDraw */ +/***en + @brief Window system dependent type for a region. + + The type MDrawRegion is for a region; an arbitrary set of pixels + on the screen (typically a rectangular area). + + What it actually points depends on a window system. A program + that uses the m17n-X library must coerce the type @c Region to + this type. */ + +typedef void *MDrawRegion; +/*=*/ + +/*** @ingroup m17nDraw */ +/***en + @brief Type of a text drawing control. + + The type #MDrawControl is the structure that controls how to draw + an M-text. */ + +typedef struct +{ + /***en If nonzero, draw an M-text as image, i.e. with background + filled with background colors of faces put on the M-text. + Otherwise, the background is not changed. */ + unsigned as_image : 1; + + /***en If nonzero and the first glyph of each line has negative + lbearing, shift glyphs horizontally to right so that no pixel is + drawn to the left of the specified position. */ + unsigned align_head : 1; + + /***en If nonzero, draw an M-text two-dimensionally, i.e., newlines + in M-text breaks lines and the following characters are drawn in + the next line. If is non-NULL, and the function + returns nonzero line width, a line longer than that width is + also broken. */ + unsigned two_dimensional : 1; + + /***en If nonzero, draw an M-text to the right of a specified + position. */ + unsigned orientation_reversed : 1; + + /***en If nonzero, reorder glyphs correctly for bidi text. */ + unsigned enable_bidi : 1; + + /***en If nonzero, don't draw characters whose general category (in + Unicode) is Cf (Other, format). */ + unsigned ignore_formatting_char : 1; + + /***en If nonzero, draw glyphs suitable for a terminal. Not yet + implemented. */ + unsigned fixed_width : 1; + + /***en If nonzero, the values are minimum line ascent and descent + pixels. */ + unsigned int min_line_ascent; + unsigned int min_line_descent; + + /***en If nonzero, the values are maximum line ascent and descent + pixels. */ + unsigned int max_line_ascent; + unsigned int max_line_descent; + + /***en If nonzero, the value specifies how many pixels each line can + occupy on the display. The value zero means that there is no + limit. It is ignored if is non-NULL. */ + unsigned int max_line_width; + + /***en If nonzero, the value specifies the distance between tab + stops in columns (the width of one column is the width of a + space in the default font of the frame). The value zero means + 8. */ + unsigned int tab_width; + + /***en If non-NULL, the value is a function that calculates the + indentation and width limit of each line based on the line + number LINE and the coordinate Y. The function store the + indentation and width limit at the place pointed by INDENT and + WIDTH respectively. + + The indentation specifies how many pixels the first glyph of + each line is shifted to the right (if the member + is zero) or to the left (otherwise). If + the value is negative, each line is shifted to the reverse + direction. + + The width limit specifies how many pixels each line can occupy + on the display. The value 0 means that there is no limit. + + LINE and Y are reset to 0 when a line is broken by a newline + character, and incremented each time when a long line is broken + because of the width limit. + + This has an effect only when is nonzero. */ + void (*format) (int line, int y, int *indent, int *width); + + /***en If non-NULL, the value is a function that calculates a line + breaking position when a line is too long to fit within the + width limit. POS is a position of the character next to the + last one that fits within the limit. FROM is a position of the + first character of the line, and TO is a position of the last + character displayed on the line if there were not width limit. + LINE and Y are the same as the arguments to . + + The function must return a character position to break the + line. + + The function should not modify MT. + + The mdraw_default_line_break () function is useful for such a + script that uses SPACE as a word separator. */ + int (*line_break) (MText *mt, int pos, int from, int to, int line, int y); + + int with_cursor; + + /***en Specifies the character position to display a cursor. If it + is greater than the maximum character position, the cursor is + displayed next to the last character of an M-text. If the value + is negative, even if is nonzero, cursor is not + displayed. */ + int cursor_pos; + + /***en If nonzero, display a cursor at the character position + . If the value is positive, it is the pixel width + of the cursor. If the value is negative, the cursor width is + the same as the underlining glyph(s). */ + int cursor_width; + + /***en If nonzero and is also nonzero, display double + bar cursors; at the character position and at the + logically previous character. Both cursors have one pixel width + with horizontal fringes at upper or lower positions. HOW TO + EXPLAIN THE DOUBLE CURSORS? */ + int cursor_bidi; + + /***en If nonzero, on drawing partial text, pixels of surrounding + texts that intrude into the drawing area are also drawn. For + instance, some CVC sequence of Thai text (C is consonant, V is + upper vowel) is drawn so that V is placed over the middle of two + Cs. If this CVC sequence is already drawn and only the last C + is drawn again (for instance by updating cursor position), the + left half of V is erased if this member is zero. By setting + this member to nonzero, even with such a drawing, we can keep + this CVC sequence correctly displayed. */ + int partial_update; + + /***en If nonzero, don't cache the result of any drawing information + of an M-text. */ + int disable_caching; + + /* If non-NULL, limit the drawing effect to the specified region. */ + MDrawRegion clip_region; + +} MDrawControl; + +/*=*/ + +/*** @ingroup m17nDraw */ +/***en + @brief Type of metric for gylphs and texts. + + The type #MDrawMetric is for a metric of a glyph and a drawn text. + It is also used to represent a rectangle area of a graphic + device. */ + +typedef struct { + int x, y; + unsigned int width, height; +} MDrawMetric; + +/*=*/ + +/*** @ingroup m17nDraw */ +/***en + @brief Type of information about a glyph. + + The type #MDrawGlyphInfo is the structure that contains + information about a glyph. It is used by mdraw_glyph_info (). */ + +typedef struct +{ + /***en Character range corresponding to the glyph. */ + int from, to; + + /***en Character ranges corresponding to the line of the glyph. */ + int line_from, line_to; + + /***en X/Y coordinates of the glyph. */ + int x, y; + + /***en Metric of the glyph. */ + MDrawMetric this; + + /***en Font used for the glyph. Set to NULL if no font is found for + the glyph. */ + MFont *font; + + /***en Character ranges corresponding to logically previous and next + glyphs. Note that we do not need the members prev_to and + next_from because they must be the same as the memberse from and + to respectively. */ + int prev_from, next_to; + + /***en Character ranges corresponding to visually left and right + glyphs. */ + int left_from, left_to; + int right_from, right_to; + +} MDrawGlyphInfo; + +/*=*/ + +/***en + @brief Type of text items. + + The type #MDrawTextItem is for @e textitem objects. + Each textitem contains an M-text and some other information to + control the drawing of the M-text. */ + +/***ja + @brief textitem Íѹ½Â¤ÂÎ + + ·¿ #MDrawTextItem ¤Ï @e ¥Æ¥­¥¹¥È¥¢¥¤¥Æ¥à ¥ª¥Ö¥¸¥§¥¯¥ÈÍѤι½Â¤ÂΤǤ¢ + ¤ë¡£³Æ¥Æ¥­¥¹¥È¥¢¥¤¥Æ¥à¤Ï¡¢1¸Ä¤Î M-text ¤È¡¢¤½¤Îɽ¼¨¤òÀ©¸æ¤¹¤ë¤¿¤á + ¤Î³Æ¼ï¾ðÊó¤ò´Þ¤ó¤Ç¤¤¤ë¡£ + + @latexonly \IPAlabel{MTextItem} @endlatexonly */ + +typedef struct +{ + /***en M-text. */ + /***ja M-text */ + MText *mt; + + /***en Optional change in the position (in the unit of pixel) along + the X-axis before the M-text is drawn. */ + /***ja ÉÁ²èÁ°¤Ë¹Ô¤Ê¤¦X¼´Êý¸þ¤Î°ÌÃÖÄ´À° (¥Ô¥¯¥»¥ëñ°Ì) */ + int delta; + + /***en Pointer to a face object. Each property of the face, if not + Mnil, overrides the same property of face(s) specified as a text + property in . */ + /***ja ¥Õ¥©¥ó¥È¥»¥Ã¥È¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¡£¤³¤ì¤Ï M-text Æâ¤Ç»Ø + Äꤵ¤ì¤¿¥Õ¥§¡¼¥¹¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤ËÍ¥À褹¤ë*/ + MFace *face; + + /***en Pointer to a draw control object. The M-text is drawn + by mdraw_text_with_control () with this control object. */ + MDrawControl *control; + +} MDrawTextItem; + +/*=*/ + +extern int mdraw_text (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to); + +extern int mdraw_image_text (MFrame *frame, MDrawWindow win, int x, int y, + MText *mt, int from, int to); + +extern int mdraw_text_with_control (MFrame *frame, MDrawWindow win, + int x, int y, MText *mt, int from, int to, + MDrawControl *control); + +extern int mdraw_coordinates_position (MFrame *frame, + MText *mt, int from, int to, + int x, int y, MDrawControl *control); + +extern int mdraw_text_extents (MFrame *frame, + MText *mt, int from, int to, + MDrawControl *control, + MDrawMetric *overall_ink_return, + MDrawMetric *overall_logical_return, + MDrawMetric *overall_line_return); + +extern int mdraw_text_per_char_extents (MFrame *frame, + MText *mt, int from, int to, + MDrawControl *control, + MDrawMetric *ink_array_return, + MDrawMetric *logical_array_return, + int array_size, + int *num_chars_return, + MDrawMetric *overall_ink_return, + MDrawMetric *overall_logical_return); + +extern int mdraw_glyph_info (MFrame *frame, MText *mt, int from, int pos, + MDrawControl *control, MDrawGlyphInfo *info); + +extern void mdraw_text_items (MFrame *frame, MDrawWindow win, int x, int y, + MDrawTextItem *items, int nitems); + +extern void mdraw_per_char_extents (MFrame *frame, MText *mt, + MDrawMetric *array_return, + MDrawMetric *overall_return); + +extern int mdraw_default_line_break (MText *mt, int pos, + int from, int to, int line, int y); + +extern void mdraw_clear_cache (MText *mt); + +/* end of drawing module */ +/*=*/ + +/*** @ingroup m17nGUI */ +/***en @defgroup m17nInputMethodWin Input Method (GUI) */ +/***ja @defgroup m17nInputMethodWin ÆþÎϥ᥽¥Ã¥É (GUI) */ +/*=*/ + +extern MInputDriver minput_gui_driver; + +/*=*/ +/*** @ingroup m17nInputMethodWin */ +/***en + @brief Type of the argument to the function minput_create_ic (). + + The type #MInputGUIArgIC is for the argument $ARG of the function + minput_create_ic () to create an input context of an internal + input method. */ + +/***ja + @brief ´Ø¿ô minput_create_ic () ¤Î°ú¿ô $ARG ¤Ç»Ø¤µ¤ì¤ë¹½Â¤ÂÎ + + #MInputGUIArgIC ·¿¤Ï¡¢´Ø¿ô minput_create_ic () ¤¬ÆâÉôÆþÎϥ᥽¥Ã + ¥É¤òÀ¸À®¤¹¤ëºÝ¤Ë¡¢°ú¿ô $ARG ¤Ë¤è¤Ã¤Æ»Ø¤µ¤ì¤ë¹½Â¤ÂΤǤ¢¤ë¡£ */ + +typedef struct +{ + /***en Frame of the client. */ + /***ja ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Õ¥ì¡¼¥à */ + MFrame *frame; + + /***en Window on which to display the preedit and status text. */ + /***ja preedit ¥Æ¥­¥¹¥È¤È status ¥Æ¥­¥¹¥È¤òɽ¼¨¤¹¤ë¥¦¥£¥ó¥É¥¦ */ + MDrawWindow client; + + /***en Window that the input context has a focus on. */ + /***ja ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤¬¥Õ¥©¡¼¥«¥¹¤ò¤ª¤¤¤Æ¤¤¤ë¥¦¥£¥ó¥É¥¦ */ + MDrawWindow focus; +} MInputGUIArgIC; + +/*=*/ + +extern MSymbol minput_event_to_key (MFrame *frame, void *event); + +/* end of input module */ +/*=*/ +/* end of window modules */ +/*=*/ + +extern MFace *mdebug_dump_face (MFace *face, int indent); +extern MFont *mdebug_dump_font (MFont *font); +extern MFontset *mdebug_dump_fontset (MFontset *fontset, int indent); + +#ifdef __cplusplus +} +#endif + +#endif /* _M17N_GUI_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n-misc.h b/src/m17n-misc.h new file mode 100644 index 0000000..8e6d7a3 --- /dev/null +++ b/src/m17n-misc.h @@ -0,0 +1,123 @@ +/* m17n-misc.h -- header file for the MISC API. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_ERR_H_ +#define _M17N_ERR_H_ + +/*** @defgroup m17nMisc MISC API */ +/*=*/ +/*** @ingroup m17nMisc */ +/***en @defgroup m17nError Error Handling */ +/***ja @defgroup m17nError ¥¨¥é¡¼½èÍý */ +/*=*/ + +/*** @ingroup m17nError */ +/***en + @brief Enumeration for error code of the m17n library. + + Enumeration for error code of the m17n library. + + When a library function is called with an invalid argument, it + sets the external variable @e merror_code to one of these values. + All the error codes are positive integers. + + When a memory allocation error happens, the function pointed to by + the external variable #m17n_memory_full_handler is called with one + of these values as an argument. */ + +/***ja + @brief m17n ¥é¥¤¥Ö¥é¥ê¥¨¥é¡¼¥³¡¼¥É¤ÎÎóµó + + m17n ¥é¥¤¥Ö¥é¥ê¥¨¥é¡¼¥³¡¼¥É¤ÎÎóµó + + ¥é¥¤¥Ö¥é¥ê¤Î´Ø¿ô¤¬ÂÅÅö¤Ç¤Ê¤¤°ú¿ô¤È¤È¤â¤Ë¸Æ¤Ð¤ì¤¿¾ì¹ç¤Ë¤Ï¡¢ÊÑ¿ô @c + merror_code ¤ò¤³¤ì¤é¤ÎÃͤΤɤ줫¤Ë¥»¥Ã¥È¤¹¤ë¡£¤¹¤Ù¤Æ¤Î¥¨¥é¡¼¥³¡¼¥É + ¤ÏÀµ¤ÎÀ°¿ô¤Ç¤¢¤ë¡£ + + ¥á¥â¥ê³äÅö¤Æ¥¨¥é¡¼¤ÎºÝ¤Ë¤Ï¡¢³°ÉôÊÑ¿ô #m17n_memory_full_handler ¤Î»Ø + ¤¹´Ø¿ô¤¬¡¢¤³¤ì¤é¤ÎÃͤΤ¦¤Á¤Î¤É¤ì¤«¤ò°ú¿ô¤È¤·¤Æ¸Æ¤Ð¤ì¤ë¡£ + */ + +enum MErrorCode + { + MERROR_NONE, + MERROR_OBJECT, + MERROR_SYMBOL, + MERROR_MTEXT, + MERROR_TEXTPROP, + MERROR_CHAR, + MERROR_CHARTABLE, + MERROR_CHARSET, + MERROR_CODING, + MERROR_RANGE, + MERROR_LANGUAGE, + MERROR_LOCALE, + MERROR_PLIST, + MERROR_MISC, + MERROR_WIN, + MERROR_X, + MERROR_FRAME, + MERROR_FACE, + MERROR_DRAW, + MERROR_FONT, + MERROR_FONTSET, + MERROR_FONT_OTF, + MERROR_FONT_FT, + MERROR_IM, + MERROR_DB, + MERROR_IO, + MERROR_DEBUG, + MERROR_MEMORY, + MERROR_MAX + }; + +/*=*/ + +extern enum MErrorCode merror_code; + +extern void (*m17n_memory_full_handler) (enum MErrorCode err); + +/*=*/ +/*** @ingroup m17nMisc */ +/***en @defgroup m17nDebug Debugging */ +/***ja @defgroup m17nDebug ¥Ç¥Ð¥Ã¥°¥µ¥Ý¡¼¥È */ +/*=*/ + +extern int mdebug_hook (void); + +extern MSymbol mdebug_dump_symbol (MSymbol sym, int indent); +extern MSymbol mdebug_dump_all_symbols (int indent); +extern MPlist *mdebug_dump_plist (MPlist *plist, int indent); +extern MText *mdebug_dump_mtext (MText *mt, int fullp, int indent); +extern MCharTable *mdebug_dump_chartab (MCharTable *table, int indent); + +#ifdef DOXYGEN_INTERNAL_MODULE +/***en @defgroup m17nInternal Internal */ +/***ja @defgroup m17nInternal Internal */ +#endif +#endif /* _M17N_ERR_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n.c b/src/m17n.c new file mode 100644 index 0000000..0b658d0 --- /dev/null +++ b/src/m17n.c @@ -0,0 +1,122 @@ +/* m17n.c -- body of the SHELL API. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "charset.h" +#include "coding.h" + +static int shell_initialized; + + +/* Internal API */ + + +/* External API */ + +void +m17n_init (void) +{ + int mdebug_mask = MDEBUG_INIT; + + if (shell_initialized) + return; + m17n_init_core (); + if (merror_code < 0) + return; + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + if (mcharset__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize charset module.")); + if (mcoding__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize conv module.")); + if (mdatabase__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize database module.")); + if (mcharset__load_from_database () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to load charset definitions.")); + if (mcoding__load_from_database () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to load coding definitions.")); + if (mchar__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize character module.")); + if (mlang__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize language module")); + if (mlocale__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize locale module.")); + if (minput__init () < 0) + goto err; + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize input module.")); + shell_initialized = 1; + + err: + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the shell modules.")); + MDEBUG_POP_TIME (); +} + +void +m17n_fini (void) +{ + int mdebug_mask = MDEBUG_FINI; + + if (shell_initialized) + { + MDEBUG_PUSH_TIME (); + MDEBUG_PUSH_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize input module.")); + minput__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize locale module.")); + mlocale__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize language module.")); + mlang__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize character module.")); + mchar__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize database module.")); + mdatabase__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize coding module.")); + mcoding__fini (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize charset module.")); + mcharset__fini (); + MDEBUG_POP_TIME (); + MDEBUG_PRINT_TIME ("INIT", (stderr, " to finalize the shell modules.")); + MDEBUG_POP_TIME (); + shell_initialized = 0; + } + m17n_fini_core (); +} + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/m17n.h b/src/m17n.h new file mode 100644 index 0000000..c0e6c8f --- /dev/null +++ b/src/m17n.h @@ -0,0 +1,1257 @@ +/* m17n.h -- header file for the SHELL API of the m17n library. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_H_ +#define _M17N_H_ + +#include +#include +#include + +#ifndef _M17N_CORE_H_ +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +extern void m17n_init (void); +#undef M17N_INIT +#define M17N_INIT() m17n_init () + +extern void m17n_fini (void); +#undef M17N_FINI +#define M17N_FINI() m17n_fini () + +/***en @defgroup m17nShell SHELL API */ +/***ja @defgroup m17nShell SHELL API */ +/*=*/ + +/* + * (11) Functions related to the m17n database + */ + +/*** @ingroup m17nShell */ +/***en @defgroup m17nDatabase Database */ +/***ja @defgroup m17nDatabase ¸À¸ì¾ðÊó¥Ç¡¼¥¿¥Ù¡¼¥¹ */ +/*=*/ +/* Directory of an application specific databases. */ +extern char *mdatabase_dir; + +/*** + @ingroup m17nDatabase */ +/***en + @brief Type of database. + + The type #MDatabase is for a database object. Its internal + structure is concealed from application programs. */ +/***ja ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î·¿Àë¸À */ +typedef struct MDatabase MDatabase; + +/*=*/ + +/* Look for a data. */ +extern MDatabase *mdatabase_find (MSymbol tag1, MSymbol tag2, + MSymbol tag3, MSymbol tag4); + +extern MPlist *mdatabase_list (MSymbol tag0, MSymbol tag1, + MSymbol tag2, MSymbol tag3); + +/* Load a data. */ +void *mdatabase_load (MDatabase *mdb); + +/* Get tags of a data. */ +extern MSymbol *mdatabase_tag (MDatabase *mdb); + +/* Define a data. */ +extern MDatabase *mdatabase_define (MSymbol tag1, MSymbol tag2, + MSymbol tag3, MSymbol tag4, + void *(*loader) (MSymbol *, void *), + void *extra_info); + +/*=*/ +/* (S2) Charset staffs */ + +/*** @ingroup m17nShell */ +/***en @defgroup m17nCharset Charset */ +/***ja @defgroup m17nCharset ʸ»ú¥»¥Ã¥È */ +/*=*/ +#define MCHAR_INVALID_CODE 0xFFFFFFFF + +/* Predefined charsets */ +extern MSymbol Mcharset_ascii; +extern MSymbol Mcharset_iso_8859_1; +extern MSymbol Mcharset_unicode; +extern MSymbol Mcharset_m17n; +extern MSymbol Mcharset_binary; + +/* Predefined keys for mchar_define_charset (). */ +extern MSymbol Mmethod; +extern MSymbol Mdimension; +extern MSymbol Mmin_range; +extern MSymbol Mmax_range; +extern MSymbol Mmin_code; +extern MSymbol Mmax_code; +extern MSymbol Mascii_compatible; +extern MSymbol Mfinal_byte; +extern MSymbol Mrevision; +extern MSymbol Mmin_char; +extern MSymbol Mmapfile; +extern MSymbol Mparents; +extern MSymbol Msubset_offset; +extern MSymbol Mdefine_coding; +extern MSymbol Maliases; + +/* Methods of a charset. */ +extern MSymbol Moffset; +extern MSymbol Mmap; +extern MSymbol Munify; +extern MSymbol Msubset; +extern MSymbol Msuperset; + +/* etc. */ +extern MSymbol Mcharset; + +extern MSymbol mchar_define_charset (char *name, MPlist *plist); + +extern MSymbol mchar_resolve_charset (MSymbol symbol); + +extern int mchar_list_charset (MSymbol **symbols); + +extern int mchar_decode (MSymbol charset_name, unsigned code); + +extern unsigned mchar_encode (MSymbol charset_name, int c); + +extern int mchar_map_charset (MSymbol charset_name, + void (*func) (int from, int to, void *arg), + void *func_arg); + +/*=*/ + +/* (S3) code conversion */ + +/*** @ingroup m17nShell */ +/***en @defgroup m17nConv Code Conversion */ +/***ja @defgroup m17nConv ¥³¡¼¥ÉÊÑ´¹ */ +/*=*/ + +/* Predefined coding systems */ +extern MSymbol Mcoding_us_ascii; +extern MSymbol Mcoding_iso_8859_1; +extern MSymbol Mcoding_utf_8; +extern MSymbol Mcoding_utf_8_full; +extern MSymbol Mcoding_utf_16; +extern MSymbol Mcoding_utf_16be; +extern MSymbol Mcoding_utf_16le; +extern MSymbol Mcoding_utf_32; +extern MSymbol Mcoding_utf_32be; +extern MSymbol Mcoding_utf_32le; +extern MSymbol Mcoding_sjis; + +/* Parameter keys for mconv_define_coding (). */ +extern MSymbol Mtype; +extern MSymbol Mcharsets; +extern MSymbol Mflags; +extern MSymbol Mdesignation; +extern MSymbol Minvocation; +extern MSymbol Mcode_unit; +extern MSymbol Mbom; +extern MSymbol Mlittle_endian; + +/* Symbols representing coding system type. */ +extern MSymbol Mutf; +extern MSymbol Miso_2022; + +/* Symbols appearing in the value of Mfrag parameter. */ +extern MSymbol Mreset_at_eol; +extern MSymbol Mreset_at_cntl; +extern MSymbol Meight_bit; +extern MSymbol Mlong_form; +extern MSymbol Mdesignation_g0; +extern MSymbol Mdesignation_g1; +extern MSymbol Mdesignation_ctext; +extern MSymbol Mdesignation_ctext_ext; +extern MSymbol Mlocking_shift; +extern MSymbol Msingle_shift; +extern MSymbol Msingle_shift_7; +extern MSymbol Meuc_tw_shift; +extern MSymbol Miso_6429; +extern MSymbol Mrevision_number; +extern MSymbol Mfull_support; + +/* etc */ +extern MSymbol Mcoding; +extern MSymbol Mmaybe; + +/*** @ingroup m17nConv */ +/***en + @brief Codes that represent the result of code conversion. + + One of these values is set in @c MConverter-\>result. */ + +/***ja + @brief ¥³¡¼¥ÉÊÑ´¹¤Î·ë²Ì¤ò¼¨¤¹¥³¡¼¥É + + ¤³¤ì¤é¤ÎÃͤΤ¦¤Á°ì¤Ä¤¬ @c MConverter-\>result ¤ËÀßÄꤵ¤ì¤ë¡£ */ + +enum MConversionResult + { + /***en Code conversion is successful. */ + /***ja ¥³¡¼¥ÉÊÑ´¹¤ÏÀ®¸ù¡£ */ + MCONVERSION_RESULT_SUCCESS, + + /***en On decoding, the source contains an invalid byte. */ + /***ja ¥Ç¥³¡¼¥É¤ÎºÝ¡¢¥½¡¼¥¹¤ËÉÔÀµ¤Ê¥Ð¥¤¥È¤¬´Þ¤Þ¤ì¤ë¡£ */ + MCONVERSION_RESULT_INVALID_BYTE, + + /***en On encoding, the source contains a character that cannot be + encoded by the specified coding system. */ + + /***ja ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢¥½¡¼¥¹¤Ë»ØÄê¤Î¥³¡¼¥É·Ï¤Ç + ¥¨¥ó¥³¡¼¥É¤Ç¤­¤Ê¤¤Ê¸»ú¤¬´Þ¤Þ¤ì¤ë¡£ */ + MCONVERSION_RESULT_INVALID_CHAR, + + /***en On decoding, the source ends with an incomplete byte sequence. */ + /***ja ¥Ç¥³¡¼¥É¤ÎºÝ¡¢¥½¡¼¥¹¤¬ÉÔ´°Á´¤Ê¥Ð¥¤¥ÈÎó¤Ç½ª¤ï¤ë¡£*/ + MCONVERSION_RESULT_INSUFFICIENT_SRC, + + /***en On encoding, the destination is too short to store the result. */ + /***ja ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢·ë²Ì¤ò³ÊǼ¤¹¤ëÎΰ褬û¤«¤¹¤®¤ë¡£ */ + MCONVERSION_RESULT_INSUFFICIENT_DST, + + /***en An I/O error occurred in the conversion. */ + /***ja ¥³¡¼¥ÉÊÑ´¹Ãæ¤Ë I/O ¥¨¥é¡¼¤¬µ¯¤³¤Ã¤¿¡£ */ + MCONVERSION_RESULT_IO_ERROR + }; +/*=*/ + +/*** @ingroup m17nConv */ +/***en + @brief Structure to be used in code conversion. + + The first three members are to control the conversion. */ + +/***ja + @brief ¥³¡¼¥ÉÊÑ´¹¤ËÍѤ¤¤é¤ì¤ë¹½Â¤ÂÎ + + @latexonly \IPAlabel{MConverter} @endlatexonly +*/ + +typedef struct +{ + /***en + Set the value to nonzero if the conversion should be lenient. + By default, the conversion is strict (i.e. not lenient). + + If the conversion is strict, the converter stops at the first + invalid byte (on decoding) or at the first character not + supported by the coding system (on encoding). If this happens, + @c MConverter-\>result is set to @c + MCONVERSION_RESULT_INVALID_BYTE or @c + MCONVERSION_RESULT_INVALID_CHAR accordingly. + + If the conversion is lenient, on decoding, an invalid byte is + kept per se, and on encoding, an invalid character is replaced + with "" (if the character is a Unicode character) or + with "" (otherwise). */ + + /***ja + ¸·Ì©¤ÊÊÑ´¹¤¬É¬ÍפǤʤ¤¾ì¹ç¤Ë¤³¤Î¥Õ¥é¥°¥Ó¥Ã¥È¤ò¤¿¤Æ¤ë¡£¥Ç¥Õ¥© + ¥ë¥È¤Ç¤Ï¡¢ÊÑ´¹¤Ï¸·Ì©¤Ç¤¢¤ë¡£ + + ÊÑ´¹¤¬¸·Ì©¤È¤Ï¡¢¥Ç¥³¡¼¥É¤ÎºÝ¤Ë¤ÏºÇ½é¤ÎÉÔÀµ¤Ê¥Ð¥¤¥È¤Ç¥³¥ó¥Ð¡¼¥¿ + ¤¬»ß¤Þ¤ë¤³¤È¡¢¥¨¥ó¥³¡¼¥É¤ÎºÝ¤Ë¤ÏÊÑ´¹¤µ¤ì¤ë¥³¡¼¥É·Ï¤Ç¥µ¥Ý¡¼¥È¤µ + ¤ì¤Ê¤¤ºÇ½é¤Îʸ»ú¤Ç¥³¥ó¥Ð¡¼¥¿¤¬»ß¤Þ¤ë¤³¤È¤ò»Ø¤¹¡£¤³¤ì¤é¤Î¾ì¹ç¡¢ + @c MConverter-\>result ¤Ï¤½¤ì¤¾¤ì@c + MCONVERSION_RESULT_INVALID_BYTE ¤«@c + MCONVERSION_RESULT_INVALID_CHAR ¤È¤Ê¤ë¡£ + + ÊÑ´¹¤¬¸·Ì©¤Ç¤Ê¤¤¾ì¹ç¤Ë¤Ï¡¢¥Ç¥³¡¼¥É¤ÎºÝ¤ÎÉÔÀµ¤Ê¥Ð¥¤¥È¤Ï¤½¤Î¥Ð¥¤ + ¥È¤Î¤Þ¤Þ»Ä¤ë¡£¤Þ¤¿¥¨¥ó¥³¡¼¥É¤ÎºÝ¤Ë¤Ï¡¢ÉÔÀµ¤Êʸ»ú¤Ï¥³¡¼¥É·Ï¤´¤È + ¤ËÄê¤á¤é¤ì¤¿¥Ç¥Õ¥©¥ë¥È¤Îʸ»ú¤ÈÃÖ¤­´¹¤¨¤é¤ì¤ë¡£ */ + + int lenient; + + /***en + Set the value to nonzero before decoding or encoding the last + block of the byte sequence or the character sequence + respectively. The value influences the conversion as below. + + On decoding, in the case that the last few bytes are too short + to form a valid byte sequence: + + If the value is nonzero, the conversion terminates by error + (MCONVERSION_RESULT_INVALID_BYTE) at the first byte of the + sequence. + + If the value is zero, the conversion terminates successfully. + Those bytes are stored in the converter as carryover and are + prepended to the byte sequence of the further conversion. + + On encoding, in the case that the coding system is context + dependent: + + If the value is nonzero, the conversion may produce a byte + sequence at the end to reset the context to the initial state + even if the source characters are zero. + + If the value is zero, the conversion never produce such a byte + sequence at the end. */ + + /***ja + ʸ»ú¥³¡¼¥ÉÎó¤Î½ªÃ¼Éôʬ¤ò¥¨¥ó¥³¡¼¥É¤¹¤ëºÝ¤Ë¤Ï¡¢¤³¤Î¥Õ¥é¥°¤òΩ¤Æ + ¤ë¡£¤³¤Î¾ì¹ç½ÐÎÏ¥³¡¼¥É¥Ý¥¤¥ó¥ÈÎó¤Î¥³¥ó¥Æ¥¯¥¹¥È¤ò¸µ¤ËÌ᤹¤¿¤á¤Î + ¿ô¥Ð¥¤¥È¤¬ÉÕ²ÃŪ¤ËÀ¸À®¤µ¤ì¤ë¤³¤È¤¬¤¢¤ë¡£ + + ¤³¤Î¥Õ¥é¥°¤Ï¥Ç¥Õ¥©¥ë¥È¤Ç¤ÏΩ¤Ã¤Æ¤ª¤é¤º¡¢¥³¥ó¥Ð¡¼¥¿¤Ï³¤±¤Æ¾¤Î + ʸ»ú¤ò¥¨¥ó¥³¡¼¥É¤¹¤ë¤â¤Î¤È²¾Äꤷ¤Æ¤¤¤ë¡£ + + ¤³¤Î¥Õ¥é¥°¤Ï¥Ç¥³¡¼¥É¤Ë¤Ï´Ø·¸¤·¤Ê¤¤¡£ */ + + int last_block; + + /***en + If the value is nonzero, it specifies at most how many + characters to convert. */ + + unsigned at_most; + + /***en + The following three members are to report the result of the + conversion. */ + + /***en + Number of characters most recently decoded or encoded. */ + + /***ja + ºÇ¸å¤Ë¥Ç¥³¡¼¥É/¥¨¥ó¥³¡¼¥É¤µ¤ì¤¿Ê¸»ú¿ô */ + + int nchars; + + /***en + Number of bytes recently decoded or encoded. */ + + /***ja + ºÇ¸å¤Ë¥Ç¥³¡¼¥É/¥¨¥ó¥³¡¼¥É¤µ¤ì¤¿¥Ð¥¤¥È¿ô */ + + int nbytes; + + /***en + Result code of the conversion. */ + + /***ja + ¥³¡¼¥ÉÊÑ´¹¤Î·ë²Ì¤ò¼¨¤¹¥³¡¼¥É */ + + enum MConversionResult result; + + /***en + Various information about the status of code conversion. The + contents depend on the type of coding system. It is assured + that @c status is aligned so that any type of casting is safe + and at least 256 bytes of memory space can be used. */ + + /***ja + ¥³¡¼¥ÉÊÑ´¹¤Î¾õ¶·¤Ë´Ø¤¹¤ë¾ðÊó¡£ÆâÍƤϥ³¡¼¥É·Ï¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê + ¤ë¡£@c status ¤Ï¤É¤Î¤è¤¦¤Ê·¿¤Ø¤Î¥­¥ã¥¹¥È¤ËÂФ·¤Æ¤â°ÂÁ´¤Ê¤è¤¦¤Ë¥á + ¥â¥ê¥¢¥é¥¤¥ó¤µ¤ì¤Æ¤ª¤ê¡¢¤Þ¤¿ºÇÄã256¥Ð¥¤¥È¤Î¥á¥â¥êÎΰ褬»È¤¨¤ë¤è + ¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£ */ + + union { + void *ptr; + double dbl; + char c[256]; + } status; + + /***en + This member is for internally use only. An application program + should never touch it. */ + void *internal_info; +} MConverter; +/*=*/ + +/*** @ingroup m17nConv */ +/***en @brief Types of coding system */ +/***ja @brief ¥³¡¼¥É·Ï¤Î¥¿¥¤¥× */ + +enum MCodingType + { + /***en + A coding system of this type supports charsets directly. + The dimension of each charset defines the length of bytes to + represent a single character of the charset, and a byte + sequence directly represents the code-point of a character. + + The m17n library provides the default decoding and encoding + routines of this type. */ + + /***ja + ¤³¤Î¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤Ïʸ»ú¥»¥Ã¥È¤òľÀÜ¥µ¥Ý¡¼¥È¤¹¤ë¡£³Æʸ»ú¥»¥Ã + ¥È¤Î¼¡¸µ¤È¤Ï¡¢¤½¤Îʸ»ú¥»¥Ã¥È¤Ç°ìʸ»ú¤òɽ¸½¤¹¤ë¤¿¤á¤ËɬÍפʥХ¤ + ¥È¿ô¤Ç¤¢¤ê¡¢¥Ð¥¤¥ÈÎó¤Ïʸ»ú¤Î¥³¡¼¥É¥Ý¥¤¥ó¥È¤òľÀÜɽ¤ï¤¹¡£ + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¤³¤Î¥¿¥¤¥×ÍѤΥǥե©¥ë¥È¤Î¥¨¥ó¥³¡¼¥É¡¿¥Ç¥³¡¼ + ¥É¥ë¡¼¥Æ¥£¥ó¤òÄ󶡤¹¤ë¡£ */ + + MCODING_TYPE_CHARSET, + + /***en + A coding system of this type supports byte sequences of a + UTF (UTF-8, UTF-16, UTF-32) like structure. + + The m17n library provides the default decoding and encoding + routines of this type. */ + + /***ja + ¤³¤Î¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤Ï¡¢UTF ·Ï (UTF-8, UTF-16, UTF-32) ¤Î¥Ð¥¤ + ¥ÈÎó¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£ + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¤³¤Î¥¿¥¤¥×ÍѤΥǥե©¥ë¥È¤Î¥¨¥ó¥³¡¼¥É¡¿¥Ç¥³¡¼ + ¥É¥ë¡¼¥Æ¥£¥ó¤òÄ󶡤¹¤ë¡£ */ + + MCODING_TYPE_UTF, + + /***en + A coding system of this type supports byte sequences of an + ISO-2022 like structure. The details of each structure are + specified by @c MCodingInfoISO2022 . + + The m17n library provides decoding and encoding routines of + this type. */ + + /***ja + ¤³¤Î¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤Ï¡¢ISO-2022 ·Ï¤Î¥Ð¥¤¥ÈÎó¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£ + ¤³¤ì¤é¤Î¥³¡¼¥É·Ï¤Î¹½Â¤¤Î¾ÜºÙ¤Ï @c MCodingInfoISO2022 ¤Ç»ØÄꤵ + ¤ì¤ë¡£ + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¤³¤Î¥¿¥¤¥×ÍѤΥǥե©¥ë¥È¤Î¥¨¥ó¥³¡¼¥É¡¿¥Ç¥³¡¼ + ¥É¥ë¡¼¥Æ¥£¥ó¤òÄ󶡤¹¤ë¡£ */ + + MCODING_TYPE_ISO_2022, + + /***en + A coding system of this type is for byte sequences of + miscellaneous structures. + + The m17n library does not provide decoding and encoding + routines of this type. They must be provided by the + application program. */ + + /***ja + ¤³¤Î¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤Ï¡¢¤½¤Î¾¤Î¹½Â¤¤Î¥Ð¥¤¥ÈÎó¤Î¤¿¤á¤Î¤â¤Î¤Ç¤¢ + ¤ë¡£ + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï¤³¤Î¥¿¥¤¥×ÍѤΥ¨¥ó¥³¡¼¥É¡¿¥Ç¥³¡¼¥É¥ë¡¼¥Æ¥£¥ó + ¤òÄ󶡤·¤Ê¤¤¤Î¤Ç¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¦¤Ç¤½¤ì¤é¤ò½àÈ÷¤¹ + ¤ëɬÍפ¬¤¢¤ë¡£ */ + + MCODING_TYPE_MISC + }; +/*=*/ + +/*** @ingroup m17nConv */ +/***en @brief Bit-masks to specify the detail of coding system whose type is + MCODING_TYPE_ISO_2022. */ + +/***ja @brief MCODING_TYPE_ISO_2022 ¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤Î¾ÜºÙ¤òɽ¤ï¤¹¥Ó¥Ã¥È¥Þ¥¹ + ¥¯ */ + +enum MCodingFlagISO2022 + { + /***en + On encoding, reset the invocation and designation status to + initial at end of line. */ + /***ja ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢¹ÔËö¤Ç¸Æ¤Ó½Ð¤· (invocation) ¤È»Ø¼¨ + (designation) ¤Î¾õÂÖ¤ò½é´üÃͤËÌ᤹¡£ */ + MCODING_ISO_RESET_AT_EOL = 0x1, + + /***en + On encoding, reset the invocation and designation status to + initial before any control codes. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢¤¹¤Ù¤Æ¤ÎÀ©¸æʸ»ú¤ÎÁ°¤Ç¡¢¸Æ¤Ó½Ð¤· + (invocation) ¤È»Ø¼¨ (designation) ¤Î¾õÂÖ¤ò½é´üÃͤËÌ᤹¡£ */ + MCODING_ISO_RESET_AT_CNTL = 0x2, + + /***en + Use the right graphic plane. */ + /***ja + ¿Þ·Áʸ»ú½¸¹ç¤Î±¦Â¦¤ò»È¤¦¡£ */ + MCODING_ISO_EIGHT_BIT = 0x4, + + /***en + Use the non-standard 4 bytes format for designation sequence + for charsets JISX0208.1978, GB2312, and JISX0208.1983. */ + /***ja + JISX0208.1978, GB2312, JISX0208.1983 ¤Îʸ»ú½¸¹ç¤ËÂФ¹¤ë»Ø¼¨¥·¡¼ + ¥¯¥¨¥ó¥¹¤È¤·¤Æ¡¢Èóɸ½à¤Î4¥Ð¥¤¥È·Á¼°¤òÍѤ¤¤ë¡£ */ + + MCODING_ISO_LONG_FORM = 0x8, + + /***en + On encoding, unless explicitly specified, designate charsets + to G0. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢Æä˻ØÄꤵ¤ì¤Ê¤¤¸Â¤ê¡¢Ê¸»ú½¸¹ç¤ò G0 ¤Ë + »Ø¼¨¤¹¤ë¡£*/ + MCODING_ISO_DESIGNATION_G0 = 0x10, + + /***en + On encoding, unless explicitly specified, designate charsets + except for ASCII to G1. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢Æä˻ØÄꤵ¤ì¤Ê¤¤¸Â¤ê¡¢ASCII °Ê³°¤Îʸ»ú½¸¹ç¤ò G1 + ¤Ë»Ø¼¨¤¹¤ë¡£*/ + MCODING_ISO_DESIGNATION_G1 = 0x20, + + /***en + On encoding, unless explicitly specified, designate 94-chars + charsets to G0, 96-chars charsets to G1. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢Æä˻ØÄꤵ¤ì¤Ê¤¤¸Â¤ê¡¢94ʸ»ú½¸¹ç¤ò G0 + ¤Ë¡¢96ʸ»ú½¸¹ç¤ò G1 ¤Ë»Ø¼¨¤¹¤ë¡£*/ + MCODING_ISO_DESIGNATION_CTEXT = 0x40, + + /***en + On encoding, encode such charsets not conforming to ISO-2022 + by ESC % / ..., and encode non-supported Unicode characters by + ESC % G ... ESC % @@ . On decoding, handle those escape + sequences. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢ISO-2022 ¤Ë¹çÃפ·¤Ê¤¤Ê¸»ú½¸¹ç¤ò ESC % / ... ¤Ç¥¨ + ¥ó¥³¡¼¥É¤¹¤ë¡£¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Ê¤¤ Unicode ʸ»ú¤Ï ESC % G ... + ESC % @@ ¤Ç¥¨¥ó¥³¡¼¥É¤¹¤ë¡£ + ¥Ç¥³¡¼¥É¤ÎºÝ¡¢¤³¤ì¤é¤Î¥¨¥¹¥±¡¼¥×¡¦¥·¡¼¥±¥ó¥¹¤ò²ò¼á¤¹¤ë¡£ */ + MCODING_ISO_DESIGNATION_CTEXT_EXT = 0x80, + + /***en + Use locking shift. */ + /***ja + ¥í¥Ã¥­¥ó¥°¥·¥Õ¥È¤ò»È¤¦¡£ */ + MCODING_ISO_LOCKING_SHIFT = 0x100, + + /***en + Use single shift (SS2 (0x8E or ESC N), SS3 (0x8F or ESC O)). */ + /***ja + ¥·¥ó¥°¥ë¥·¥Õ¥È (SS2 or ESC N) ¤ò»È¤¦¡£ */ + MCODING_ISO_SINGLE_SHIFT = 0x200, + + /***en + Use 7-bit single shift 2 (SS2 (0x19)). */ + /***ja + ¥·¥ó¥°¥ë¥·¥Õ¥È (0x19) ¤ò»È¤¦¡£ */ + MCODING_ISO_SINGLE_SHIFT_7 = 0x400, + + /***en + Use EUC-TW like special shifting. */ + /***ja + EUC-TW É÷¤ÎÆÃÊ̤ʥ·¥Õ¥È¤ò»È¤¦¡£ */ + MCODING_ISO_EUC_TW_SHIFT = 0x800, + + /***en + Use ISO-6429 escape sequences to indicate direction. + Not yet implemented. */ + /***ja + ISO-6429 ¤Î¥¨¥¹¥±¡¼¥×¥·¡¼¥¯¥¨¥ó¥¹¤ÇÊý¸þ¤ò»Ø¼¨¤¹¤ë¡£ */ + MCODING_ISO_ISO6429 = 0x1000, + + /***en + On encoding, if a charset has revision number, produce escape + sequences to specify the number. */ + /***ja + ¥¨¥ó¥³¡¼¥É¤ÎºÝ¡¢Ê¸»ú¥»¥Ã¥È¤Ë revision number ¤¬¤¢¤ì¤Ð¤½ + ¤ì¤òɽ¤ï¤¹¥¨¥¹¥±¡¼¥×¥·¡¼¥¯¥¨¥ó¥¹¤òÀ¸À®¤¹¤ë¡£ */ + MCODING_ISO_REVISION_NUMBER = 0x2000, + + /***en + Support all ISO-2022 charsets. */ + /***ja + ISO-2022 ¤ÎÁ´Ê¸»ú½¸¹ç¤ò¥µ¥Ý¡¼¥È¤¹¤ë */ + MCODING_ISO_FULL_SUPPORT = 0x3000, + + MCODING_ISO_FLAG_MAX + }; +/*=*/ + +/*** @ingroup m17nConv */ +/***en + @brief Structure for a coding system of type MCODING_TYPE_ISO_2022. + + Structure for extra information about a coding system of type + MCODING_TYPE_ISO_2022. */ + +/***ja + @brief MCODING_TYPE_ISO_2022 ¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤ÇɬÍ× + ¤ÊÉղþðÊóÍѹ½Â¤ÂÎ + + @latexonly \IPAlabel{MCodingInfoISO2022} @endlatexonly */ + +typedef struct +{ + /***en + Table of numbers of an ISO2022 code extension element invoked + to each graphic plane (Graphic Left and Graphic Right). -1 + means no code extension element is invoked to that plane. */ + + /***ja + ³Æ¿Þ·Áʸ»úÎΰè (Graphic Left ¤È Graphic Right) ¤Ë¸Æ¤Ó½Ð¤µ¤ì¤Æ¤¤ + ¤ë¡¢ISO2022 Éä¹ç³ÈÄ¥Í×ÁǤÎÈÖ¹æ¤Î¥Æ¡¼¥Ö¥ë¡£-1 ¤Ï¤½¤ÎÎΰè¤Ë¤É¤ÎÉä + ¹ç³ÈÄ¥Í×ÁǤâ¸Æ¤Ó½Ð¤µ¤ì¤Æ¤¤¤Ê¤¤¤³¤È¤ò¼¨¤¹¡£ */ + + int initial_invocation[2]; + + /***en + Table of code extension elements. The Nth element corresponds + to the Nth charset in $CHARSET_NAMES, which is an argument given + to the mconv_define_coding () function. + + If an element value is 0..3, it specifies a graphic register + number to designate the corresponds charset. In addition, the + charset is initially designated to that graphic register. + + If the value is -4..-1, it specifies a graphic register number + 0..3 respectively to designate the corresponds charset. + Initially, the charset is not designated to any graphic + register. */ + + /***ja + + Éä¹ç³ÈÄ¥Í×ÁǤΥơ¼¥Ö¥ë¡£NÈÖÌܤÎÍ×ÁǤϡ¢$CHARSET_NAMES ¤Î N ÈÖÌÜ + ¤Îʸ»ú¥»¥Ã¥È¤ËÂбþ¤¹¤ë¡£$CHARSET_NAMES ¤Ï´Ø¿ô + mconv_define_coding () ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ + + Ãͤ¬ 0..3 ¤À¤Ã¤¿¤é¡¢Âбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò G0..G3 ¤Î¤½¤ì¤¾¤ì¤Ë»Ø¼¨ + ¤¹¤ë¤³¤È¤ò°ÕÌ£¤¹¤ë¡£¤µ¤é¤Ë¡¢½é´ü¾õÂ֤Ǥ¹¤Ç¤Ë G0..G3 ¤Ë»Ø¼¨¤µ¤ì¤Æ + ¤¤¤ë¡£ + + Ãͤ¬ -4..-1 ¤À¤Ã¤¿¤é¡¢Âбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò G0..G3 ¤Î¤½¤ì¤¾¤ì¤Ë»Ø + ¼¨¤¹¤ë¤¬¡¢½é´ü¾õÂ֤ǤϤɤ³¤Ë¤â»Ø¼¨¤µ¤ì¤Æ¤¤¤Ê¤¤¤³¤È¤ò°ÕÌ£¤¹¤ë¡£ */ + + char designations[32]; + + /***en + Bitwise OR of @c enum @c MCodingFlagISO2022 . */ + + /***ja + @c enum @c MCodingFlagISO2022 ¤Î¥Ó¥Ã¥Èñ°Ì¤Ç¤ÎÏÀÍý OR */ + + unsigned flags; + +} MCodingInfoISO2022; +/*=*/ + +/*** @ingroup m17nConv */ +/***en + @brief Structure for extra information about a coding system of + type #MCODING_TYPE_UTF. */ + +/***ja + @brief MCODING_TYPE_UTF ¥¿¥¤¥×¤Î¥³¡¼¥É·Ï¤ÇɬÍפÊÉղþðÊóÍѤι½Â¤ÂÎ + + @latexonly \IPApage{MCodingInfoUTF} @endlatexonly + + @latexonly \IPAlabel{MCodingInfoUTF} @endlatexonly */ + +typedef struct +{ + /***en + Specify bits of a code unit. The value must be 8, 16, or 32. */ + int code_unit_bits; + + /***en + Specify how to handle the heading BOM (byte order mark). The + value must be 0, 1, or 2. The meanings are as follows: + + 0: On decoding, check the first two byte. If they are BOM, + decide endian by them. If not, decide endian by the member @c + endian. On encoding, produce byte sequence according to + @c endian with heading BOM. + + 1: On decoding, do not handle the first two bytes as BOM, and + decide endian by @c endian. On encoding, produce byte sequence + according to @c endian without BOM. + + 2: On decoding, handle the first two bytes as BOM and decide + ending by them. On encoding, produce byte sequence according to + @c endian with heading BOM. + + If is 8, the value has no meaning. */ + + /***ja + ÀèƬ¤Î BOM (¥Ð¥¤¥È¥ª¡¼¥À¡¼¥Þ¡¼¥¯) ¤Î¼è¤ê°·¤¤¤ò»ØÄꤹ¤ë¡£ÃÍ¤Ï 0, + 1, 2 ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ê¡¢¤½¤ì¤¾¤ì¤Î°ÕÌ£¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£ + + 0: ¥Ç¥³¡¼¥É¤ÎºÝ¤ËºÇ½é¤Î2¥Ð¥¤¥È¤òÄ´¤Ù¤ë¡£¤â¤·¤½¤ì¤¬ BOM ¤Ç¤¢¤ì¤Ð¡¢ + ¥¨¥ó¥Ç¥£¥¢¥ó¤ò¤½¤ì¤ÇȽÄꤹ¤ë¡£¤â¤·ºÇ½é¤Î2¥Ð¥¤¥È¤¬ BOM ¤Ç¤Ê¤±¤ì¤Ð¡¢ + ¥á¥ó¥Ð @c endian ¤Ë½¾¤Ã¤Æ¥¨¥ó¥Ç¥£¥¢¥ó¤ò·èÄꤹ¤ë¡£¥¨¥ó¥³¡¼¥É¤ÎºÝ + ¤Ë¤Ï @c endian ¤Ë½¾¤Ã¤¿¥Ð¥¤¥ÈÎó¤ò BOM ÉÕ¤ÇÀ¸À®¤¹¤ë¡£ + + 1: ¥Ç¥³¡¼¥É¤ÎºÝ¡¢ºÇ½é¤Î2¥Ð¥¤¥È¤ò BOM ¤È¤·¤Æ°·¤ï¤Ê¤¤¡£¥¨¥ó + ¥Ç¥£¥¢¥ó¤Ï @c endian ¤ÇȽÄꤹ¤ë¡£¥¨¥ó¥³¡¼¥É¤ÎºÝ¤Ë¤Ï¡¢BOM + ¤ò½ÐÎϤ»¤º¡¢@c endian ¤Ë±þ¤¸¤¿¥Ð¥¤¥ÈÎó¤òÀ¸À®¤¹¤ë¡£ + + 2: ¥Ç¥³¡¼¥É¤ÎºÝ¤ËºÇ½é¤Î2¥Ð¥¤¥È¤ò BOM¤È¤·¤Æ°·¤¤¡¢¤½¤ì¤Ë½¾¤Ã + ¤Æ¥¨¥ó¥Ç¥£¥¢¥ó¤òȽÄꤹ¤ë¡£¥¨¥ó¥³¡¼¥É¤ÎºÝ¤Ë¤Ï @c endian ¤Ë + ±þ¤¸¤¿¥Ð¥¤¥ÈÎó¤ò BOM ÉÕ¤­¤ÇÀ¸À®¤¹¤ë¡£ */ + int bom; + + /***en + Specify the endian type. The value must be 0 or 1. 0 means + little endian, and 1 means big endian. + + If is 8, the value has no meaning. */ + /***ja + ¥¨¥ó¥Ç¥£¥¢¥ó¤Î¥¿¥¤¥×¤ò»ØÄꤹ¤ë¡£ÃÍ¤Ï 0 ¤« 1 ¤Ç¤¢¤ê¡¢0 ¤Ê¤é¤Ð¥ê¥È + ¥ë¥¨¥ó¥Ç¥£¥¢¥ó¡¢1 ¤Ê¤é¤Ð¥Ó¥Ã¥°¥¨¥ó¥Ç¥£¥¢¥ó¤Ç¤¢¤ë¡£*/ + int endian; +} MCodingInfoUTF; +/*=*/ + +extern MSymbol mconv_define_coding (char *name, MPlist *plist, + int (*resetter) (MConverter *), + int (*decoder) (unsigned char *, int, + MText *, MConverter *), + int (*encoder) (MText *, int, int, + unsigned char *, int, + MConverter *), + void *extra_info); + +extern MSymbol mconv_resolve_coding (MSymbol symbol); + +extern int mconv_list_codings (MSymbol **symbols); + +extern MConverter *mconv_buffer_converter (MSymbol coding, unsigned char *buf, + int n); + +extern MConverter *mconv_stream_converter (MSymbol coding, FILE *fp); + +extern int mconv_reset_converter (MConverter *converter); + +extern void mconv_free_converter (MConverter *converter); + +extern MConverter *mconv_rebind_buffer (MConverter *converter, + unsigned char *buf, int n); + +extern MConverter *mconv_rebind_stream (MConverter *converter, FILE *fp); + +extern MText *mconv_decode (MConverter *converter, MText *mt); + +MText *mconv_decode_buffer (MSymbol name, unsigned char *buf, int n); + +MText *mconv_decode_stream (MSymbol name, FILE *fp); + +extern int mconv_encode (MConverter *converter, MText *mt); + +extern int mconv_encode_range (MConverter *converter, MText *mt, + int from, int to); + +extern int mconv_encode_buffer (MSymbol name, MText *mt, + unsigned char *buf, int n); + +extern int mconv_encode_stream (MSymbol name, MText *mt, FILE *fp); + +extern int mconv_getc (MConverter *converter); + +extern int mconv_ungetc (MConverter *converter, int c); + +extern int mconv_putc (MConverter *converter, int c); + +extern MText *mconv_gets (MConverter *converter, MText *mt); + +/* (S4) Locale related functions corresponding to libc functions */ + +/*** @ingroup m17nShell */ +/***en @defgroup m17nLocale Locale */ +/***ja @defgroup m17nLocale ¥í¥±¡¼¥ë */ +/*=*/ + +/***en + @brief @c struct @c MLocale + + The structure @c MLocale is used to hold information about name, + language, territory, modifier, codeset, and the corresponding + coding system of locales. + + The contents of this structure are implementation dependent. Its + internal structure is concealed from application programs. */ + +/***ja + @brief @c MLocale ¹½Â¤ÂÎ + + @c MLocale ¹½Â¤ÂΤϡ¢¥í¥±¡¼¥ë¤Î̾Á°¡¢¸À¸ì¡¢ÃÏ°è¡¢¥â¥Ç¥£¥Õ¥¡¥¤¥¢¡¢ + ¥³¡¼¥É¥»¥Ã¥È¡¢¤ª¤è¤ÓÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¤¿¤á¤ËÍÑ + ¤¤¤é¤ì¤ë¡£ + + ¤³¤Î¹½Â¤ÂΤÎÆâÍƤϼÂÁõ¤Ë°Í¸¤¹¤ë¡£ ÆâÉô¹½Â¤¤Ï¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í + ¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ */ + +/*** + @seealso + mlocale_get_prop () */ + +typedef struct MLocale MLocale; + +/*=*/ + +extern MSymbol Mlanguage; +extern MSymbol Mterritory; +extern MSymbol Mmodifier; +extern MSymbol Mcodeset; + +extern MLocale *mlocale_set (int category, const char *locale); + +extern MSymbol mlocale_get_prop (MLocale *locale, MSymbol key); + +extern int mtext_ftime (MText *mt, const char *format, const struct tm *tm, + MLocale *locale); + +extern MText *mtext_getenv (const char *name); + +extern int mtext_putenv (MText *mt); + +extern int mtext_coll (MText *mt1, MText *mt2); + +/* + * (9) Miscellaneous functions of libc level (not yet implemented) + */ + +/* +extern int mtext_width (MText *mt, int n); +extern MText *mtext_tolower (MText *mt); +extern MText *mtext_toupper (MText *mt); +*/ + +/* + * (10) Input method + */ + +/*** @ingroup m17nShell */ +/***en @defgroup m17nInputMethod Input Method (basic) */ +/***ja @defgroup m17nInputMethod ÆþÎϥ᥽¥Ã¥É (´ðËÜ) */ +/*=*/ + +/* Struct forward declaration. */ +typedef struct MInputMethod MInputMethod; +typedef struct MInputContext MInputContext; + +/*** @ingroup m17nInputMethod */ + +/***en + @brief Type of input method callback functions. + + This is the type of callback functions called from input method + drivers. #IC is a pointer to an input context, #COMMAND is a name + of callback for which the function is called. */ + +typedef void (*MInputCallbackFunc) (MInputContext *ic, MSymbol command); +/*=*/ + +/***en + @brief Structure of input method driver. + + The type @c MInputDriver is the structure of an input driver that + contains several functions to handle an input method. */ + +/***ja + @brief ÆþÎϥɥ饤¥Ð + + @c MInputDriver ·¿¤Ï¡¢ÆþÎϥ᥽¥Ã¥É¤ò¼è¤ê°·¤¦´Ø¿ô¤ò´Þ¤àÆþÎϥɥ饤¥Ð + ¤Î¹½Â¤ÂΤǤ¢¤ë¡£ */ + +typedef struct MInputDriver +{ + /***en + @brief Open an input method. + + This function opens the input method $IC. It is called from the + function minput_open_im () after all member of $IM but + set. If opening $IM succeeds, it returns 0. Otherwise, it + returns -1. The function can setup $IM->info to keep various + information that is referred by the other driver functions. */ + + /***ja + @brief ÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë + + ¤³¤Î´Ø¿ô¤Ï¡¢ÆþÎϥ᥽¥Ã¥É$IM¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£$IM ¤Î°Ê³°¤ÎÁ´ + ¥á¥ó¥Ð¡¼¤¬¥»¥Ã¥È¤µ¤ì¤¿¸å¤Ç¡¢´Ø¿ô minput_open_im () ¤«¤é¸Æ¤Ð¤ì¤ë¡£ + $IM ¤ò¥ª¡¼¥×¥ó¤Ç¤­¤ì¤Ð 0 ¤ò¡¢¤Ç¤­¤Ê¤±¤ì¤Ð -1¤òÊÖ¤¹¡£¤³¤Î´Ø¿ô¤Ï + $IM->info ¤òÀßÄꤷ¤Æ¡¢Â¾¤Î¥É¥é¥¤¥Ð´Ø¿ô¤«¤é»²¾È¤µ¤ì¤ë¾ðÊó¤òÊÝ»ý¤¹ + ¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + */ + + int (*open_im) (MInputMethod *im); + + /***en + @brief Close an input method. + + This function closes the input method $IM. It is called from + the function minput_close_im (). It frees all memory allocated + for $IM->info (if any) after finishing all the tasks of closing + the input method. But, the other members of $IM should not be + touched. */ + + /***ja + @brief ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼¥º¤¹¤ë + + ¤³¤Î´Ø¿ô¤Ï´Ø¿ô minput_close_im () ¤«¤é¸Æ¤Ð¤ì¡¢ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼ + ¥º¤¹¤ë¡£¡£ÆþÎϥ᥽¥Ã¥É¤Î¥¯¥í¡¼¥º¤¬¤¹¤Ù¤Æ½ªÎ»¤·¤¿»þÅÀ¤Ç¡¢ + $IM->info ¤Ë³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¥á¥â¥ê¤ò(¤¢¤ì¤Ð)³«Êü¤¹¤ë¡£¤¿¤À¤·¡¢ + $IM ¤Î¾¤Î¥á¥ó¥Ð¤Ë±Æ¶Á¤òÍ¿¤¨¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ */ + + void (*close_im) (MInputMethod *im); + + /***en + @brief Create an input context. + + This function creates the input context $IC. It is called from + the function minput_create_ic () after all members of $IC but + are set. If creating $IC succeeds, it returns 0. + Otherwise, it returns -1. The function can setup $IC->info to + keep various information that is referred by the other driver + functions. */ + + /***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÀ¸À®¤¹¤ë + + ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤òÀ¸À®¤¹¤ë´Ø¿ô¡£¤³¤Î´Ø¿ô¤Ï¡¢$IC ¤Î + °Ê³°¤ÎÁ´¥á¥ó¥Ð¡¼¤¬¥»¥Ã¥È¤µ¤ì¤¿¸å¤Ç¡¢´Ø¿ô minput_create_ic () ¤« + ¤é¸Æ¤Ð¤ì¤ë¡£$IC ¤òÀ¸À®¤Ç¤­¤ì¤Ð 0 ¤ò¡¢¤Ç¤­¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£¤³ + ¤Î´Ø¿ô¤Ï $IC->info ¤òÀßÄꤷ¤Æ¡¢Â¾¤Î¥É¥é¥¤¥Ð´Ø¿ô¤«¤é»²¾È¤µ¤ì¤ë¾ð + Êó¤òÊÝ»ý¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ */ + + + int (*create_ic) (MInputContext *ic); + + /***en + @brief Destroy an input context. + + This function is called from the function minput_destroy_ic () + and destroys the input context $IC. It frees all memory + allocated for $IC->info (if any) after finishing all the tasks + of destroying the input method. But, the other members of $IC + should not be touched. */ + + /***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÇ˲õ¤¹¤ë + + ´Ø¿ô minput_destroy_ic () ¤«¤é¸Æ¤Ð¤ì¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤òÇË + ²õ¤¹¤ë¡£ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ÎÇ˲õ¤¬¤¹¤Ù¤Æ½ªÎ»¤·¤¿»þÅÀ¤Ç¡¢$IC->info + ¤Ë³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¥á¥â¥ê¤ò(¤¢¤ì¤Ð)³«Êü¤¹¤ë¡£¤¿¤À¤·¡¢$IC ¤Î¾¤Î + ¥á¥ó¥Ð¤Ë±Æ¶Á¤òÍ¿¤¨¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ */ + + void (*destroy_ic) (MInputContext *ic); + + /***en + @brief Filter an input key. + + This function is called from the function minput_filter () and + filters an input key. $KEY and $ARG are the same as what given + to minput_filter (). + + The task of the function is to handle $KEY, update the internal + state of $IC. If $KEY is absorbed by the input method and no + text is produced, it returns 1. Otherwise, it returns 0. + + It may update $IC->status, $IC->preedit, $IC->cursor_pos, + $IC->ncandidates, $IC->candidates, and $IC->produced if that is + necessary for the member . + + The meaning of $ARG depends on the input driver. See the + documentation of @c minput_default_driver and @c + minput_gui_driver for instance. */ + + /***ja + @brief ÆþÎÏ¥¤¥Ù¥ó¥È¤¢¤ë¤¤¤ÏÆþÎÏ¥­¡¼¤ò¥Õ¥£¥ë¥¿¤¹¤ë + + ´Ø¿ô minput_filter () ¤«¤é¸Æ¤Ð¤ì¡¢ÆþÎÏ¥­¡¼¤ò¥Õ¥£¥ë¥¿¤¹¤ë¡£°ú¿ô + $KEY, $ARG ¤Ï´Ø¿ô minput_filter () ¤Î¤â¤Î¤ÈƱ¤¸¡£ + + ¤³¤Î´Ø¿ô¤Ï $KEY ¤ò½èÍý¤·¡¢$IC ¤ÎÆâÉô¾õÂÖ¤ò¥¢¥Ã¥×¥Ç¡¼¥È¤¹¤ë¡£ + $KEY ¤¬ÆþÎϥ᥽¥Ã¥É¤ËµÛ¼ý¤µ¤ì¤Æ¥Æ¥­¥¹¥È¤¬À¸À®¤µ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë + ¤Ï¡¢ 1 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£ + + ¥á¥ó¥Ð ¤ËɬÍפǤ¢¤ì¤Ð¡¢$IC->status, $IC->preedit, + $IC->cursor_pos, $IC->ncandidates, $IC->candidates, + $IC->produced ¤ò¥¢¥Ã¥×¥Ç¡¼¥È¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + + $ARG ¤Î°ÕÌ£¤ÏÆþÎϥɥ饤¥Ð¤Ë°Í¸¤¹¤ë¡£Îã¤Ï @c + minput_default_driver ¤Þ¤¿¤Ï @c minput_gui_driver ¤Î¥É¥­¥å¥á¥ó¥È + ¤ò»²¾È¤Î¤³¤È¡£ */ + + int (*filter) (MInputContext *ic, MSymbol key, void *arg); + + /***en + @brief Lookup a produced text in an input context + + It is called from the function minput_lookup () and looks up a + produced text in the input context $IC. This function + concatenate a text produced by the input key $KEY (if any) to + M-text $MT. If $KEY was correctly handled by the input method + of $IC, it returns 0. Otherwise, it returns 1. + + The meaning of $ARG depends on the input driver. See the + documentation of @c minput_default_driver and @c + minput_gui_driver for instance. */ + + /***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ÇÀ¸À®¤µ¤ì¤ë¥Æ¥­¥¹¥È¤Î³ÍÆÀ + + ´Ø¿ô minput_lookup () ¤«¤é¸Æ¤Ð¤ì¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤ÇÀ¸À®¤µ + ¤ì¤ë¥Æ¥­¥¹¥È¤ò¸¡º÷¤¹¤ë¡£ÆþÎÏ¥­¡¼ $KEY ¤Ë¤è¤Ã¤ÆÀ¸À®¤µ¤ì¤ë¥Æ¥­¥¹¥È + ¤¬¤¢¤ì¤Ð¡¢$IC->produced ¤ËÀßÄꤹ¤ë¡£ $KEY ¤¬ÆþÎϥ᥽¥Ã¥É $IC ¤Ë + ¤è¤Ã¤ÆÀµ¤·¤¯½èÍý¤µ¤ì¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 1 ¤òÊÖ¤¹¡£ + + $ARG ¤Î°ÕÌ£¤ÏÆþÎϥɥ饤¥Ð¤Ë°Í¸¤¹¤ë¡£Îã¤Ï @c + minput_default_driver ¤Þ¤¿¤Ï @c minput_gui_driver ¤Î¥É¥­¥å¥á¥ó¥È + ¤ò»²¾È¤Î¤³¤È¡£ */ + + int (*lookup) (MInputContext *ic, MSymbol key, void *arg, MText *mt); + + /***en + @brief List of callback functions. + + List of callback functions. Keys are one of + #Minput_preedit_start, #Minput_preedit_draw, + #Minput_preedit_done, #Minput_status_start, #Minput_status_draw, + #Minput_status_done, #Minput_candidates_start, + #Minput_candidates_draw, #Minput_candidates_done, + #Minput_set_spot, and #Minput_toggle. Values are functions of + type #MInputCallbackFunc. */ + MPlist *callback_list; + +} MInputDriver; +/*=*/ + +extern MInputDriver minput_default_driver; + +extern MSymbol Minput_driver; + +extern MInputDriver *minput_driver; + +/** Symbols for callback commands. */ +extern MSymbol Minput_preedit_start; +extern MSymbol Minput_preedit_draw; +extern MSymbol Minput_preedit_done; +extern MSymbol Minput_status_start; +extern MSymbol Minput_status_draw; +extern MSymbol Minput_status_done; +extern MSymbol Minput_candidates_start; +extern MSymbol Minput_candidates_draw; +extern MSymbol Minput_candidates_done; +extern MSymbol Minput_set_spot; +extern MSymbol Minput_toggle; + +/***en + @brief Structure of input method. + + The type @c MInputMethod is the structure of input method + objects. */ +/***ja + @brief ÆþÎϥ᥽¥Ã¥ÉÍѹ½Â¤ÂÎ + + @c MInputMethod ·¿¤Ï¡¢ÆþÎϥ᥽¥Ã¥É¥ª¥Ö¥¸¥§¥¯¥ÈÍѤι½Â¤ÂΤǤ¢¤ë¡£ */ + +struct MInputMethod +{ + /***en Which language this input method is for. The value is @c + Mnil if the input method is foreign. */ + /***ja ¤É¤Î¸À¸ìÍѤÎÆþÎϥ᥽¥Ã¥É¤«¡£ÆþÎϥ᥽¥Ã¥É¤¬³°Éô¤Î¤â¤Î¤Ç¤¢¤ë¾ì + ¹ç¤Ë¤ÏÃͤȤ·¤Æ @c ¤ò»ý¤Ä¡£ */ + MSymbol language; + + /***en Name of the input method. If the input method is foreign, it + must has a property of key @c Minput_driver and the value must be a + pointer to a proper input driver. */ + /***ja ÆþÎϥ᥽¥Ã¥É¤Î̾Á°¡£³°Éô¥á¥½¥Ã¥É¤Ç¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢@c + Minput_driver ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤÏŬÀÚ¤ÊÆþÎ촃 + ¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£*/ + MSymbol name; + + /***en Input driver of the input method. */ + /***ja ¤½¤ÎÆþÎϥ᥽¥Ã¥ÉÍѤÎÆþÎϥɥ饤¥Ð */ + MInputDriver driver; + + /***en The argument given to minput_open_im (). */ + /***ja minput_open_im () ¤ËÍ¿¤¨¤é¤ì¤ë°ú¿ô */ + void *arg; + + /***en Pointer to extra information that .open_im () + setups. */ + /***ja .open_im () ¤¬ÀßÄꤹ¤ëÄɲþðÊó¤Ø¤Î¥Ý¥¤¥ó¥¿ */ + void *info; +}; + +/*=*/ + +/***en + @brief Structure of input context. + + The type @c MInputContext is the structure of input context + objects. */ + +/***ja + @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥ÈÍѹ½Â¤ÂÎ + + @c MInputContext ·¿¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¥ª¥Ö¥¸¥§¥¯¥ÈÍѤι½Â¤ÂΤǤ¢ + ¤ë¡£ */ + +struct MInputContext +{ + /***en Backward pointer to the input method. It is set up be the + function minput_create_ic (). */ + /***ja ÆþÎϥ᥽¥Ã¥É¤Ø¤ÎµÕ¥Ý¥¤¥ó¥¿¡£´Ø¿ô minput_create_ic () ¤Ë¤è¤Ã¤Æ + ÀßÄꤵ¤ì¤ë¡£ */ + MInputMethod *im; + + /***en M-text produced by the input method. It is set up by the + function minput_lookup () . */ + /***ja ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆÀ¸À®¤µ¤ì¤ë M-text¡£´Ø¿ô minput_lookup () + ¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤ë¡£ */ + MText *produced; + + /***en Argument given to the function minput_create_im (). */ + /***ja ´Ø¿ô minput_create_ic () ¤ËÅϤµ¤ì¤ë°ú¿ô */ + void *arg; + + /***en Flag telling whether the input context is currently active or + inactive. The value is set to 1 (active) when the input context + is created. It can be toggled by the function minput_toggle + (). */ + /***ja ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤¬¥¢¥¯¥Æ¥£¥Ö¤«¤É¤¦¤«¤ò¼¨¤¹¥Õ¥é¥°¡£ÆþÎÏ¥³¥ó¥Æ + ¥¯¥¹¥È¤¬À¸À®¤µ¤ì¤¿»þÅÀ¤Ç¤ÏÃÍ¤Ï 1 ¡Ê¥¢¥¯¥Æ¥£¥Ö¡Ë¤Ç¤¢¤ê¡¢´Ø¿ô + minput_toggle () ¤Ë¤è¤Ã¤Æ¥È¥°¥ë¤Ç¤­¤ë¡£ */ + int active; + + + /***en Spot location and size of the input context. */ + /***ja ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¥¹¥Ý¥Ã¥È¤Î°ÌÃÖ¤ÈÂ礭¤µ */ + struct { + /***en X and Y coordinate of the spot. */ + /***ja ¥¹¥Ý¥Ã¥È¤Î X, Y ºÂɸ */ + int x, y; + /***en Ascent and descent pixels of the line of the spot. */ + /***ja ¥¹¥Ý¥Ã¥È¤Î¥¢¥»¥ó¥È¤È¥Ç¥£¥»¥ó¥È¤Î¥Ô¥¯¥»¥ë¿ô */ + int ascent, descent; + + /***en Font size for preedit text in 1/10 point. */ + int fontsize; + + /***en M-text at the spot, or NULL. */ + MText *mt; + /***en Character position in at the spot. */ + int pos; + } spot; + + /***en The usage of the following members depends on the input + driver. The descriptions below are for the input driver of an + internal input method. They are set by the function + ->driver.filter (). */ + /***ja °Ê²¼¤Î¥á¥ó¥Ð¤Î»ÈÍÑË¡¤ÏÆþÎϥɥ饤¥Ð¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£°Ê²¼¤ÎÀâÌÀ + ¤Ï¡¢ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤÎÆþÎϥɥ饤¥Ð¤ËÂФ¹¤ë¤â¤Î¤Ç¤¢¤ë¡£ */ + + /***en Pointer to extra information that ->driver.create_ic () + setups. It is used to record the internal state of the input + context. */ + /***ja ->driver.create_ic () ¤¬ÀßÄꤹ¤ëÄɲþðÊó¤Ø¤Î¥Ý¥¤¥ó¥¿¡£Æþ + ÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ÎÆâÉô¾õÂÖ¤òµ­Ï¿¤¹¤ë¤¿¤á¤ËÍѤ¤¤é¤ì¤ë¡£ */ + void *info; + + /***en M-text describing the current status of the input + context. */ + /***ja ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¸½ºß¤Î¾õ¶·¤òɽ¤¹ M-text */ + MText *status; + + /***en The function ->driver.filter () sets the value to 1 when + it changes . */ + int status_changed; + + /***en M-text containing the current preedit text. The function + ->driver.filter () sets the value. */ + /***ja ¸½ºß¤Î preedit ¥Æ¥­¥¹¥È¤ò´Þ¤à M-text */ + MText *preedit; + + /***en The function ->driver.filter () sets the value to 1 when + it changes . */ + int preedit_changed; + + /***en Cursor position of . */ + /***ja ¤Î¥«¡¼¥½¥ë°ÌÃÖ */ + int cursor_pos; + + int cursor_pos_changed; + + /***en Array of the current candidate texts. */ + /***ja ¸½ºß¤Î¥Æ¥­¥¹¥È¸õÊä¤Î¥°¥ë¡¼¥×¤Î¥ê¥¹¥È */ + MPlist *candidate_list; + int candidate_index; + int candidate_from, candidate_to; + int candidate_show; + + /***en The function ->driver.filter () sets the value to 1 when + it changes one of the above members. */ + int candidates_changed; + + MPlist *plist; +}; + +/*=*/ + +extern MInputMethod *minput_open_im (MSymbol language, MSymbol name, + void *arg); + +/*=*/ + +extern void minput_close_im (MInputMethod *im); + +extern MInputContext *minput_create_ic (MInputMethod *im, void *arg); + +extern void minput_destroy_ic (MInputContext *ic); + +extern int minput_filter (MInputContext *ic, MSymbol key, void *arg); + +extern int minput_lookup (MInputContext *ic, MSymbol key, void *arg, + MText *mt); + +extern void minput_set_spot (MInputContext *ic, int x, int y, + int ascent, int descent, int fontsize, + MText *mt, int pos); + +extern void minput_toggle (MInputContext *ic); + +extern MSymbol minput_char_to_key (int c); + +/*=*/ + +extern MInputMethod *mdebug_dump_im (MInputMethod *im, int indent); + +#ifdef __cplusplus +} +#endif + +#endif /* _M17N_H_ */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/mlocale.h b/src/mlocale.h new file mode 100644 index 0000000..306dfe9 --- /dev/null +++ b/src/mlocale.h @@ -0,0 +1,30 @@ +/* mlocale.c -- header file for the locale module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_LOCAL_H_ +#define _M17N_LOCAL_H_ + +/** The current locales of each category. */ +extern MLocale *mlocale__collate, *mlocale__ctype; +extern MLocale *mlocale__messages, *mlocale__time; + +#endif /* _M17N_LOCAL_H_ */ diff --git a/src/mtext.c b/src/mtext.c new file mode 100644 index 0000000..d052cfe --- /dev/null +++ b/src/mtext.c @@ -0,0 +1,2491 @@ +/* mtext.c -- M-text module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nMtext + @brief M-text objects and API for them. + + In the m17n library, text is represented as an object called @e + M-text rather than as a C-string (char * or unsigned + char *). An M-text is a sequence of characters whose length + is equals to or more than 0, and can be coined from various + character sources, e.g. C-strings, files, character codes, etc. + + M-texts are more useful than C-strings in the following points. + + @li M-texts can handle mixture of characters of various scripts, + including all Unicode characters and more. This is an + indispensable facility when handling multilingual text. + + @li Each character in an M-text can have properties called @e text + @e properties. Text properties store various kinds of information + attached to parts of an M-text to provide application programs + with a unified view of those information. As rich information can + be stored in M-texts in the form of text properties, functions in + application programs can be simple. + + In addition, the library provides many functions to manipulate an + M-text just the same way as a C-string. */ + +/***ja + @addtogroup m17nMtext + + @brief M-text ¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤ÏÄ̾ï¤Î C-string¡Êchar * ¤ä unsigned + char *¡Ë¤Ç¤Ï¤Ê¤¯ M-text ¤È¸Æ¤Ö¥ª¥Ö¥¸¥§¥¯¥È¤Ç¥Æ¥­¥¹¥È¤òɽ¸½¤¹ + ¤ë¡£M-text ¤ÏŤµ£°°Ê¾å¤Îʸ»ú¤ÎÎ󤫤é¤Ê¤ê¡¢¼ï¡¹¤Îʸ»ú¥½¡¼¥¹¡ÊÎ㤨 + ¤Ð C-string¡¢¥Õ¥¡¥¤¥ë¡¢Ê¸»ú¥³¡¼¥ÉÅù¡Ë¤«¤éºîÀ®¤Ç¤­¤ë¡£ + + M-text ¤Ë¤Ï¡¢C-string ¤Ë¤Ê¤¤°Ê²¼¤ÎÆÃħ¤¬¤¢¤ë¡£ + + @li M-text ¤ÏÈó¾ï¤Ë¿¤¯¤Î¼ïÎà¤Îʸ»ú¤ò¡¢Æ±»þ¤Ë¡¢º®ºß¤µ¤»¤Æ¡¢Æ±Åù¤Ë + °·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤Ï¤â¤Á¤í¤ó¡¢¤è¤ê¿¤¯¤Îʸ»ú¤Þ + ¤Ç°·¤¨¤ë¡£¤³¤ì¤Ï¿¸À¸ì¥Æ¥­¥¹¥È¤ò°·¤¦¾å¤Ç¤Ïɬ¿Ü¤Îµ¡Ç½¤Ç¤¢¤ë¡£ + + @li M-text Æâ¤Î³Æʸ»ú¤Ï¡¢@e ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£ + ¤ò»ý¤Ä¤³¤È¤¬¤Ç¤­¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ë¤è¤Ã¤Æ¡¢¥Æ¥­¥¹¥È¤Î³ÆÉô°Ì¤Ë + ´Ø¤¹¤ëÍÍ¡¹¤Ê¾ðÊó¤ò M-text Æâ¤ËÊÝ»ý¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î¤¿¤á¡¢¤½¤ì + ¤é¤Î¾ðÊó¤ò¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÆâ¤ÇÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£ + ¤Þ¤¿¡¢M-text ¼«ÂΤ¬Ë­É٤ʾðÊó¤ò»ý¤Ä¤¿¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é + ¥àÃæ¤Î³Æ´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + + ¤µ¤é¤Ë¡¢m17n ¥é¥¤¥Ö¥é¥ê¤Ï C-string ¤òÁàºî¤¹¤ë¤¿¤á¤ËÄ󶡤µ¤ì¤ë¼ï¡¹¤Î + ´Ø¿ô¤ÈƱÅù¤â¤Î¤ò M-text ¤òÁàºî¤¹¤ë¤¿¤á¤ËÄ󶡤¹¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "textprop.h" +#include "character.h" +#include "mtext.h" +#include "plist.h" + +static M17NObjectArray mtext_table; + +static MSymbol M_charbag; + +#ifdef WORDS_BIGENDIAN +static enum MTextFormat default_utf_16 = MTEXT_FORMAT_UTF_16BE; +static enum MTextFormat default_utf_32 = MTEXT_FORMAT_UTF_32BE; +#else +static enum MTextFormat default_utf_16 = MTEXT_FORMAT_UTF_16LE; +static enum MTextFormat default_utf_32 = MTEXT_FORMAT_UTF_32LE; +#endif + +/** Increment character position CHAR_POS and byte position BYTE_POS + so that they point to the next character in M-text MT. No range + check for CHAR_POS and BYTE_POS. */ + +#define INC_POSITION(mt, char_pos, byte_pos) \ + do { \ + int c; \ + \ + if ((mt)->format == MTEXT_FORMAT_UTF_8) \ + { \ + c = (mt)->data[(byte_pos)]; \ + (byte_pos) += CHAR_UNITS_BY_HEAD_UTF8 (c); \ + } \ + else \ + { \ + c = ((unsigned short *) ((mt)->data))[(byte_pos)]; \ + \ + if ((mt)->format != default_utf_16) \ + c = SWAP_16 (c); \ + (byte_pos) += (c < 0xD800 || c >= 0xE000) ? 1 : 2; \ + } \ + (char_pos)++; \ + } while (0) + + +/** Decrement character position CHAR_POS and byte position BYTE_POS + so that they point to the previous character in M-text MT. No + range check for CHAR_POS and BYTE_POS. */ + +#define DEC_POSITION(mt, char_pos, byte_pos) \ + do { \ + if ((mt)->format == MTEXT_FORMAT_UTF_8) \ + { \ + unsigned char *p1 = (mt)->data + (byte_pos); \ + unsigned char *p0 = p1 - 1; \ + \ + while (! CHAR_HEAD_P (p0)) p0--; \ + (byte_pos) -= (p1 - p0); \ + } \ + else \ + { \ + int c = ((unsigned short *) ((mt)->data))[(byte_pos) - 1]; \ + \ + if ((mt)->format != default_utf_16) \ + c = SWAP_16 (c); \ + (byte_pos) -= (c < 0xD800 || c >= 0xE000) ? 1 : 2; \ + } \ + (char_pos)--; \ + } while (0) + + +static int +compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2) +{ + if (mt1->format == mt2->format + && (mt1->format < MTEXT_FORMAT_UTF_8)) + { + unsigned char *p1, *pend1, *p2, *pend2; + + p1 = mt1->data + mtext__char_to_byte (mt1, from1); + pend1 = mt1->data + mtext__char_to_byte (mt1, to1); + + p2 = mt2->data + mtext__char_to_byte (mt2, from2); + pend2 = mt2->data + mtext__char_to_byte (mt2, to2); + + for (; p1 < pend1 && p2 < pend2; p1++, p2++) + if (*p1 != *p2) + return (*p1 > *p2 ? 1 : -1); + return (p2 == pend2 ? (p1 < pend1) : -1); + } + for (; from1 < to1 && from2 < to2; from1++, from2++) + { + int c1 = mtext_ref_char (mt1, from1); + int c2 = mtext_ref_char (mt2, from2); + + if (c1 != c2) + return (c1 > c2 ? 1 : -1); + } + return (from2 == to2 ? (from1 < to1) : -1); +} + +static MText * +copy (MText *mt1, int pos, MText *mt2, int from, int to) +{ + int pos_byte = POS_CHAR_TO_BYTE (mt1, pos); + int nbytes; + struct MTextPlist *plist; + unsigned char *p; + + if (mt2->format <= MTEXT_FORMAT_UTF_8) + { + int from_byte = POS_CHAR_TO_BYTE (mt2, from); + + p = mt2->data + from_byte; + nbytes = POS_CHAR_TO_BYTE (mt2, to) - from_byte; + } + else + { + unsigned char *p1; + int pos1; + + p = p1 = alloca (MAX_UNICODE_CHAR_BYTES * (to - from)); + for (pos1 = from; pos1 < to; pos1++) + { + int c = mtext_ref_char (mt2, pos1); + p1 += CHAR_STRING (c, p1); + } + nbytes = p1 - p; + } + + if (mt1->cache_char_pos > pos) + { + mt1->cache_char_pos = pos; + mt1->cache_byte_pos = pos_byte; + } + + if (pos_byte + nbytes >= mt1->allocated) + { + mt1->allocated = pos_byte + nbytes + 1; + MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT); + } + memcpy (mt1->data + pos_byte, p, nbytes); + mt1->nbytes = pos_byte + nbytes; + mt1->data[mt1->nbytes] = 0; + + plist = mtext__copy_plist (mt2->plist, from, to, mt1, pos); + if (pos == 0) + { + if (mt1->plist) + mtext__free_plist (mt1); + mt1->plist = plist; + } + else + { + if (pos < mt1->nchars) + mtext__adjust_plist_for_delete (mt1, pos, mt1->nchars - pos); + if (from < to) + mtext__adjust_plist_for_insert (mt1, pos, to - from, plist); + } + + mt1->nchars = pos + (to - from); + if (mt1->nchars < mt1->nbytes) + mt1->format = MTEXT_FORMAT_UTF_8; + return mt1; +} + + +static MCharTable * +get_charbag (MText *mt) +{ + MTextProperty *prop = mtext_get_property (mt, 0, M_charbag); + MCharTable *table; + int i; + + if (prop) + { + if (prop->end == mt->nchars) + return ((MCharTable *) prop->val); + mtext_detach_property (prop); + } + + table = mchartable (Msymbol, (void *) 0); + for (i = mt->nchars - 1; i >= 0; i--) + mchartable_set (table, mtext_ref_char (mt, i), Mt); + prop = mtext_property (M_charbag, table, MTEXTPROP_VOLATILE_WEAK); + mtext_attach_property (mt, 0, mtext_nchars (mt), prop); + M17N_OBJECT_UNREF (prop); + return table; +} + + +/* span () : Number of consecutive chars starting at POS in MT1 that + are included (if NOT is Mnil) or not included (if NOT is Mt) in + MT2. */ + +static int +span (MText *mt1, MText *mt2, int pos, MSymbol not) +{ + int nchars = mtext_nchars (mt1); + MCharTable *table = get_charbag (mt2); + int i; + + for (i = pos; i < nchars; i++) + if ((MSymbol) mchartable_lookup (table, mtext_ref_char (mt1, i)) == not) + break; + return (i - pos); +} + + +static int +count_utf_8_chars (void *data, int nitems) +{ + unsigned char *p = (unsigned char *) data; + unsigned char *pend = p + nitems; + int nchars = 0; + + while (p < pend) + { + int i, n; + + for (; p < pend && *p < 128; nchars++, p++); + if (p == pend) + return nchars; + if (! CHAR_HEAD_P_UTF8 (p)) + return -1; + n = CHAR_UNITS_BY_HEAD_UTF8 (*p); + if (p + n > pend) + return -1; + for (i = 1; i < n; i++) + if (CHAR_HEAD_P_UTF8 (p + i)) + return -1; + p += n; + nchars++; + } + return nchars; +} + +static int +count_utf_16_chars (void *data, int nitems, int swap) +{ + unsigned short *p = (unsigned short *) data; + unsigned short *pend = p + nitems; + int nchars = 0; + + while (p < pend) + { + unsigned b; + + for (; p < pend; nchars++, p++) + { + b = swap ? *p & 0xFF : *p >> 8; + + if (b >= 0xD8 && b < 0xE0) + { + if (b >= 0xDC) + return -1; + break; + } + } + if (p == pend) + break; + if (p + 1 == pend) + return -1; + p++; + b = swap ? *p & 0xFF : *p >> 8; + if (b < 0xDC || b >= 0xE0) + return -1; + nchars++; + p++; + } + + return nchars; +} + + +static int +find_char_forward (MText *mt, int from, int to, int c) +{ + int from_byte = POS_CHAR_TO_BYTE (mt, from); + + if (mt->format <= MTEXT_FORMAT_UTF_8) + { + unsigned char *p = mt->data + from_byte; + + while (from < to && STRING_CHAR_ADVANCE_UTF8 (p) != c) from++; + } + else if (mt->format <= MTEXT_FORMAT_UTF_16LE) + { + unsigned short *p = (unsigned short *) (mt->data) + from_byte; + + if (mt->format == default_utf_16) + { + unsigned short *p = (unsigned short *) (mt->data) + from_byte; + + while (from < to && STRING_CHAR_ADVANCE_UTF16 (p) != c) from++; + } + else if (c < 0x10000) + { + c = SWAP_16 (c); + while (from < to && *p != c) + { + from++; + p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2; + } + } + else if (c < 0x110000) + { + int c1 = (c >> 10) + 0xD800; + int c2 = (c & 0x3FF) + 0xDC00; + + c1 = SWAP_16 (c1); + c2 = SWAP_16 (c2); + while (from < to && (*p != c1 || p[1] != c2)) + { + from++; + p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2; + } + } + } + else if (c < 0x110000) + { + unsigned *p = (unsigned *) (mt->data) + from_byte; + unsigned c1 = c; + + if (mt->format != default_utf_32) + c1 = SWAP_32 (c1); + while (from < to && *p++ != c1) from++; + } + + return (from < to ? from : -1); +} + + +static int +find_char_backward (MText *mt, int from, int to, int c) +{ + int to_byte = POS_CHAR_TO_BYTE (mt, to); + + if (mt->format <= MTEXT_FORMAT_UTF_8) + { + unsigned char *p = mt->data + to_byte; + + while (from < to) + { + for (p--; ! CHAR_HEAD_P (p); p--); + if (c == STRING_CHAR (p)) + break; + to--; + } + } + else if (mt->format <= MTEXT_FORMAT_UTF_16LE) + { + unsigned short *p = (unsigned short *) (mt->data) + to_byte; + + if (mt->format == default_utf_16) + { + while (from < to) + { + p--; + if (*p >= 0xDC00 && *p < 0xE000) + p--; + if (c == STRING_CHAR_UTF16 (p)) + break; + to--; + } + } + else if (c < 0x10000) + { + c = SWAP_16 (c); + while (from < to && p[-1] != c) + { + to--; + p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2; + } + } + else if (c < 0x110000) + { + int c1 = (c >> 10) + 0xD800; + int c2 = (c & 0x3FF) + 0xDC00; + + c1 = SWAP_32 (c1); + c2 = SWAP_32 (c2); + while (from < to && (p[-1] != c2 || p[-2] != c1)) + { + to--; + p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2; + } + } + } + else if (c < 0x110000) + { + unsigned *p = (unsigned *) (mt->data) + to_byte; + unsigned c1 = c; + + if (mt->format != default_utf_32) + c1 = SWAP_32 (c1); + while (from < to && p[-1] != c1) to--, p--; + } + + return (from < to ? to - 1 : -1); +} + + +static void +free_mtext (void *object) +{ + MText *mt = (MText *) object; + + if (mt->plist) + mtext__free_plist (mt); + if (mt->data && mt->allocated >= 0) + free (mt->data); + M17N_OBJECT_UNREGISTER (mtext_table, mt); + free (object); +} + +/** Structure for an iterator used in case-fold comparison. */ + +struct casecmp_iterator { + MText *mt; + int pos; + MText *folded; + unsigned char *foldedp; + int folded_len; +}; + +static int +next_char_from_it (struct casecmp_iterator *it) +{ + int c, c1; + + if (it->folded) + { + c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len); + return c; + } + + c = mtext_ref_char (it->mt, it->pos); + c1 = (int) mchar_get_prop (c, Msimple_case_folding); + if (c1 == 0xFFFF) + { + it->folded + = (MText *) mchar_get_prop (c, Mcomplicated_case_folding); + it->foldedp = it->folded->data; + c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len); + return c; + } + + if (c1 >= 0) + c = c1; + return c; +} + +static void +advance_it (struct casecmp_iterator *it) +{ + if (it->folded) + { + it->foldedp += it->folded_len; + if (it->foldedp == it->folded->data + it->folded->nbytes) + it->folded = NULL; + } + if (! it->folded) + { + it->pos++; + } +} + +static int +case_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2) +{ + struct casecmp_iterator it1, it2; + + it1.mt = mt1, it1.pos = from1, it1.folded = NULL; + it2.mt = mt2, it2.pos = from2, it2.folded = NULL; + + while (it1.pos < to1 && it2.pos < to2) + { + int c1 = next_char_from_it (&it1); + int c2 = next_char_from_it (&it2); + + if (c1 != c2) + return (c1 > c2 ? 1 : -1); + advance_it (&it1); + advance_it (&it2); + } + return (it2.pos == to2 ? (it1.pos < to1) : -1); +} + + +/* Internal API */ + +int +mtext__init () +{ + M_charbag = msymbol_as_managing_key (" charbag"); + mtext_table.count = 0; + return 0; +} + + +void +mtext__fini (void) +{ + mdebug__report_object ("M-text", &mtext_table); +} + + +int +mtext__char_to_byte (MText *mt, int pos) +{ + int char_pos, byte_pos; + int forward; + + if (pos < mt->cache_char_pos) + { + if (mt->cache_char_pos == mt->cache_byte_pos) + return pos; + if (pos < mt->cache_char_pos - pos) + { + char_pos = byte_pos = 0; + forward = 1; + } + else + { + char_pos = mt->cache_char_pos; + byte_pos = mt->cache_byte_pos; + forward = 0; + } + } + else + { + if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos) + return (mt->cache_byte_pos + (pos - mt->cache_char_pos)); + if (pos - mt->cache_char_pos < mt->nchars - pos) + { + char_pos = mt->cache_char_pos; + byte_pos = mt->cache_byte_pos; + forward = 1; + } + else + { + char_pos = mt->nchars; + byte_pos = mt->nbytes; + forward = 0; + } + } + if (forward) + while (char_pos < pos) + INC_POSITION (mt, char_pos, byte_pos); + else + while (char_pos > pos) + DEC_POSITION (mt, char_pos, byte_pos); + mt->cache_char_pos = char_pos; + mt->cache_byte_pos = byte_pos; + return byte_pos; +} + +/* mtext__byte_to_char () */ + +int +mtext__byte_to_char (MText *mt, int pos_byte) +{ + int char_pos, byte_pos; + int forward; + + if (pos_byte < mt->cache_byte_pos) + { + if (mt->cache_char_pos == mt->cache_byte_pos) + return pos_byte; + if (pos_byte < mt->cache_byte_pos - pos_byte) + { + char_pos = byte_pos = 0; + forward = 1; + } + else + { + char_pos = mt->cache_char_pos; + byte_pos = mt->cache_byte_pos; + forward = 0; + } + } + else + { + if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos) + return (mt->cache_char_pos + (pos_byte - mt->cache_byte_pos)); + if (pos_byte - mt->cache_byte_pos < mt->nbytes - pos_byte) + { + char_pos = mt->cache_char_pos; + byte_pos = mt->cache_byte_pos; + forward = 1; + } + else + { + char_pos = mt->nchars; + byte_pos = mt->nbytes; + forward = 0; + } + } + if (forward) + while (byte_pos < pos_byte) + INC_POSITION (mt, char_pos, byte_pos); + else + while (byte_pos > pos_byte) + DEC_POSITION (mt, char_pos, byte_pos); + mt->cache_char_pos = char_pos; + mt->cache_byte_pos = byte_pos; + return char_pos; +} + +/* Estimated extra bytes that malloc will use for its own purpose on + each memory allocation. */ +#define MALLOC_OVERHEAD 4 +#define MALLOC_MININUM_BYTES 12 + +void +mtext__enlarge (MText *mt, int nbytes) +{ + nbytes += MAX_UTF8_CHAR_BYTES; + if (mt->allocated >= nbytes) + return; + if (nbytes < MALLOC_MININUM_BYTES) + nbytes = MALLOC_MININUM_BYTES; + while (mt->allocated < nbytes) + mt->allocated = mt->allocated * 2 + MALLOC_OVERHEAD; + MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT); +} + +int +mtext__takein (MText *mt, int nchars, int nbytes) +{ + if (mt->plist) + mtext__adjust_plist_for_insert (mt, mt->nchars, nchars, NULL); + mt->nchars += nchars; + mt->nbytes += nbytes; + mt->data[mt->nbytes] = 0; + return 0; +} + + +int +mtext__cat_data (MText *mt, unsigned char *p, int nbytes, + enum MTextFormat format) +{ + int nchars = -1; + + if (mt->format > MTEXT_FORMAT_UTF_8) + MERROR (MERROR_MTEXT, -1); + if (format == MTEXT_FORMAT_US_ASCII) + nchars = nbytes; + else if (format == MTEXT_FORMAT_UTF_8) + nchars = count_utf_8_chars (p, nbytes); + if (nchars < 0) + MERROR (MERROR_MTEXT, -1); + mtext__enlarge (mt, mtext_nbytes (mt) + nbytes + 1); + memcpy (MTEXT_DATA (mt) + mtext_nbytes (mt), p, nbytes); + mtext__takein (mt, nchars, nbytes); + return nchars; +} + +MText * +mtext__from_data (void *data, int nitems, enum MTextFormat format, + int need_copy) +{ + MText *mt; + int nchars = nitems; + int bytes = nitems; + + if (format == MTEXT_FORMAT_US_ASCII) + { + char *p = (char *) data, *pend = p + nitems; + + while (p < pend) + if (*p++ < 0) + MERROR (MERROR_MTEXT, NULL); + } + else if (format == MTEXT_FORMAT_UTF_8) + { + if ((nchars = count_utf_8_chars (data, nitems)) < 0) + MERROR (MERROR_MTEXT, NULL); + } + else if (format <= MTEXT_FORMAT_UTF_16BE) + { + if ((nchars = count_utf_16_chars (data, nitems, + format != default_utf_16)) < 0) + MERROR (MERROR_MTEXT, NULL); + bytes = sizeof (short) * nitems; + } + else if (format <= MTEXT_FORMAT_UTF_32BE) + { + unsigned *p = (unsigned *) data, *pend = p + nitems; + int swap = format != default_utf_32; + + for (; p < pend; p++) + { + unsigned c = swap ? SWAP_32 (*p) : *p; + + if ((c >= 0xD800 && c < 0xE000) || (c >= 0x110000)) + MERROR (MERROR_MTEXT, NULL); + } + bytes = sizeof (unsigned) * nitems; + } + else + MERROR (MERROR_MTEXT, NULL); + + mt = mtext (); + mt->format = format; + mt->allocated = need_copy ? bytes : -1; + mt->nchars = nchars; + mt->nbytes = nitems; + if (need_copy) + { + mt->data = malloc (bytes + 1); + memcpy (mt->data, data, bytes); + mt->data[bytes] = 0; + } + else + mt->data = (unsigned char *) data; + return mt; +} + + +int +mtext__replace (MText *mt, int from, int to, char *from_str, char *to_str) +{ + int from_byte = POS_CHAR_TO_BYTE (mt, from); + int to_byte = POS_CHAR_TO_BYTE (mt, to); + unsigned char *p = MTEXT_DATA (mt) + from_byte; + unsigned char *endp = MTEXT_DATA (mt) + to_byte; + int from_str_len = strlen (from_str); + int to_str_len = strlen (to_str); + int diff = to_str_len - from_str_len; + unsigned char saved_byte; + int pos, pos_byte; + + if (mtext_nchars (mt) == 0 + || from_str_len == 0) + return 0; + M_CHECK_READONLY (mt, -1); + M_CHECK_RANGE (mt, from, to, -1, 0); + + saved_byte = *endp; + *endp = '\0'; + while ((p = (unsigned char *) strstr ((char *) p, from_str)) != NULL) + { + if (diff < 0) + { + pos_byte = p - MTEXT_DATA (mt); + pos = POS_BYTE_TO_CHAR (mt, pos_byte); + mtext_del (mt, pos, pos - diff); + } + else if (diff > 0) + { + pos_byte = p - MTEXT_DATA (mt); + pos = POS_BYTE_TO_CHAR (mt, pos_byte); + mtext_ins_char (mt, pos, ' ', diff); + /* The above may relocate mt->data. */ + endp += (MTEXT_DATA (mt) + pos_byte) - p; + p = MTEXT_DATA (mt) + pos_byte; + } + memmove (p, to_str, to_str_len); + p += to_str_len; + endp += diff; + } + *endp = saved_byte; + return 0; +} + + +/* Find the position of a character at the beginning of a line of + M-Text MT searching backward from POS. */ + +int +mtext__bol (MText *mt, int pos) +{ + int byte_pos; + + if (pos == 0) + return pos; + byte_pos = POS_CHAR_TO_BYTE (mt, pos); + if (mt->format <= MTEXT_FORMAT_UTF_8) + { + unsigned char *p = mt->data + byte_pos; + + if (p[-1] == '\n') + return pos; + p--; + while (p > mt->data && p[-1] != '\n') + p--; + if (p == mt->data) + return 0; + byte_pos = p - mt->data; + return POS_BYTE_TO_CHAR (mt, byte_pos); + } + else if (mt->format <= MTEXT_FORMAT_UTF_16BE) + { + unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos; + unsigned short newline = mt->format == default_utf_16 ? 0x0A00 : 0x000A; + + if (p[-1] == newline) + return pos; + p--; + while (p > (unsigned short *) (mt->data) && p[-1] != newline) + p--; + if (p == (unsigned short *) (mt->data)) + return 0; + byte_pos = p - (unsigned short *) (mt->data); + return POS_BYTE_TO_CHAR (mt, byte_pos);; + } + else + { + unsigned *p = ((unsigned *) (mt->data)) + byte_pos; + unsigned newline = mt->format == default_utf_32 ? 0x0A000000 : 0x0000000A; + + if (p[-1] == newline) + return pos; + p--, pos--; + while (p > (unsigned *) (mt->data) && p[-1] != newline) + p--, pos--; + return pos; + } +} + + +/* Find the position of a character at the end of a line of M-Text MT + searching forward from POS. */ + +int +mtext__eol (MText *mt, int pos) +{ + int byte_pos; + + if (pos == mt->nchars) + return pos; + byte_pos = POS_CHAR_TO_BYTE (mt, pos); + if (mt->format <= MTEXT_FORMAT_UTF_8) + { + unsigned char *p = mt->data + byte_pos; + unsigned char *endp; + + if (*p == '\n') + return pos + 1; + p++; + endp = mt->data + mt->nbytes; + while (p < endp && *p != '\n') + p++; + if (p == endp) + return mt->nchars; + byte_pos = p + 1 - mt->data; + return POS_BYTE_TO_CHAR (mt, byte_pos); + } + else if (mt->format <= MTEXT_FORMAT_UTF_16BE) + { + unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos; + unsigned short *endp; + unsigned short newline = mt->format == default_utf_16 ? 0x0A00 : 0x000A; + + if (*p == newline) + return pos + 1; + p++; + endp = (unsigned short *) (mt->data) + mt->nbytes; + while (p < endp && *p != newline) + p++; + if (p == endp) + return mt->nchars; + byte_pos = p + 1 - (unsigned short *) (mt->data); + return POS_BYTE_TO_CHAR (mt, byte_pos); + } + else + { + unsigned *p = ((unsigned *) (mt->data)) + byte_pos; + unsigned *endp; + unsigned newline = mt->format == default_utf_32 ? 0x0A000000 : 0x0000000A; + + if (*p == newline) + return pos + 1; + p++, pos++; + endp = (unsigned *) (mt->data) + mt->nbytes; + while (p < endp && *p != newline) + p++, pos++; + return pos; + } +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nMtext */ +/*** @{ */ +/*=*/ + +/***en + @brief Allocate a new M-text. + + The mtext () function allocates a new M-text of length 0 and + returns a pointer to it. The allocated M-text will not be freed + unless the user explicitly does so with the m17n_object_free () + function. */ + +/***ja + @brief ¿·¤·¤¤M-text¤ò³ä¤êÅö¤Æ¤ë + + ´Ø¿ô mtext () ¤Ï¡¢Ä¹¤µ 0 ¤Î¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¡¢¤½¤ì¤Ø¤Î¥Ý¥¤ + ¥ó¥¿¤òÊÖ¤¹¡£³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Ï¡¢´Ø¿ô m17n_object_free () ¤Ë + ¤è¤Ã¤Æ¥æ¡¼¥¶¤¬ÌÀ¼¨Åª¤Ë¹Ô¤Ê¤ï¤Ê¤¤¸Â¤ê¡¢²òÊü¤µ¤ì¤Ê¤¤¡£ + + @latexonly \IPAlabel{mtext} @endlatexonly */ + +/*** + @seealso + m17n_object_free () */ + +MText * +mtext () +{ + MText *mt; + + M17N_OBJECT (mt, free_mtext, MERROR_MTEXT); + mt->format = MTEXT_FORMAT_UTF_8; + M17N_OBJECT_REGISTER (mtext_table, mt); + return mt; +} + +/***en + @brief Allocate a new M-text with specified data. + + The mtext_from_data () function allocates a new M-text whose + character sequence is specified by array $DATA of $NITEMS + elements. $FORMAT specifies the format of $DATA. + + When $FORMAT is either #MTEXT_FORMAT_US_ASCII or + #MTEXT_FORMAT_UTF_8, the contents of $DATA must be of the type @c + unsigned @c char, and $NITEMS counts by byte. + + When $FORMAT is either #MTEXT_FORMAT_UTF_16LE or + #MTEXT_FORMAT_UTF_16BE, the contents of $DATA must be of the type + @c unsigned @c short, and $NITEMS counts by unsigned short. + + When $FORMAT is either #MTEXT_FORMAT_UTF_32LE or + #MTEXT_FORMAT_UTF_32BE, the contents of $DATA must be of the type + @c unsigned, and $NITEMS counts by unsigned. + + The character sequence of the M-text is not modifiable. + The contents of $DATA must not be modified while the M-text is alive. + + The allocated M-text will not be freed unless the user explicitly + does so with the m17n_object_free () function. Even in that case, + $DATA is not freed. + + @return + If the operation was successful, mtext_from_data () returns a + pointer to the allocated M-text. Otherwise it returns @c NULL and + assigns an error code to the external variable #merror_code. */ + +/*** + @errors + @c MERROR_MTEXT */ + +MText * +mtext_from_data (void *data, int nitems, enum MTextFormat format) +{ + if (nitems < 0) + MERROR (MERROR_MTEXT, NULL); + if (nitems == 0) + { + if (format == MTEXT_FORMAT_US_ASCII + || format == MTEXT_FORMAT_UTF_8) + { + unsigned char *p = data; + + while (*p++) nitems++; + } + else if (format <= MTEXT_FORMAT_UTF_16BE) + { + unsigned short *p = data; + + while (*p++) nitems++; + } + else if (format <= MTEXT_FORMAT_UTF_32BE) + { + unsigned *p = data; + + while (*p++) nitems++; + } + else + MERROR (MERROR_MTEXT, NULL); + } + return mtext__from_data (data, nitems, format, 0); +} + +/*=*/ + +/***en + @brief Number of characters in M-text. + + The mtext_len () function returns the number of characters in + M-text $MT. */ + +/***ja + @brief M-text Ãæ¤Îʸ»ú¿ô + + ´Ø¿ô mtext_len () ¤Ï M-text $MT Ãæ¤Îʸ»ú¿ô¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_len} @endlatexonly */ + +int +mtext_len (MText *mt) +{ + return (mt->nchars); +} + +/*=*/ + +/***en + @brief Return the character at the specified position in an M-text. + + The mtext_ref_char () function returns the character at $POS in + M-text $MT. If an error is detected, it returns -1 and assigns an + error code to the external variable #merror_code. */ + +/***ja + @brief M-text Ãæ¤Î»ØÄꤵ¤ì¤¿°ÌÃÖ¤Îʸ»ú¤òÊÖ¤¹ + + ´Ø¿ô mtext_ref_char () ¤Ï¡¢M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤òÊÖ¤¹¡£ + ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code + ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_ref_char} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE */ + +int +mtext_ref_char (MText *mt, int pos) +{ + int c; + + M_CHECK_POS (mt, pos, -1); + if (mt->format <= MTEXT_FORMAT_UTF_8) + { + unsigned char *p = mt->data + POS_CHAR_TO_BYTE (mt, pos); + + c = STRING_CHAR (p); + } + else if (mt->format <= MTEXT_FORMAT_UTF_16BE) + { + unsigned short *p + = (unsigned short *) (mt->data) + POS_CHAR_TO_BYTE (mt, pos); + + if (mt->format == default_utf_16) + c = STRING_CHAR_UTF16 (p); + else + { + c = (*p >> 8) | ((*p & 0xFF) << 8); + if (c >= 0xD800 && c < 0xE000) + { + int c1 = (p[1] >> 8) | ((p[1] & 0xFF) << 8); + c = ((c - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000; + } + } + } + else + { + unsigned *p = (unsigned *) (mt->data) + POS_CHAR_TO_BYTE (mt, pos); + + if (mt->format == default_utf_32) + c = *p; + else + c = SWAP_32 (*p); + } + return c; +} + +/*=*/ + +/***en + @brief Store a character into an M-text. + + The mtext_set_char () function sets character $C, which has no + text properties, at $POS in M-text $MT. + + @return + If the operation was successful, mtext_set_char () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief M-text ¤Ë°ìʸ»ú¤òÀßÄꤹ¤ë + + ´Ø¿ô mtext_set_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò + M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤ËÀßÄꤹ¤ë¡£ + + @return + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mtext_set_char () ¤Ï 0 ¤òÊÖ¤¹¡£¼ºÇÔ¤¹¤ì¤Ð -1 ¤òÊÖ + ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_set_char} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE */ + +int +mtext_set_char (MText *mt, int pos, int c) +{ + int byte_pos; + int bytes_old, bytes_new; + int delta; + unsigned char str[MAX_UTF8_CHAR_BYTES]; + unsigned char *p; + int i; + + M_CHECK_POS (mt, pos, -1); + M_CHECK_READONLY (mt, -1); + + byte_pos = POS_CHAR_TO_BYTE (mt, pos); + p = mt->data + byte_pos; + bytes_old = CHAR_BYTES_AT (p); + bytes_new = CHAR_STRING (c, str); + delta = bytes_new - bytes_old; + + /* mtext__adjust_plist_for_change (mt, pos, pos + 1);*/ + + if (delta) + { + int byte_pos_old = byte_pos + bytes_old; + int byte_pos_new = byte_pos + bytes_new; + + if (mt->cache_char_pos > pos) + mt->cache_byte_pos += delta; + + if ((mt->allocated - mt->nbytes) <= delta) + { + mt->allocated = mt->nbytes + delta + 1; + MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT); + } + + memmove (mt->data + byte_pos_old, mt->data + byte_pos_new, + mt->nbytes - byte_pos_old); + mt->nbytes += delta; + mt->data[mt->nbytes] = 0; + } + for (i = 0; i < bytes_new; i++) + mt->data[byte_pos + i] = str[i]; + return 0; +} + +/*=*/ + +/***en + @brief Append a character to an M-text. + + The mtext_cat_char () function appends character $C, which has no + text properties, to the end of M-text $MT. + + @return + This function returns a pointer to the resulting M-text $MT. If + $C is an invalid character, it returns @c NULL. */ + +/***ja + @brief M-text ¤Ë°ìʸ»úÄɲ乤ë + + ´Ø¿ô mtext_cat_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò + M-text $MT ¤ÎËöÈø¤ËÄɲ乤롣 + + @return + ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£$C ¤¬Àµ¤·¤¤Ê¸ + »ú¤Ç¤Ê¤¤¾ì¹ç¤Ë¤Ï @c NULL ¤òÊÖ¤¹¡£ */ + +/*** + @seealso + mtext_cat (), mtext_ncat () */ + +MText * +mtext_cat_char (MText *mt, int c) +{ + unsigned char buf[MAX_UTF8_CHAR_BYTES]; + int nbytes; + int total_bytes; + + M_CHECK_READONLY (mt, NULL); + if (c < 0 || c > MCHAR_MAX) + return NULL; + nbytes = CHAR_STRING (c, buf); + + total_bytes = mt->nbytes + nbytes; + + mtext__adjust_plist_for_insert (mt, mt->nchars, 1, NULL); + + if (total_bytes >= mt->allocated) + { + mt->allocated = total_bytes + 1; + MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT); + } + memcpy (mt->data + mt->nbytes, buf, nbytes); + mt->nbytes = total_bytes; + mt->nchars++; + mt->data[total_bytes] = 0; + return mt; +} + +/*=*/ + +/***en + @brief Create a copy of an M-text. + + The mtext_dup () function creates a copy of M-text $MT while + inheriting all the text properties of $MT. + + @return + This function returns a pointer to the created copy. */ + +/***ja + @brief M-text ¤Î¥³¥Ô¡¼¤òºî¤ë + + ´Ø¿ô mtext_dup () ¤Ï¡¢M-text $MT ¤Î¥³¥Ô¡¼¤òºî¤ë¡£$MT ¤Î¥Æ¥­¥¹¥È¥× + ¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ïºî¤é¤ì¤¿¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_dup} @endlatexonly */ + +/*** + @seealso + mtext_duplicate () */ + +MText * +mtext_dup (MText *mt) +{ + return copy (mtext (), 0, mt, 0, mt->nchars); +} + +/*=*/ + +/***en + @brief Append an M-text to another. + + The mtext_cat () function appends M-text $MT2 to the end of M-text + $MT1 while inheriting all the text properties. $MT2 itself is not + modified. + + @return + This function returns a pointer to the resulting M-text $MT1. */ + +/***ja + @brief 2¸Ä¤Î M-text¤òÏ¢·ë¤¹¤ë + + ´Ø¿ô mtext_cat () ¤Ï¡¢ M-text $MT2 ¤ò M-text $MT1 ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨ + ¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê + ¤¤¡£ + + @return + ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_cat} @endlatexonly */ + +/*** + @seealso + mtext_ncat (), mtext_cat_char () */ + +MText * +mtext_cat (MText *mt1, MText *mt2) +{ + M_CHECK_READONLY (mt1, NULL); + + return copy (mt1, mt1->nchars, mt2, 0, mt2->nchars); +} + + +/*=*/ + +/***en + @brief Append a part of an M-text to another. + + The mtext_ncat () function appends the first $N characters of + M-text $MT2 to the end of M-text $MT1 while inheriting all the + text properties. If the length of $MT2 is less than $N, all + characters are copied. $MT2 is not modified. + + @return + If the operation was successful, mtext_ncat () returns a pointer + to the resulting M-text $MT1. If an error is detected, it returns + @c NULL and assigns an error code to the global variable @c + merror_code. */ + + +/***ja + @brief M-text ¤Î°ìÉô¤òÊ̤ΠM-text ¤ËÉղ乤ë + + ´Ø¿ô mtext_ncat () ¤Ï¡¢M-text $MT2 ¤Î¤Ï¤¸¤á¤Î $N ʸ»ú¤ò M-text + $MT1 ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì + ¤ë¡£$MT2 ¤ÎŤµ¤¬ $N °Ê²¼¤Ê¤é¤Ð¡¢$MT2 ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤¬Éղ䵤ì¤ë¡£ + $N ¤¬Éé¤Î¾ì¹ç¡¢$MT1 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + $MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncat () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý + ¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c + merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_ncat} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_cat (), mtext_cat_char () */ + +MText * +mtext_ncat (MText *mt1, MText *mt2, int n) +{ + M_CHECK_READONLY (mt1, NULL); + if (n < 0) + MERROR (MERROR_RANGE, NULL); + return copy (mt1, mt1->nchars, mt2, 0, mt2->nchars < n ? mt2->nchars : n); +} + + +/*=*/ + +/***en + @brief Copy an M-text to another. + + The mtext_cpy () function copies M-text $MT2 to M-text $MT1 while + inheriting all the text properties. The old text in $MT1 is + overwritten and the length of $MT1 is extended if necessary. $MT2 + is not modified. + + @return + This function returns a pointer to the resulting M-text $MT1. */ + +/***ja + @brief M-text ¤ò¥³¥Ô¡¼¤¹¤ë + + ´Ø¿ô mtext_cpy () ¤Ï M-text $MT2 ¤ò M-text $MT1 ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£ + $MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 ¤ÎŤµ¤ÏɬÍפ˱þ + ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + + @return + ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_cpy} @endlatexonly */ + +/*** + @seealso + mtext_ncpy (), mtext_copy () */ + +MText * +mtext_cpy (MText *mt1, MText *mt2) +{ + M_CHECK_READONLY (mt1, NULL); + return copy (mt1, 0, mt2, 0, mt2->nchars); +} + +/*=*/ + +/***en + @brief Copy the first some characters in an M-text to another. + + The mtext_ncpy () function copies the first $N characters of + M-text $MT2 to M-text $MT1 while inheriting all the text + properties. If the length of $MT2 is less than $N, all characters + of $MT2 are copied. The old text in $MT1 is overwritten and the + length of $MT1 is extended if necessary. $MT2 is not modified. + + @return + If the operation was successful, mtext_ncpy () returns a pointer + to the resulting M-text $MT1. If an error is detected, it returns + @c NULL and assigns an error code to the global variable @c + merror_code. */ + +/***ja + @brief M-text ¤Ë´Þ¤Þ¤ì¤ëºÇ½é¤Î²¿Ê¸»ú¤«¤ò¥³¥Ô¡¼¤¹¤ë + + ´Ø¿ô mtext_ncpy () ¤Ï¡¢M-text $MT2 ¤ÎºÇ½é¤Î $N ʸ»ú¤ò M-text $MT1 + ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£¤â¤· $MT2 ¤ÎŤµ¤¬ $N ¤è¤ê¤â¾®¤µ¤±¤ì¤Ð $MT2 ¤Î + ¤¹¤Ù¤Æ¤Îʸ»ú¤ò¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ + ¤ì¤ë¡£$MT1 ¤ÎŤµ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncpy () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text$MT1 ¤Ø¤Î¥Ý¥¤ + ¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c + merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_ncpy} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_cpy (), mtext_copy () */ + +MText * +mtext_ncpy (MText *mt1, MText *mt2, int n) +{ + M_CHECK_READONLY (mt1, NULL); + if (n < 0) + MERROR (MERROR_RANGE, NULL); + return (copy (mt1, 0, mt2, 0, mt2->nchars < n ? mt2->nchars : n)); +} + +/*=*/ + +/***en + @brief Create a new M-text from a part of an existing M-text. + + The mtext_duplicate () function creates a copy of sub-text of + M-text $MT, starting at $FROM (inclusive) and ending at $TO + (exclusive) while inheriting all the text properties of $MT. $MT + itself is not modified. + + @return + If the operation was successful, mtext_duplicate () returns a + pointer to the created M-text. If an error is detected, it returns 0 + and assigns an error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤Î°ìÉô¤«¤é¿·¤·¤¤ M-text ¤ò¤Ä¤¯¤ë + + ´Ø¿ô mtext_duplicate () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO + ¡Ê´Þ¤Þ¤Ê¤¤¡Ë¤Þ¤Ç¤ÎÉôʬʸ»úÎó¤Î¥³¥Ô¡¼¤òºî¤ë¡£¤³¤Î¤È¤­ $MT ¤Î¥Æ¥­¥¹ + ¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT ¤½¤Î¤â¤Î¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_duplicate () ¤Ïºî¤é¤ì¤¿ M-text ¤Ø¤Î¥Ý¥¤¥ó + ¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code + ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_duplicate} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_dup () */ + +MText * +mtext_duplicate (MText *mt, int from, int to) +{ + MText *new = mtext (); + + M_CHECK_RANGE (mt, from, to, NULL, new); + return copy (new, 0, mt, from, to); +} + +/*=*/ + +/***en + @brief Copy characters in the specified range into an M-text. + + The mtext_copy () function copies the text between $FROM + (inclusive) and $TO (exclusive) in M-text $MT2 to the region + starting at $POS in M-text $MT1 while inheriting the text + properties. The old text in $MT1 is overwritten and the length of + $MT1 is extended if necessary. $MT2 is not modified. + + @return + If the operation was successful, mtext_copy () returns a pointer + to the modified $MT1. Otherwise, it returns @c NULL and assigns + an error code to the external variable #merror_code. */ + +/***ja + @brief M-text ¤Î»ØÄêÈϰϤÎʸ»ú¤ò¥³¥Ô¡¼¤¹¤ë + + ´Ø¿ô mtext_copy () ¤Ï¡¢ M-text $MT2 ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO ¡Ê´Þ + ¤Þ¤Ê¤¤¡Ë¤Þ¤Ç¤ÎÈϰϤΥƥ­¥¹¥È¤ò M-text $MT1 ¤Î°ÌÃÖ $POS ¤«¤é¾å½ñ¤­ + ¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 ¤ÎĹ + ¤µ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£ + + @latexonly \IPAlabel{mtext_copy} @endlatexonly + + @return + ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_copy () ¤ÏÊѹ¹¤µ¤ì¤¿ $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò + ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼ + ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_cpy (), mtext_ncpy () */ + +MText * +mtext_copy (MText *mt1, int pos, MText *mt2, int from, int to) +{ + M_CHECK_POS_X (mt1, pos, NULL); + M_CHECK_READONLY (mt1, NULL); + M_CHECK_RANGE (mt2, from, to, NULL, mt1); + return copy (mt1, pos, mt2, from, to); +} + +/*=*/ + + +/***en + @brief Delete characters in the specified range destructively. + + The mtext_del () function deletes the characters in the range + $FROM (inclusive) and $TO (exclusive) from M-text $MT + destructively. As a result, the length of $MT shrinks by ($TO - + $FROM) characters. + + @return + If the operation was successful, mtext_del () returns 0. + Otherwise, it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief »ØÄêÈϰϤÎʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯ + + ´Ø¿ô mtext_del () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO ¡Ê´Þ¤Þ + ¤Ê¤¤¡Ë¤Þ¤Ç¤Îʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯¡£·ë²ÌŪ¤Ë $MT ¤ÏŤµ¤¬ ($TO @c + - $FROM) ¤À¤±½Ì¤à¤³¤È¤Ë¤Ê¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_del () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ + ¤·¡¢Æ±»þ¤Ë³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_ins () */ + +int +mtext_del (MText *mt, int from, int to) +{ + int from_byte, to_byte; + + M_CHECK_READONLY (mt, -1); + M_CHECK_RANGE (mt, from, to, -1, 0); + + from_byte = POS_CHAR_TO_BYTE (mt, from); + to_byte = POS_CHAR_TO_BYTE (mt, to); + + if (mt->cache_char_pos >= to) + { + mt->cache_char_pos -= to - from; + mt->cache_byte_pos -= to_byte - from_byte; + } + else if (mt->cache_char_pos > from) + { + mt->cache_char_pos -= from; + mt->cache_byte_pos -= from_byte; + } + + mtext__adjust_plist_for_delete (mt, from, to - from); + memmove (mt->data + from_byte, mt->data + to_byte, mt->nbytes - to_byte + 1); + mt->nchars -= (to - from); + mt->nbytes -= (to_byte - from_byte); + mt->cache_char_pos = from; + mt->cache_byte_pos = from_byte; + return 0; +} + + +/*=*/ + +/***en + @brief Insert an M-text into another M-text. + + The mtext_ins () function inserts M-text $MT2 into M-text $MT1, at + position $POS. As a result, $MT1 is lengthen by the length of + $MT2. On insertion, all the text properties of $MT2 are + inherited. The original $MT2 is not modified. + + @return + If the operation was successful, mtext_ins () returns 0. + Otherwise, it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief M-text ¤òÊ̤ΠM-text ¤ËÁÞÆþ¤¹¤ë + + ´Ø¿ô mtext_ins () ¤Ï M-text $MT1 ¤Î $POS ¤Î°ÌÃÖ¤Ë Ê̤ΠM-text $MT2 + ¤òÁÞÆþ¤¹¤ë¡£¤³¤Î·ë²Ì $MT1 ¤ÎŤµ¤Ï $MT2 ¤Î¤Ö¤ó¤À¤±Áý¤¨¤ë¡£ÁÞÆþ¤ÎºÝ¡¢ + $MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤½¤Î¤â¤Î¤ÏÊѹ¹¤µ + ¤ì¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_ins () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ + ¤·¡¢Æ±»þ¤Ë³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_del () */ + +int +mtext_ins (MText *mt1, int pos, MText *mt2) +{ + int byte_pos; + int total_bytes; + + M_CHECK_READONLY (mt1, -1); + M_CHECK_POS_X (mt1, pos, -1); + + if (mt2->nchars == 0) + return 0; + mtext__adjust_plist_for_insert + (mt1, pos, mt2->nchars, + mtext__copy_plist (mt2->plist, 0, mt2->nchars, mt1, pos)); + + total_bytes = mt1->nbytes + mt2->nbytes; + if (total_bytes >= mt1->allocated) + { + mt1->allocated = total_bytes + 1; + MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT); + } + byte_pos = POS_CHAR_TO_BYTE (mt1, pos); + if (mt1->cache_char_pos > pos) + { + mt1->cache_char_pos += mt2->nchars; + mt1->cache_byte_pos += mt2->nbytes; + } + memmove (mt1->data + byte_pos + mt2->nbytes, mt1->data + byte_pos, + mt1->nbytes - byte_pos + 1); + memcpy (mt1->data + byte_pos, mt2->data, mt2->nbytes); + mt1->nbytes += mt2->nbytes; + mt1->nchars += mt2->nchars; + return 0; +} + + +int +mtext_ins_char (MText *mt, int pos, int c, int n) +{ + int byte_pos; + int nbytes, total_bytes; + unsigned char *buf; + int i; + + M_CHECK_READONLY (mt, -1); + M_CHECK_POS_X (mt, pos, -1); + if (c < 0 || c > MCHAR_MAX) + MERROR (MERROR_MTEXT, -1); + if (n <= 0) + return 0; + mtext__adjust_plist_for_insert (mt, pos, n, NULL); + buf = alloca (MAX_UTF8_CHAR_BYTES * n); + for (i = 0, nbytes = 0; i < n; i++) + nbytes += CHAR_STRING (c, buf + nbytes); + total_bytes = mt->nbytes + nbytes; + if (total_bytes >= mt->allocated) + { + mt->allocated = total_bytes + 1; + MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT); + } + byte_pos = POS_CHAR_TO_BYTE (mt, pos); + if (mt->cache_char_pos > pos) + { + mt->cache_char_pos++; + mt->cache_byte_pos += nbytes; + } + memmove (mt->data + byte_pos + nbytes, mt->data + byte_pos, + mt->nbytes - byte_pos + 1); + memcpy (mt->data + byte_pos, buf, nbytes); + mt->nbytes += nbytes; + mt->nchars += n; + return 0; +} + +/*=*/ + +/***en + @brief Search a character in an M-text. + + The mtext_character () function searches M-text $MT for character + $C. If $FROM < $TO, search begins at position $FROM and goes + forward but does not exceed ($TO - 1). Otherwise, search begins + at position ($FROM - 1) and goes backward but does not exceed $TO. + An invalid position specification is regarded as both $FROM and + $TO being 0. + + @return + If $C is found, mtext_character () returns the position of its + first occurrence. Otherwise it returns -1 without changing the + external variable #merror_code. If an error is detected, it returns -1 and + assigns an error code to the external variable #merror_code. */ + +/***ja + @brief M-text Ãæ¤ÎÆÃÄê¤Îʸ»ú¤òõ¤¹ + + ´Ø¿ô mtext_character () ¤Ï M-text $MT Ãæ¤Ë¤ª¤±¤ëʸ»ú $C ¤Î½Ð¸½°ÌÃÖ + ¤òÄ´¤Ù¤ë¡£¤â¤· $FROM < $TO ¤Ê¤é¤Ð¡¢Ãµº÷¤Ï°ÌÃÖ $FROM ¤«¤éËöÈøÊý¸þ¤Ø¡¢ + ºÇÂç ($TO - 1) ¤Þ¤Ç¿Ê¤à¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð°ÌÃÖ ($FROM - 1) ¤«¤éÀèƬÊý + ¸þ¤Ø¡¢ºÇÂç $TO ¤Þ¤Ç¿Ê¤à¡£°ÌÃ֤λØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM ¤È + $TO ¤ÎξÊý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£ + + @return + ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_character () ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ + ¤¹¡£¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code ¤òÊѹ¹¤»¤º¤Ë -1 ¤òÊÖ + ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼ + ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @seealso + mtext_chr(), mtext_rchr () */ + +int +mtext_character (MText *mt, int from, int to, int c) +{ + if (from < to) + { + /* We do not use M_CHECK_RANGE () because this function should + not set merror_code. */ + if (from < 0 || to > mt->nchars) + return -1; + return find_char_forward (mt, from, to, c); + } + else + { + /* ditto */ + if (to < 0 || from > mt->nchars) + return -1; + return find_char_backward (mt, to, from, c); + } +} + + +/*=*/ + +/***en + @brief Return the position of the first occurrence of a + character in an M-text. + + The mtext_chr () function searches M-text $MT for character $C. + Search starts from the beginning of $MT and goes toward the end. + + @return + If $C is found, mtext_chr () returns its position; otherwise it + returns. */ + +/***ja + @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹ + + ´Ø¿ô mtext_chr () ¤Ï M-text $MT Ãæ¤Ë¤ª¤±¤ëʸ»ú $C ¤Î½Ð¸½°ÌÃÖ¤òÄ´¤Ù + ¤ë¡£Ãµº÷¤Ï $MT ¤ÎÀèƬ¤«¤éËöÈøÊý¸þ¤Ë¿Ê¤à¡£ + + @return + ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_chr () ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é + ¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_chr} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_rchr (), mtext_character () */ + +int +mtext_chr (MText *mt, int c) +{ + return find_char_forward (mt, 0, mt->nchars, c); +} + +/*=*/ + +/***en + @brief Return the position of the last occurrence of a + character in an M-text. + + The mtext_rchr () function searches M-text $MT for character $C. + Search starts from the end of $MT and goes backwardly toward the + beginning. + + @return + If $C is found, mtext_chr () returns its position; otherwise it + returns -1. */ + +/***ja + @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ¸å¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹ + + ´Ø¿ô mtext_rchr () ¤Ï M-text $MT Ãæ¤Ë¤ª¤±¤ëʸ»ú $C ¤Î½Ð¸½°ÌÃÖ¤òÄ´ + ¤Ù¤ë¡£Ãµº÷¤Ï $MT ¤ÎºÇ¸å¤«¤éÀèƬÊý¸þ¤Ø¤È¸å¸þ¤­¤Ë¿Ê¤à¡£ + + @return + ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_chr () ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é + ¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_rchr} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_chr (), mtext_character () */ + +int +mtext_rchr (MText *mt, int c) +{ + return find_char_backward (mt, mt->nchars, 0, c); +} + + +/*=*/ + +/***en + @brief Compare two M-texts character-by-character. + + The mtext_cmp () function compares M-texts $MT1 and $MT2 character + by character. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. Comparison is based on + character codes. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_cmp () ¤Ï¡¢ M-text $MT1 ¤È $MT2 ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å + ¤¯¡£ + + @latexonly \IPAlabel{mtext_cmp} @endlatexonly */ + +/*** + @seealso + mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (), + mtext_compare (), mtext_case_compare () */ + +int +mtext_cmp (MText *mt1, MText *mt2) +{ + return compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars); +} + + +/*=*/ + +/***en + @brief Compare two M-texts character-by-character. + + The mtext_ncmp () function is similar to mtext_cmp (), but + compares at most $N characters from the beginning. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_ncmp () ¤Ï¡¢´Ø¿ô mtext_cmp () ƱÍͤΠM-text Ʊ»Î¤ÎÈæ³Ó + ¤òÀèƬ¤«¤éºÇÂç $N ʸ»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_ncmp} @endlatexonly */ + +/*** + @seealso + mtext_cmp (), mtext_casecmp (), mtext_ncasecmp () + mtext_compare (), mtext_case_compare () */ + +int +mtext_ncmp (MText *mt1, MText *mt2, int n) +{ + if (n < 0) + return 0; + return compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n), + mt2, 0, (mt2->nchars < n ? mt2->nchars : n)); +} + +/*=*/ + +/***en + @brief Compare two M-texts. + + The mtext_compare () function compares two M-texts $MT1 and $MT2, + character-by-character. The compared regions are between $FROM1 + and $TO1 in $MT1 and $FROM2 to $TO2 in MT2. $FROM1 and $FROM2 are + inclusive, $TO1 and $TO2 are exclusive. $FROM1 being equal to + $TO1 (or $FROM2 being equal to $TO2) means an M-text of length + zero. An invalid region specification is regarded as both $FROM1 + and $TO1 (or $FROM2 and $TO2) being 0. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. Comparison is based on + character codes. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤òÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 ¤òʸ»úñ°Ì¤ÇÈæ + ³Ó¤¹¤ë¡£Èæ³ÓÂоݤȤʤë¤Î¤Ï $MT1 ¤Ç¤Ï $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¡¢$MT2 + ¤Ç¤Ï $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£$FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 + ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë + ¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì + ¹ç¤Ï¡¢$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë ξÊý¤Ë 0 ¤¬»ØÄꤵ + ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð 1 ¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð + ¤Å¤¯¡£ */ + +/*** + @seealso + mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (), + mtext_case_compare () */ + +int +mtext_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2) +{ + if (from1 < 0 || from1 > to1 || to1 > mt1->nchars) + from1 = to1 = 0; + + if (from2 < 0 || from2 > to2 || to2 > mt2->nchars) + from2 = to2 = 0; + + return compare (mt1, from1, to1, mt2, from2, to2); +} + +/*=*/ + +/***en + @brief Search an M-text for a set of characters. + + The mtext_spn () function returns the length of the initial + segment of M-text $MT1 that consists entirely of characters in + M-text $MT2. */ + +/***ja + @brief ¤¢¤ëʸ»ú¤Î½¸¹ç¤ò M-text ¤ÎÃæ¤Çõ¤¹ + + ´Ø¿ô mtext_spn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬÉôʬ¤Ç M-text $MT2 ¤Ë´Þ¤Þ + ¤ì¤ëʸ»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎºÇÂ獵¤ò·×»»¤¹¤ë¡£ + + @latexonly \IPAlabel{mtext_spn} @endlatexonly */ + +/*** + @seealso + mtext_cspn () */ + +int +mtext_spn (MText *mt, MText *accept) +{ + return span (mt, accept, 0, Mnil); +} + +/*=*/ + +/***en + @brief Search an M-text for the complement of a set of characters. + + The mtext_cspn () returns the length of the initial segment of + M-text $MT1 that consists entirely of characters not in M-text $MT2. */ + +/***ja + @brief ¤¢¤ë½¸¹ç¤Ë°¤µ¤Ê¤¤Ê¸»ú¤ò M-text ¤ÎÃæ¤Çõ¤¹ + + ´Ø¿ô mtext_cspn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬÉôʬ¤Ç M-text $MT2 ¤Ë´Þ¤Þ + ¤ì¤Ê¤¤Ê¸»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎºÇÂ獵¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_cspn} @endlatexonly */ + +/*** + @seealso + mtext_spn () */ + +int +mtext_cspn (MText *mt, MText *reject) +{ + return span (mt, reject, 0, Mt); +} + +/*=*/ + +/***en + @brief Search an M-text for any of a set of characters + + The mtext_pbrk () function locates the first occurrence in M-text + $MT1 of any of the characters in M-text $MT2. + + @return + This function returns the position in $MT1 of the found character. + If no such character is found, it returns -1. */ + +/***ja + @brief Ê̤ΠM-text ¤Ë´Þ¤Þ¤ì¤ëʸ»ú¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤ò¸«¤Ä¤±¤ë + + ´Ø¿ô mtext_pbrk () ¤Ï¡¢M-text $MT1 Ãæ¤Ç M-text $MT2 ¤Î¤¤¤º¤ì¤«¤Îʸ + »ú¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÄ´¤Ù¤ë¡£ + + @return + ¸«¤Ä¤«¤Ã¤¿Ê¸»ú¤Î¡¢$MT1 Æâ¤Ë¤ª¤±¤ë½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¤â¤·¤½¤Î¤è¤¦¤Êʸ + »ú¤¬¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_pbrk} @endlatexonly */ + +int +mtext_pbrk (MText *mt, MText *accept) +{ + int nchars = mtext_nchars (mt); + int len = span (mt, accept, 0, Mt); + + return (len == nchars ? -1 : len); +} + +/*=*/ + +/***en + @brief Look for a token in an M-text + + The mtext_tok () function searches a token that firstly occurs + after position $POS in M-text $MT. Here, a token means a + substring each of which does not appear in M-text $DELIM. Note + that the type of $POS is not @c int but pointer to @c int. + + @return + If a token is found, mtext_tok () copies the corresponding part of + $MT and returns a pointer to the copy. In this case, $POS is set + to the end of the found token. If no token is found, it returns + @c NULL without changing the external variable #merror_code. If an + error is detected, it returns @c NULL and assigns an error code + to the external variable #merror_code. */ + +/***ja + @brief M-text Ãæ¤Î¥È¡¼¥¯¥ó¤òõ¤¹ + + ´Ø¿ô mtext_tok () ¤Ï¡¢M-text $MT ¤ÎÃæ¤Ç $POS °Ê¹ßºÇ½é¤Ë¸½¤ì¤ë¥È¡¼ + ¥¯¥ó¤òõ¤¹¡£¤³¤³¤Ç¥È¡¼¥¯¥ó¤È¤Ï M-text $DELIM ¤ÎÃæ¤Ë¸½¤ï¤ì¤Ê¤¤Ê¸»ú + ¤À¤±¤«¤é¤Ê¤ëÉôʬʸ»úÎó¤Ç¤¢¤ë¡£$POS ¤Î·¿¤¬ @c int ¤Ç¤Ï¤Ê¤¯¤Æ @c int + ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¤³¤È¤ËÃí°Õ¡£ + + @return + ¤â¤·¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤ì¤Ð mtext_tok ()¤Ï¤½¤Î¥È¡¼¥¯¥ó¤ËÁêÅö¤¹¤ëÉôʬ + ¤Î $MT ¤ò¥³¥Ô¡¼¤·¡¢¤½¤Î¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¡¢$POS ¤Ï + ¸«¤Ä¤«¤Ã¤¿¥È¡¼¥¯¥ó¤Î½ªÃ¼¤Ë¥»¥Ã¥È¤µ¤ì¤ë¡£¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿ + ¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code ¤òÊѤ¨¤º¤Ë @c NULL ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð + ¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢ÊÑÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_tok} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE */ + +MText * +mtext_tok (MText *mt, MText *delim, int *pos) +{ + int nchars = mtext_nchars (mt); + int pos2; + + M_CHECK_POS (mt, *pos, NULL); + + /* + Skip delimiters starting at POS in MT. + Never do *pos += span(...), or you will change *pos + even though no token is found. + */ + pos2 = *pos + span (mt, delim, *pos, Mnil); + + if (pos2 == nchars) + return NULL; + + *pos = pos2 + span (mt, delim, pos2, Mt); + return (copy (mtext (), 0, mt, pos2, *pos)); +} + +/*=*/ + +/***en + @brief Locate an M-text in another. + + The mtext_text () function finds the first occurrence of M-text + $MT2 in M-text $MT1 after the position $POS while ignoring + difference of the text properties. + + @return + If $MT2 is found in $MT1, mtext_text () returns the position of it + first occurrence. Otherwise it returns -1. If $MT2 is empty, it + returns 0. */ + +/***ja + @brief M-text Ãæ¤ÎÊ̤ΠM-text ¤òõ¤¹ + + ´Ø¿ô mtext_text () ¤Ï¡¢M-text $MT1 Ãæ¤Ë¤ª¤±¤ë M-text $MT2 ¤ÎºÇ½é¤Î + ½Ð¸½°ÌÃÖ¤òÄ´¤Ù¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ã¤¤¤Ï̵»ë¤µ¤ì¤ë¡£ + + @return + $MT1 Ãæ¤Ë $MT2 ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_text() ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ + ¤¹¡£¸«¤Ä¤«¤é¤Ê¤¤¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£¤â¤· $MT2 ¤¬¶õ¤Ê¤é¤Ð 0 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_text} @endlatexonly */ + +int +mtext_text (MText *mt1, int pos, MText *mt2) +{ + int from = pos; + int pos_byte = POS_CHAR_TO_BYTE (mt1, pos); + int c = mtext_ref_char (mt2, 0); + int nbytes1 = mtext_nbytes (mt1); + int nbytes2 = mtext_nbytes (mt2); + int limit; + int use_memcmp = (mt1->format == mt2->format + || (mt1->format < MTEXT_FORMAT_UTF_8 + && mt2->format == MTEXT_FORMAT_UTF_8)); + int unit_bytes = (mt1->format <= MTEXT_FORMAT_UTF_8 ? 1 + : mt1->format <= MTEXT_FORMAT_UTF_16BE ? 2 + : 4); + + if (nbytes2 > pos_byte + nbytes1) + return -1; + pos_byte = nbytes1 - nbytes2; + limit = POS_BYTE_TO_CHAR (mt1, pos_byte); + + while (1) + { + if ((pos = mtext_character (mt1, from, limit, c)) < 0) + return -1; + pos_byte = POS_CHAR_TO_BYTE (mt1, pos); + if (use_memcmp + ? ! memcmp (mt1->data + pos_byte * unit_bytes, + mt2->data, nbytes2 * unit_bytes) + : ! compare (mt1, pos, mt2->nchars, mt2, 0, mt2->nchars)) + break; + from = pos + 1; + } + return pos; +} + +int +mtext_search (MText *mt1, int from, int to, MText *mt2) +{ + int c = mtext_ref_char (mt2, 0); + int from_byte; + int nbytes2 = mtext_nbytes (mt2); + + if (mt1->format > MTEXT_FORMAT_UTF_8 + || mt2->format > MTEXT_FORMAT_UTF_8) + MERROR (MERROR_MTEXT, -1); + + if (from < to) + { + to -= mtext_nchars (mt2); + if (from > to) + return -1; + while (1) + { + if ((from = find_char_forward (mt1, from, to, c)) < 0) + return -1; + from_byte = POS_CHAR_TO_BYTE (mt1, from); + if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2)) + break; + from++; + } + } + else if (from > to) + { + from -= mtext_nchars (mt2); + if (from < to) + return -1; + while (1) + { + if ((from = find_char_backward (mt1, from, to, c)) < 0) + return -1; + from_byte = POS_CHAR_TO_BYTE (mt1, from); + if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2)) + break; + from--; + } + } + + return from; +} + +/*=*/ + +/***en + @brief Compare two M-texts ignoring cases. + + The mtext_casecmp () function is similar to mtext_cmp (), but + ignores cases on comparison. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_casecmp () ¤Ï¡¢´Ø¿ô mtext_cmp () ƱÍͤΠM-text Ʊ»Î¤ÎÈæ + ³Ó¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤Æ¹Ô¤Ê¤¦¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_casecmp} @endlatexonly */ + +/*** + @seealso + mtext_cmp (), mtext_ncmp (), mtext_ncasecmp () + mtext_compare (), mtext_case_compare () */ + +int +mtext_casecmp (MText *mt1, MText *mt2) +{ + return case_compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars); +} + +/*=*/ + +/***en + @brief Compare two M-texts ignoring cases. + + The mtext_ncasecmp () function is similar to mtext_casecmp (), but + compares at most $N characters from the beginning. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_ncasecmp () ¤Ï¡¢´Ø¿ô mtext_casecmp () ƱÍͤΠM-text Ʊ + »Î¤ÎÈæ³Ó¤òÀèƬ¤«¤éºÇÂç $N ʸ»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£ + + @latexonly \IPAlabel{mtext_ncasecmp} @endlatexonly */ + +/*** + @seealso + mtext_cmp (), mtext_casecmp (), mtext_casecmp () + mtext_compare (), mtext_case_compare () */ + +int +mtext_ncasecmp (MText *mt1, MText *mt2, int n) +{ + if (n < 0) + return 0; + return case_compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n), + mt2, 0, (mt2->nchars < n ? mt2->nchars : n)); +} + +/*=*/ + +/***en + @brief Compare two M-texts ignoring cases. + + The mtext_case_compare () function compares two M-texts $MT1 and + $MT2, character-by-character, ignoring cases. The compared + regions are between $FROM1 and $TO1 in $MT1 and $FROM2 to $TO2 in + MT2. $FROM1 and $FROM2 are inclusive, $TO1 and $TO2 are + exclusive. $FROM1 being equal to $TO1 (or $FROM2 being equal to + $TO2) means an M-text of length zero. An invalid region + specification is regarded as both $FROM1 and $TO1 (or $FROM2 and + $TO2) being 0. + + @return + This function returns 1, 0, or -1 if $MT1 is found greater than, + equal to, or less than $MT2, respectively. Comparison is based on + character codes. */ + +/***ja + @brief Æó¤Ä¤Î M-text ¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤¿Ê¸»úñ°Ì¤ÇÈæ³Ó¤¹¤ë + + ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 ¤ò¡¢Âçʸ»ú¡¿¾® + ʸ»ú¤Î¶èÊ̤ò̵»ë¤·¤Ä¤Äʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£Èæ³ÓÂоݤȤʤë¤Î¤Ï $MT1 + ¤Ç¤Ï $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¡¢$MT2 ¤Ç¤Ï $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£ + $FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È + $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text + ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï + $FROM2 ¤È $TO2 ¡ËξÊý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£ + + @return + ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì + ¤Ð1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð-1¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å¤¯¡£ + + @latexonly \IPAlabel{mtext_case_compare} @endlatexonly +*/ + +/*** + @seealso + mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (), + mtext_compare () */ + +int +mtext_case_compare (MText *mt1, int from1, int to1, + MText *mt2, int from2, int to2) +{ + if (from1 < 0 || from1 > to1 || to1 > mt1->nchars) + from1 = to1 = 0; + + if (from2 < 0 || from2 > to2 || to2 > mt2->nchars) + from2 = to2 = 0; + + return case_compare (mt1, from1, to1, mt2, from2, to2); +} + +/*** @} */ + +#include + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump an M-text + + The mdebug_dump_mtext () function prints the M-text $MT in a human + readable way to the stderr. $INDENT specifies how many columns to + indent the lines but the first one. If $FULLP is zero, this + function prints only a character code sequence. Otherwise, it + prints the internal byte sequence and text properties as well. + + @return + This function returns $MT. */ + +MText * +mdebug_dump_mtext (MText *mt, int indent, int fullp) +{ + char *prefix = (char *) alloca (indent + 1); + int i; + unsigned char *p; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + if (! fullp) + { + fprintf (stderr, "\""); + for (i = 0; i < mt->nbytes; i++) + { + int c = mt->data[i]; + if (c >= ' ' && c < 127) + fprintf (stderr, "%c", c); + else + fprintf (stderr, "\\x%02X", c); + } + fprintf (stderr, "\""); + return mt; + } + + fprintf (stderr, + "(mtext (size %d %d %d) (cache %d %d)", + mt->nchars, mt->nbytes, mt->allocated, + mt->cache_char_pos, mt->cache_byte_pos); + if (mt->nchars > 0) + { + fprintf (stderr, "\n%s (bytes \"", prefix); + for (i = 0; i < mt->nbytes; i++) + fprintf (stderr, "\\x%02x", mt->data[i]); + fprintf (stderr, "\")\n"); + fprintf (stderr, "%s (chars \"", prefix); + p = mt->data; + for (i = 0; i < mt->nchars; i++) + { + int len; + int c = STRING_CHAR_AND_BYTES (p, len); + + if (c >= ' ' && c < 127 && c != '\\' && c != '"') + fputc (c, stderr); + else + fprintf (stderr, "\\x%X", c); + p += len; + } + fprintf (stderr, "\")"); + if (mt->plist) + { + fprintf (stderr, "\n%s ", prefix); + dump_textplist (mt->plist, indent + 1); + } + } + fprintf (stderr, ")"); + return mt; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/mtext.h b/src/mtext.h new file mode 100644 index 0000000..75cfd7b --- /dev/null +++ b/src/mtext.h @@ -0,0 +1,68 @@ +/* mtext.h -- header file for the M-text module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_MTEXT_H_ +#define _M17N_MTEXT_H_ + +/** @file mtext.h + @brief Header for M-text handling. +*/ + +#define POS_CHAR_TO_BYTE(mt, pos) \ + (mtext_nchars (mt) == mtext_nbytes (mt) ? (pos) \ + : (pos) == (mt)->cache_char_pos ? (mt)->cache_byte_pos \ + : mtext__char_to_byte ((mt), (pos))) + +#define POS_BYTE_TO_CHAR(mt, pos_byte) \ + (mtext_nchars (mt) == mtext_nbytes (mt) ? (pos_byte) \ + : (pos_byte) == (mt)->cache_byte_pos ? (mt)->cache_char_pos \ + : mtext__byte_to_char ((mt), (pos_byte))) + + +#define MTEXT_DATA(mt) ((mt)->data) + +extern int mtext__char_to_byte (MText *mt, int pos); + +extern int mtext__byte_to_char (MText *mt, int pos_byte); + +extern void mtext__enlarge (MText *mt, int nbytes); + +extern int mtext__takein (MText *mt, int nchars, int nbytes); + +extern int mtext__cat_data (MText *mt, unsigned char *p, int nbytes, + enum MTextFormat format); + +#define MTEXT_CAT_ASCII(mt, str) \ + mtext__cat_data ((mt), (unsigned char *) (str), strlen (str), \ + MTEXT_FORMAT_US_ASCII) + +extern MText *mtext__from_data (void *data, int nitems, + enum MTextFormat format, int need_copy); + +extern int mtext__replace (MText *mt, int from, int to, + char *from_str, char *to_str); + +extern int mtext__bol (MText *mt, int pos); + +extern int mtext__eol (MText *mt, int pos); + +#endif /* _M17N_MTEXT_H_ */ diff --git a/src/plist.c b/src/plist.c new file mode 100644 index 0000000..b9854e8 --- /dev/null +++ b/src/plist.c @@ -0,0 +1,1173 @@ +/* plist.c -- plist module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nPlist + + @brief Property List objects and API for them. + + A @e property @e list (or @e plist for short) is a list of zero or + more properties. A property consists of a @e key and a @e value, + where key is a symbol and value is anything that can be cast to + (void *). + + If the key of a property is a @e managing @e key, its @e value is + a @e managed @e object. A property list itself is a managed + objects. */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "character.h" +#include "mtext.h" +#include "symbol.h" +#include "plist.h" + +static M17NObjectArray plist_table; + +/** Set PLIST to a newly allocated plist object. */ + +#define MPLIST_NEW(plist) \ + do { \ + M17N_OBJECT (plist, free_plist, MERROR_PLIST); \ + M17N_OBJECT_REGISTER (plist_table, plist); \ + } while (0) + + +/** Set the element of PLIST to KEY and VAL. If PLIST is an anchor, + append a new anchor. */ + +#define MPLIST_SET(plist, key, val) \ + do { \ + MPLIST_KEY (plist) = (key); \ + MPLIST_VAL (plist) = (val); \ + if (! (plist)->next) \ + MPLIST_NEW ((plist)->next); \ + } while (0) + + +/** Set the element of PLIST to KEY and VAL. PLIST must be an anchor. + Append a new anchor and set PLIST to that anchor. */ + +#define MPLIST_SET_ADVANCE(plist, key, val) \ + do { \ + MPLIST_KEY (plist) = (key); \ + MPLIST_VAL (plist) = (val); \ + MPLIST_NEW ((plist)->next); \ + plist = (plist)->next; \ + } while (0) + + +static void +free_plist (void *object) +{ + MPlist *plist = (MPlist *) object; + + do { + MPlist *next = plist->next; + + if (MPLIST_KEY (plist) != Mnil && MPLIST_KEY (plist)->managing_key) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_UNREGISTER (plist_table, plist); + free (plist); + plist = next; + } while (plist && plist->control.ref_count == 1); + M17N_OBJECT_UNREF (plist); +} + + + +/* Load a plist from a string. */ + +#define READ_CHUNK 0x10000 + +typedef struct +{ + /* File pointer if the stream is associated with a file. Otherwise + NULL. */ + FILE *fp; + int eof; + unsigned char buffer[READ_CHUNK]; + unsigned char *p, *pend; +} MStream; + +static int +get_byte (MStream *st) +{ + int n; + + if (! st->fp || st->eof) + return EOF; + n = fread (st->buffer, 1, READ_CHUNK, st->fp); + if (n <= 0) + { + st->eof = 1; + return EOF; + } + st->p = st->buffer + 1; + st->pend = st->buffer + n; + return st->buffer[0]; +} + +#define GETC(st) \ + ((st)->p < (st)->pend ? *(st)->p++ : get_byte (st)) + + +#define UNGETC(c, st) \ + (*--(st)->p = (c)) + +/** Mapping table for reading a number. Hexadecimal chars + (0..9,A..F,a..F) are mapped to the corresponding numbers. + Apostrophe (code 39) is mapped to 254. All the other bytes are + mapped to 255. */ +unsigned char hex_mnemonic[256]; + +/** Mapping table for escaped characters. Mnemonic characters (e, b, + f, n, r, or t) that follows '\' are mapped to the corresponding + character code. All the other bytes are mapped to themselves. */ +unsigned char escape_mnemonic[256]; + + +/** Read an integer from the stream ST. It is assumed that we have + already read one character C. */ + +static int +read_decimal (MStream *st, int c) +{ + int num = 0; + + while (c >= '0' && c <= '9') + { + num = (num * 10) + (c - '0'); + c = GETC (st); + } + + if (c != EOF) + UNGETC (c, st); + return num; +} + +/** Read an unsigned from the stream ST. */ + +static unsigned +read_hexadesimal (MStream *st) +{ + int c; + unsigned num = 0, n; + + while ((c = GETC (st)) != EOF + && (n = hex_mnemonic[c]) < 16) + num = (num << 4) | n; + if (c != EOF) + UNGETC (c, st); + return num; +} + + +/** Read an M-text element from ST, and add it to LIST. Return a list + for the next element. */ + +static MPlist * +read_mtext_element (MPlist *plist, MStream *st) +{ + unsigned char buffer[1024]; + int bufsize = 1024; + unsigned char *buf = buffer; + int c, i; + + i = 0; + while ((c = GETC (st)) != EOF && c != '"') + { + if (i + MAX_UTF8_CHAR_BYTES >= bufsize) + { + bufsize *= 2; + if (buf == buffer) + { + MTABLE_MALLOC (buf, bufsize, MERROR_PLIST); + memcpy (buf, buffer, i); + } + else + MTABLE_REALLOC (buf, bufsize, MERROR_PLIST); + } + + if (c == '\\') + { + c = GETC (st); + if (c == EOF) + break; + if (c == 'x') + { + int next_c; + + c = read_hexadesimal (st); + next_c = GETC (st); + if (next_c != ' ') + UNGETC (next_c, st); + } + else + c = escape_mnemonic[c]; + } + + buf[i++] = c; + } + + MPLIST_SET_ADVANCE (plist, Mtext, + mtext__from_data (buf, i, MTEXT_FORMAT_UTF_8, 1)); + if (buf != buffer) + free (buf); + return plist; +} + +static int +read_character (MStream *st, int c) +{ + unsigned char buf[MAX_UTF8_CHAR_BYTES + 1]; + int len = CHAR_BYTES_BY_HEAD (c); + int i; + + buf[0] = c; + for (i = 1; i < len; i++) + { + c = GETC (st); + if (c == EOF + || (c & 0xC0) != 0x80) + break; + buf[i] = c; + } + if (i == len) + c = STRING_CHAR_UTF8 (buf); + else + c = buf[0]; + return c; +} + + +/** Read an integer element from ST, and add it to LIST. Return a + list for the next element. It is assumed that we have already + read the character C. */ + +static MPlist * +read_integer_element (MPlist *plist, MStream *st, int c) +{ + int num; + + if (c == '0' || c == '#') + { + c = GETC (st); + if (c == 'x') + num = read_hexadesimal (st); + else + num = read_decimal (st, c); + } + else if (c == '?') + { + c = GETC (st); + if (c == EOF) + num = 0; + else if (c != '\\') + { + if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c)) + num = c; + else + num = read_character (st, c); + } + else + { + c = GETC (st); + if (c == EOF) + num = '\\'; + else if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c)) + num = escape_mnemonic[c]; + else + num = read_character (st, c); + } + } + else if (c == '-') + num = - read_decimal (st, GETC (st)); + else + num = read_decimal (st, c); + + MPLIST_SET_ADVANCE (plist, Minteger, (void *) num); + return plist; +} + +/** Read a symbol element from ST, and add it to LIST. Return a list + for the next element. */ + +static MPlist * +read_symbol_element (MPlist *plist, MStream *st) +{ + unsigned char buffer[1024]; + int bufsize = 1024; + unsigned char *buf = buffer; + int c, i; + + i = 0; + while ((c = GETC (st)) != EOF + && c > ' ' + && c != ')' && c != '(' && c != '"') + { + if (i >= bufsize) + { + bufsize *= 2; + if (buf == buffer) + { + MTABLE_MALLOC (buf, bufsize, MERROR_PLIST); + memcpy (buf, buffer, i); + } + else + MTABLE_REALLOC (buf, bufsize, MERROR_PLIST); + } + if (c == '\\') + { + c = GETC (st); + if (c == EOF) + break; + c = escape_mnemonic[c]; + } + buf[i++] = c; + } + + buf[i] = 0; + MPLIST_SET_ADVANCE (plist, Msymbol, msymbol ((char *) buf)); + if (buf != buffer) + free (buf); + if (c > ' ') + UNGETC (c, st); + return plist; +} + +/* Read an element of various type from stream ST, and add it to LIST. + Return a list for the next element. The element type is decided by + the first token character found as below: + '(': plist + '"': mtext + '0'..'9', '-': integer + '?': integer representing character code + the other ASCII letters: symbol +*/ + +static MPlist * +read_element (MPlist *plist, MStream *st) +{ + int c; + + /* Skip separators and comments. */ + while (1) + { + while ((c = GETC (st)) != EOF && c <= ' '); + if (c != ';') + break; + while ((c = GETC (st)) != EOF && c != '\n'); + if (c == EOF) + break; + } + + if (c == '(') + { + MPlist *pl, *p; + + MPLIST_NEW (pl); + p = pl; + while ((p = read_element (p, st))); + MPLIST_SET_ADVANCE (plist, Mplist, pl); + return plist; + } + if (c == '"') + return read_mtext_element (plist, st); + if ((c >= '0' && c <= '9') || c == '-' || c == '?' || c == '#') + return read_integer_element (plist, st, c); + if (c == EOF || c == ')') + return NULL; + UNGETC (c, st); + return read_symbol_element (plist, st); +} + +void +write_element (MText *mt, MPlist *plist) +{ + if (MPLIST_SYMBOL_P (plist)) + { + MSymbol sym = MPLIST_SYMBOL (plist); + + if (sym == Mnil) + { + MTEXT_CAT_ASCII (mt, "nil"); + } + else + { + char *name = MSYMBOL_NAME (sym); + char *buf = alloca (MSYMBOL_NAMELEN (sym) * 2 + 1), *p = buf; + + while (*name) + { + if (*name <= ' ' || *name == '"' || *name == ')' || *name == ')') + *p++ = '\\'; + *p++ = *name++; + } + *p = '\0'; + MTEXT_CAT_ASCII (mt, buf); + } + } + else if (MPLIST_INTEGER_P (plist)) + { + int num = MPLIST_INTEGER (plist); + char buf[128]; + + sprintf (buf, "%d", num); + MTEXT_CAT_ASCII (mt, buf); + } + else if (MPLIST_PLIST_P (plist)) + { + MPlist *pl; + + plist = MPLIST_PLIST (plist); + mtext_cat_char (mt, '('); + MPLIST_DO (pl, plist) + { + if (pl != plist) + mtext_cat_char (mt, ' '); + write_element (mt, pl); + } + mtext_cat_char (mt, ')'); + } + else if (MPLIST_MTEXT_P (plist)) + { + mtext_cat_char (mt, '"'); + /* Not yet implemnted */ + mtext_cat_char (mt, '"'); + } +} + +/* Support functions for mdebug_dump_plist. */ + +static void +dump_string (char *str) +{ + char *p = str, *pend = p + strlen (p), *new, *p1; + + new = p1 = alloca ((pend - p) * 4 + 1); + while (p < pend) + { + if (*p < 0) + { + sprintf (p1, "\\x%02X", (unsigned char) *p); + p1 += 4; + } + else if (*p < ' ') + { + *p1++ = '^'; + *p1++ = *p + '@'; + } + else if (*p == ' ') + { + *p1++ = '\\'; + *p1++ = ' '; + } + else + *p1++ = *p; + p++; + } + *p1 = '\0'; + fprintf (stderr, "%s", new); +} + +static void +dump_plist_element (MPlist *plist, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MSymbol key; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + key = MPLIST_KEY (plist); + fprintf (stderr, "(%s(#%d) ", msymbol_name (MPLIST_KEY (plist)), + plist->control.ref_count); + if (key == Msymbol) + dump_string (msymbol_name (MPLIST_SYMBOL (plist))); + else if (key == Mtext) + mdebug_dump_mtext (MPLIST_MTEXT (plist), indent, 0); + else if (key == Minteger) + fprintf (stderr, "%x", MPLIST_INTEGER (plist)); + else if (key == Mstring) + fprintf (stderr, "\"%s\"", MPLIST_STRING (plist)); + else if (key == Mplist) + { + fprintf (stderr, "\n%s", prefix); + mdebug_dump_plist (MPLIST_PLIST (plist), indent); + } + else + fprintf (stderr, "0x%X", (unsigned) MPLIST_VAL (plist)); + fprintf (stderr, ")"); +} + + +/* Internal API */ +int +mplist__init () +{ + int i; + + plist_table.count = 0; + + Minteger = msymbol ("integer"); + Mplist = msymbol_as_managing_key ("plist"); + Mtext = msymbol_as_managing_key ("mtext"); + + for (i = 0; i < 256; i++) + hex_mnemonic[i] = 255; + for (i = '0'; i <= '9'; i++) + hex_mnemonic[i] = i - '0'; + for (i = 'A'; i <= 'F'; i++) + hex_mnemonic[i] = i - 'A' + 10; + for (i = 'a'; i <= 'f'; i++) + hex_mnemonic[i] = i - 'a' + 10; + for (i = 0; i < 256; i++) + escape_mnemonic[i] = i; + escape_mnemonic['e'] = 27; + escape_mnemonic['b'] = '\b'; + escape_mnemonic['f'] = '\f'; + escape_mnemonic['n'] = '\n'; + escape_mnemonic['r'] = '\r'; + escape_mnemonic['t'] = '\t'; + escape_mnemonic['\\'] = '\\'; + + return 0; +} + +void +mplist__fini (void) +{ + mdebug__report_object ("Plist", &plist_table); +} + + +/* Parse this form of PLIST: + (symbol:KEY1 TYPE1:VAL1 symbol:KEY2 TYPE2:VAL2 ...) + and return a newly created plist of this form: + (KEY1:VAL1 KEY2:VAL2 ...) */ + +MPlist * +mplist__from_plist (MPlist *plist) +{ + MPlist *pl, *p; + + MPLIST_NEW (pl); + p = pl; + while (! MPLIST_TAIL_P (plist)) + { + MSymbol key, type; + + if (! MPLIST_SYMBOL_P (plist)) + MERROR (MERROR_PLIST, NULL); + key = MPLIST_SYMBOL (plist); + plist = MPLIST_NEXT (plist); + type = MPLIST_KEY (plist); + if (type->managing_key) + M17N_OBJECT_REF (MPLIST_VAL (plist)); + MPLIST_SET_ADVANCE (p, key, MPLIST_VAL (plist)); + plist = MPLIST_NEXT (plist); + } + return pl; +} + +/** Parse this form of PLIST: + ((symbol:KEY1 ANY:VAL1 ... ) (symbol:KEY2 ANY:VAL2 ...) ...) + and return a newly created plist of this form: + (KEY1:(ANY:VAL1 ...) KEY2:(ANY:VAL2 ...) ...) + ANY can be any type. */ + +MPlist * +mplist__from_alist (MPlist *plist) +{ + MPlist *pl, *p; + + MPLIST_NEW (pl); + p = pl; + MPLIST_DO (plist, plist) + { + MPlist *elt; + + if (! MPLIST_PLIST_P (plist)) + MERROR (MERROR_PLIST, NULL); + elt = MPLIST_PLIST (plist); + if (! MPLIST_SYMBOL_P (elt)) + MERROR (MERROR_PLIST, NULL); + MPLIST_SET_ADVANCE (p, MPLIST_SYMBOL (elt), MPLIST_NEXT (elt)); + M17N_OBJECT_REF (MPLIST_NEXT (elt)); + } + return pl; +} + + +MPlist * +mplist__from_file (FILE *fp) +{ + MPlist *plist, *pl; + MStream st; + + st.fp = fp; + st.eof = 0; + st.p = st.pend = st.buffer; + MPLIST_NEW (plist); + pl = plist; + while ((pl = read_element (pl, &st))); + return plist; +} + + +/** Parse $STR of $N bytes and return a property list object. $FORMAT + must be either @c MTEXT_FORMAT_US_ASCII or @c MTEXT_FORMAT_UTF_8, + and controls how to produce @c STRING or @c M-TEXT in the + following definition. + + The syntax of $STR is as follows. + + PLIST ::= '(' ELEMENT * ')' + + ELEMENT ::= SYMBOL | INTEGER | UNSIGNED | STRING | M-TEXT | PLIST + + SYMBOL ::= ascii-character-sequence + + INTEGER ::= '-' ? [ '0' | .. | '9' ] + + + UNSIGNED ::= '0x' [ '0' | .. | '9' | 'A' | .. | 'F' | 'a' | .. | 'f' ] + + + STRING ::= '"' byte-sequence '"' + + M-TEXT ::= '"' byte-sequence '"' + + Each kind of @c ELEMENT is assigned one of these keys: + @c Msymbol, @c Mint, @c Munsigned, + @c Mstring, @c Mtext, @c Mplist + + In an ascii-character-sequence, a backslush (\) is used as the escape + character, which means that, for instance, "abc\ def" + produces a symbol whose name is of length seven with the fourth + character being a space. + + In a byte-sequence, "\r", "\n", "\e", and "\t" are replaced by CR, + NL, ESC, and TAB character respectively, "\xXX" are replaced by + byte 0xXX. After this replacement, the byte-sequence is decoded + into STRING or M-TEXT as below: + + If $FORMAT is MTEXT_FORMAT_US_ASCII and the byte-sequence + contains only ASCII characters, it is decoded into STRING. + Otherwise, it is regarded as an UTF-8 sequence, and decoded into + M-TEXT. */ + +MPlist * +mplist__from_string (unsigned char *str, int n) +{ + MPlist *plist, *pl; + MStream st; + + st.fp = NULL; + st.eof = 0; + st.p = str; + st.pend = str + n; + MPLIST_NEW (plist); + pl = plist; + while ((pl = read_element (pl, &st))); + return plist; +} + +int +mplist__serialize (MText *mt, MPlist *plist) +{ + MPlist *pl; + + MPLIST_DO (pl, plist) + { + if (pl != plist) + mtext_cat_char (mt, ' '); + write_element (mt, pl); + } + return 0; +} + +MPlist * +mplist__deserialize (MText *mt) +{ + if (mt->format > MTEXT_FORMAT_UTF_8) + MERROR (MERROR_PLIST, NULL); + return mplist__from_string (MTEXT_DATA (mt), mtext_nbytes (mt)); +} + + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nPlist */ +/*** @{ */ +/*=*/ + +/***en + @brief Symbol whose name is "integer". + + The symbol @c Minteger has the name "integer". A value + of a plist whose key is @c Minteger must be an integer. */ + +MSymbol Minteger; +/*=*/ + +/***en + @brief Symbol whose name is "plist". + + The symbol @c Mplist has the name "plist". It is a + managing key. A value of a plist whose key is @c Mplist must be a + plist. */ + +MSymbol Mplist; +/*=*/ + +/***en + @brief Symbol whose name is "mtext". + + The symbol @c Mtext has the name "mtext". It is a + managing key. A value of a plist whose key is @c Mtext must be an + M-text. */ + +/***ja + @brief "text" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë + + ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë @c Mtext ¤Ï "text" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä´ÉÍý + ¥­¡¼¤Ç¤¢¤ë¡£ */ + +MSymbol Mtext; + + +/*=*/ +/***en + @brief Create a property list object. + + The mplist () function returns a newly created property list + object of length zero. + + @returns + This function returns a newly created property list. + + @errors + This function never fails. */ + +MPlist * +mplist () +{ + MPlist *plist; + + MPLIST_NEW (plist); + return plist; +} + +/*=*/ +/***en + @brief Copy a plist. + + The mplist_copy () function copies $PLIST. In the copy, the + values are the same as those of $PLIST. + + @return + This function returns a newly created plist which is a copy of + $PLIST. */ +/*** + @errors + This function never fails. */ + +MPlist * +mplist_copy (MPlist *plist) +{ + MPlist *copy = mplist (), *pl = copy; + + MPLIST_DO (plist, plist) + pl = mplist_add (pl, MPLIST_KEY (plist), MPLIST_VAL (plist)); + return copy; +} + +/*=*/ + +/***en + @brief Set the value of a property in a property list object. + + The mplist_put () function searches property list object $PLIST + from the beginning for a property whose key is $KEY. If such a + property is found, its value is changed to $VALUE. Otherwise, a + new property whose key is $KEY and value is $VALUE is appended at + the end of $PLIST. See the documentation of mplist_add () for + the restriction on $KEY and $VAL. + + If $KEY is a managing key, $VAL must be a managed object. In this + case, the reference count of the old value, if not @c NULL, is + decremented by one, and that of $VAL is incremented by one. + + @return + If the operation was successful, mplist_put () returns a sublist of + $PLIST whose first element is the just modified or added one. + Otherwise, it returns @c NULL. */ + +MPlist * +mplist_put (MPlist *plist, MSymbol key, void *val) +{ + if (key == Mnil) + MERROR (MERROR_PLIST, NULL); + MPLIST_FIND (plist, key); + if (key->managing_key) + { + if (! MPLIST_TAIL_P (plist)) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + M17N_OBJECT_REF (val); + } + MPLIST_SET (plist, key, val); + return plist; +} + +/*=*/ + +/***en + @brief Get the value of a property in a property list object. + + The mplist_get () function searches property list object $PLIST + from the beginning for a property whose key is $KEY. If such a + property is found, a pointer to its value is returned as the type + of (void *). If not found, @c NULL is returned. + + When @c NULL is returned, there are two possibilities: one is the + case where no property is found (see above); the other is the case + where a property is found and its value is @c NULL. In case that + these two cases must be distinguished, use the mplist_find_by_key () + function. */ + +/*** + @seealso + mplist_find_by_key () */ + +void * +mplist_get (MPlist *plist, MSymbol key) +{ + MPLIST_FIND (plist, key); + return (MPLIST_TAIL_P (plist) ? NULL : MPLIST_VAL (plist)); +} + +/*=*/ + +/***en + @brief Add a property at the end of a property list object. + + The mplist_add () function appends at the end of $PLIST a property + whose key is $KEY and value is $VAL. $KEY can be any symbol + other than @c Mnil. + + If $KEY is a managing key, $VAL must be a managed object. In this + case, the reference count of $VAL is incremented by one. + + @return + If the operation was successful, mplist_add () returns a sublist of + $PLIST whose first element is the just added one. Otherwise, it + returns @c NULL. */ + +MPlist * +mplist_add (MPlist *plist, MSymbol key, void *val) +{ + if (key == Mnil) + MERROR (MERROR_PLIST, NULL); + MPLIST_FIND (plist, Mnil); + if (key->managing_key) + M17N_OBJECT_REF (val); + MPLIST_KEY (plist) = key; + MPLIST_VAL (plist) = val; + MPLIST_NEW (plist->next); + return plist; +} + +/*=*/ + +/***en + @brief Push a property to a property list object. + + The mplist_push () function pushes at the top of $PLIST a + property whose key is $KEY and value si $VAL. + + If $KEY is a managing key, $VAL must be a managed object. In this + case, the reference count of $VAL is incremented by one. + + @return + If the operation was successful, this function returns $PLIST. + Otherwise, it returns @c NULL. */ + +MPlist * +mplist_push (MPlist *plist, MSymbol key, void *val) +{ + MPlist *pl; + + if (key == Mnil) + MERROR (MERROR_PLIST, NULL); + MPLIST_NEW (pl); + MPLIST_KEY (pl) = MPLIST_KEY (plist); + MPLIST_VAL (pl) = MPLIST_VAL (plist); + pl->next = plist->next; + plist->next = pl; + if (key->managing_key) + M17N_OBJECT_REF (val); + MPLIST_KEY (plist) = key; + MPLIST_VAL (plist) = val; + return plist; +} + +/*=*/ + +/***en + @brief Pop a property from a property list object. + + The mplist_pop () function pops the topmost property from $PLIST. + As a result, the key and value of $PLIST becomes those of the next + of $PLIST. + + @return + If the operation was successful, this function return the value of + the just popped property. Otherwise, it returns @c NULL. */ + +void * +mplist_pop (MPlist *plist) +{ + void *val; + MPlist *next; + + if (MPLIST_TAIL_P (plist)) + return NULL; + val = MPLIST_VAL (plist); + next = plist->next; + MPLIST_KEY (plist) = MPLIST_KEY (next); + MPLIST_VAL (plist) = MPLIST_VAL (next); + if (MPLIST_KEY (plist) != Mnil + && MPLIST_KEY (plist)->managing_key + && MPLIST_VAL (plist)) + M17N_OBJECT_REF (MPLIST_VAL (plist)); + plist->next = next->next; + if (plist->next) + M17N_OBJECT_REF (plist->next); + M17N_OBJECT_UNREF (next); + return val; +} + +/*=*/ +/***en + @brief Find a property of a specific key in a property list object. + + The mplist_find_by_key () function searches property list object + $PLIST from the beginning for a property whose key is $KEY. If + such a property is found, a sublist of $PLIST whose first element + is the found one is returned. Otherwise, @c NULL is returned. + + If $KEY is Mnil, it returns the last a sublist of $PLIST whose + first element is the last one of $PLIST. */ + +MPlist * +mplist_find_by_key (MPlist *plist, MSymbol key) +{ + MPLIST_FIND (plist, key); + return (MPLIST_TAIL_P (plist) + ? (key == Mnil ? plist : NULL) + : plist); +} + +/*=*/ +/***en + @brief Find a property of a specific value in a property list object. + + The mplist_find_by_value () function searches property list object + $PLIST from the beginning for a property whose value is $VAL. If + such a property is found, a sublist of $PLIST whose first element + is the found one is returned. Otherwise, @c NULL is returned. */ + +MPlist * +mplist_find_by_value (MPlist *plist, void *val) +{ + MPLIST_DO (plist, plist) + { + if (MPLIST_VAL (plist) == val) + return plist; + } + return NULL; +} + +/*=*/ + +/***en + @brief Return the next sublist of a plist. + + The mplist_next () function returns a pointer to the sublist of + $PLIST, which begins at the second element in $PLIST. If the + length of $PLIST is zero, it returns @c NULL. */ + +MPlist * +mplist_next (MPlist *plist) +{ + return (MPLIST_TAIL_P (plist) ? NULL : plist->next); +} + +/*=*/ + +/***en + @brief Set the first property in a property list object. + + The mplist_set () function sets the key and value of the first + property in property list object $PLIST to $KEY and $VALUE, + respectively. See the documentation of mplist_add () for the + restriction on $KEY and $VAL. + + @return + If the operation was successful, mplist_set () returns $PLIST. + Otherwise, it returns @c NULL. */ + +MPlist * +mplist_set (MPlist *plist, MSymbol key, void * val) +{ + if (key == Mnil) + { + if (! MPLIST_TAIL_P (plist)) + { + key = MPLIST_KEY (plist); + M17N_OBJECT_UNREF (MPLIST_NEXT (plist)); + MPLIST_KEY (plist) = Mnil; + if (key->managing_key && MPLIST_VAL (plist)) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + plist->next = NULL; + } + } + else + { + if (! MPLIST_TAIL_P (plist) + && MPLIST_KEY (plist)->managing_key + && MPLIST_VAL (plist)) + M17N_OBJECT_UNREF (MPLIST_VAL (plist)); + if (key->managing_key) + M17N_OBJECT_REF (val); + MPLIST_SET (plist, key, val); + } + return plist; +} + +/*=*/ + +/***en + @brief Return the length of a plist. + + The mplist_length () function returns the number of properties in + property list object $PLIST. */ + +int +mplist_length (MPlist *plist) +{ + int n; + + for (n = 0; ! (MPLIST_TAIL_P (plist)); n++, plist = plist->next); + return n; +} + +/*=*/ + +/***en + @brief Return the key of the first property in a property list object. + + The mplist_key () function returns the key of the first property + in property list object $PLIST. If the length of $PLIST is zero, + it returns @c Mnil. */ + +MSymbol +mplist_key (MPlist *plist) +{ + return MPLIST_KEY (plist); +} + +/*=*/ + +/***en + @brief Return the value of the first property in a property list object. + + The mplist_value () function returns the value of the first + property in property list object $PLIST. If the length of $PLIST + is zero, it returns @c NULL. */ + +void * +mplist_value (MPlist *plist) +{ + return MPLIST_VAL (plist); +} + +/*** @} */ + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump a plist. + + The mdebug_dump_plist () function prints $PLIST in a human + readable way to the stderr. $INDENT specifies how many columns to + indent the lines but the first one. + + @return + This function returns $PLIST. */ + +MPlist * +mdebug_dump_plist (MPlist *plist, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + MPlist *pl; + int first = 1; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "("); + MPLIST_DO (pl, plist) + { + if (first) + first = 0; + else + fprintf (stderr, "\n%s ", prefix); + dump_plist_element (pl, indent + 2); + } + fprintf (stderr, ")"); + return plist; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/plist.h b/src/plist.h new file mode 100644 index 0000000..372a3e6 --- /dev/null +++ b/src/plist.h @@ -0,0 +1,97 @@ +/* plist.h -- header file for the plist module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_PLIST_H_ +#define _M17N_PLIST_H_ + +struct MPlist +{ + /** Header for a managed object. */ + M17NObject control; + + /**en Key of the first element of the plist. If the value is Mnil, + this is the tail of the plist. In that case, and is + NULL. If the value is a managing key, is a managed + object. */ + MSymbol key; + + /**en Value of the first element of the plist. */ + void *val; + + /**en Plist for the next element. */ + MPlist *next; +}; + +/** Macros to access each member of PLIST. */ + +#define MPLIST_KEY(plist) ((plist)->key) +#define MPLIST_VAL(plist) ((plist)->val) +#define MPLIST_VAL_MANAGED_P(plist) ((plist)->control.flag) +#define MPLIST_NEXT(plist) ((plist)->next) +#define MPLIST_TAIL_P(plist) ((plist)->key == Mnil) + +#define MPLIST_SYMBOL_P(plist) (MPLIST_KEY (plist) == Msymbol) +#define MPLIST_STRING_P(plist) (MPLIST_KEY (plist) == Mstring) +#define MPLIST_MTEXT_P(plist) (MPLIST_KEY (plist) == Mtext) +#define MPLIST_INTEGER_P(plist) (MPLIST_KEY (plist) == Minteger) +#define MPLIST_PLIST_P(plist) (MPLIST_KEY (plist) == Mplist) + +#define MPLIST_SYMBOL(plist) ((MSymbol) MPLIST_VAL (plist)) +#define MPLIST_STRING(plist) ((char *) MPLIST_VAL (plist)) +#define MPLIST_MTEXT(plist) ((MText *) MPLIST_VAL (plist)) +#define MPLIST_INTEGER(plist) ((int) MPLIST_VAL (plist)) +#define MPLIST_PLIST(plist) ((MPlist *) MPLIST_VAL (plist)) + +#define MPLIST_FIND(plist, key) \ + do { \ + while (! MPLIST_TAIL_P (plist) && MPLIST_KEY (plist) != (key)) \ + (plist) = (plist)->next; \ + } while (0) + + +#define MPLIST_DO(elt, plist) \ + for ((elt) = (plist); ! MPLIST_TAIL_P (elt); (elt) = MPLIST_NEXT (elt)) + +#define MPLIST_LENGTH(plist) \ + (MPLIST_TAIL_P (plist) ? 0 \ + : MPLIST_TAIL_P ((plist)->next) ? 1 \ + : MPLIST_TAIL_P ((plist)->next->next) ? 2 \ + : mplist_length (plist)) + + +extern unsigned char hex_mnemonic[256]; +extern unsigned char escape_mnemonic[256]; + +extern MPlist *mplist__from_file (FILE *fp); + +extern MPlist *mplist__from_plist (MPlist *plist); + +extern MPlist *mplist__from_alist (MPlist *plist); + +extern MPlist *mplist__from_string (unsigned char *str, int n); + +extern int mplist__serialize (MText *mt, MPlist *plist); + +extern MPlist *mplist__deserialize (MText *mt); + + +#endif /* _M17N_PLIST_H_ */ diff --git a/src/symbol.c b/src/symbol.c new file mode 100644 index 0000000..f85db6c --- /dev/null +++ b/src/symbol.c @@ -0,0 +1,698 @@ +/* symbol.c -- symbol module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nSymbol + + @brief Symbol objects and API for them. + + The m17n library uses objects called @e symbols as unambiguous + identifiers. Symbols are similar to atoms in the X library, but a + symbol can have zero or more @e symbol @e properties. A symbol + property consists of a @e key and a @e value, where key is also a + symbol and value is anything that can be cast to (void + *). "The symbol property that belongs to the symbol S and + whose key is K" may be shortened to "K property of S". + + Symbols are used mainly in the following three ways. + + @li As keys of symbol properties and other properties. + + @li To represent various objects, e.g. charsets, coding systems, + fontsets. + + @li As arguments of the m17n library functions to control + their behavior. + + There is a special kind of symbol, a @e managing @e key. The + value of a property whose key is a managing key must be a @e + managed @e object. See @ref m17nObject for the detail. +*/ + +/***ja + @addtogroup m17nSymbol ¥·¥ó¥Ü¥ë + + @brief ¥·¥ó¥Ü¥ë¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API + + m17n ¥é¥¤¥Ö¥é¥ê¤Ï°ì°Õ¤Ë·è¤Þ¤ëµ­½Ò»Ò¤È¤·¤Æ @e ¥·¥ó¥Ü¥ë ¤È¸Æ¤Ö¥ª¥Ö¥¸¥§ + ¥¯¥È¤òÍѤ¤¤ë¡£¥·¥ó¥Ü¥ë¤Ï X ¥é¥¤¥Ö¥é¥ê¤Î¥¢¥È¥à¤È»÷¤Æ¤¤¤ë¤¬¡¢0 ¸Ä°Ê + ¾å¤Î @e ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ ¤ò»ý¤Ä¤³¤È¤¬¤Ç¤­¤ëÅÀ¤Ç¡¢¥¢¥È¥à¤è¤ê¹âµ¡ + ǽ¤Ç¤¢¤ë¡£¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥­¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥­¡¼¤Ï¤½ + ¤ì¼«ÂÎ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢ÃÍ¤Ï (void *) ·¿¤Ë¥­¥ã¥¹¥È¤Ç¤­¤ë¤â + ¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£¡Ö¥·¥ó¥Ü¥ë S ¤¬»ý¤Ä¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¥­¡¼ + ¤¬ K ¤Î¤â¤Î¡×¤ò´Êñ¤Ë¡ÖS ¤Î K ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ + + ¥·¥ó¥Ü¥ë¤ÎÍÑÅӤϼç¤Ë°Ê²¼¤Î3Ä̤ê¤Ç¤¢¤ë¡£ + + @li ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ (¤ª¤è¤Ó¾¤Î¥×¥í¥Ñ¥Æ¥£) ¤Î¥­¡¼¤òɽ¤ï¤¹¡£ + + @li ʸ»ú¥»¥Ã¥È¡¢¥³¡¼¥É·Ï¡¢¥Õ¥©¥ó¥È¥»¥Ã¥È¤Ê¤É¤Î³Æ¼ï¥ª¥Ö¥¸¥§¥¯¥È¤òɽ + ¤ï¤¹¡£ + + @li m17n ¥é¥¤¥Ö¥é¥ê´Ø¿ô¤Î°ú¿ô¤È¤Ê¤ê¡¢´Ø¿ô¤ÎµóÆ°¤òÀ©¸æ¤¹¤ë¡£ + + ¥­¡¼¤¬ @c Mmanaging_key ¤ÇÃͤ¬ @c NULL °Ê³°¤Î¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò + »ý¤Ä¥·¥ó¥Ü¥ë¤Ï¡¢@e ´ÉÍý¥­¡¼ ¤È¸Æ¤Ð¤ì¤ë¡£¤¢¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼ + ¤¬´ÉÍý¥­¡¼¤Î¾ì¹ç¡¢¤½¤ÎÃÍ¤Ï @e ´ÉÍý²¼¥ª¥Ö¥¸¥§¥¯¥È ¤È¤·¤Æ°·¤ï¤ì¤ë¡£ + ´ÉÍý²¼¥ª¥Ö¥¸¥§¥¯¥È¤Ë´Ø¤¹¤ë¾ÜºÙ¤Ï @e ´ÉÍý²¼¥ª¥Ö¥¸¥§¥¯¥È ¤Î¾Ï¤ò»²¾È + ¤Î¤³¤È¡£ + + ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤Ë¤ÏÃͤÎÂå¤ê¤Ë @e ¥×¥í¥Ð¥¤¥À´Ø¿ô ¤ò»ý¤¿¤»¤ë¤³¤È + ¤â¤Ç¤­¤ë¡£¥×¥í¥Ð¥¤¥À´Ø¿ô¤Ï¤½¤Î¥­¡¼¤ÎÃͤòÆÀ¤è¤¦¤È¤¹¤ë»þ¤Ë¸Æ¤Ð¤ì¡¢¤½ + ¤ÎÊÖ¤êÃͤ¬µá¤á¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤʤ롣¤³¤Î»ÅÁȤߤˤè¤Ã¤Æ¥· + ¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ÎÃÙ±äɾ²Á¤¬²Äǽ¤Ë¤Ê¤ë¡£¥×¥í¥Ð¥¤¥À´Ø¿ô¤ÏÃͤòÊÖ¤¹¤È + Ʊ»þ¤ËÃͤòÀßÄꤹ¤ë¤³¤È¤â¤Ç¤­¡¢¤³¤Î¾ì¹ç¤Ë¤Ï¡¢¥×¥í¥Ð¥¤¥À´Ø¿ô¼«ÂΤÏÃÍ + ¤ÎÀßÄê°Ê¹ß¤Ï̵¸ú¤È¤Ê¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "character.h" +#include "mtext.h" +#include "plist.h" + +static int num_symbols; + +#define SYMBOL_TABLE_SIZE 1024 + +static MSymbol symbol_table[SYMBOL_TABLE_SIZE]; + +static unsigned +hash_string (const char *str, int len) +{ + unsigned hash = 0; + const char *end = str + len; + unsigned c; + + while (str < end) + { + c = *((unsigned char *) str++); + if (c >= 0140) + c -= 40; + hash = ((hash << 3) + (hash >> 28) + c); + } + return hash & (SYMBOL_TABLE_SIZE - 1); +} + + +static MPlist * +serialize_symbol (void *val) +{ + MPlist *plist = mplist (); + + mplist_add (plist, Msymbol, val); + return plist; +} + +static void * +deserialize_symbol (MPlist *plist) +{ + return (MPLIST_SYMBOL_P (plist) ? MPLIST_SYMBOL (plist) : Mnil); +} + + +/* Internal API */ + +int +msymbol__init () +{ + num_symbols = 0; + Mnil = (MSymbol) 0; + Mt = msymbol ("t"); + Msymbol = msymbol ("symbol"); + Mstring = msymbol ("string"); + return 0; +} + +void +msymbol__fini () +{ + int i; + MSymbol sym, next; + + for (i = 0; i < SYMBOL_TABLE_SIZE; i++) + for (sym = symbol_table[i]; sym; sym = sym->next) + if (! MPLIST_TAIL_P (&sym->plist)) + { + if (sym->plist.key->managing_key) + M17N_OBJECT_UNREF (sym->plist.val); + M17N_OBJECT_UNREF (sym->plist.next); + } + for (i = 0; i < SYMBOL_TABLE_SIZE; i++) + for (sym = symbol_table[i]; sym; sym = next) + { + next = sym->next; + free (sym->name); + free (sym); + } +} + + +MSymbol +msymbol__with_len (const char *name, int len) +{ + char *p = alloca (len + 1); + + memcpy (p, name, len); + p[len] = '\0'; + return msymbol (p); +} + + +/** Canonicalize the name of SYM, and return a symbol of the + canonicalized name. Canonicalization is done by this rule: + o convert all uppercase characters to lowercase. + o remove all non alpha-numeric characters. + o change the leading "ibm" to "cp". + o change the leading "cp" to "ibm" + o remove the leading "iso". + For instance: + "ISO-8859-2" -> "88592" + "euc-JP" -> "eucjp" + "IBM851" -> "cp851" + "CP1250" -> "ibm1250" + + This function is used to canonicalize charset and coding system + names. */ + +MSymbol +msymbol__canonicalize (MSymbol sym) +{ + char *name = sym->name; + /* Extra 2 bytes are for changing "cpXXX" to "ibmXXX" and + terminating '\0'. */ + char *canon = (char *) alloca (strlen (name) + 2); + char *p = canon; + + for (; *name; name++) + if (ISALNUM (*name)) + *p++ = TOLOWER (*name); + *p = '\0'; + if (p - canon > 3 && canon[0] == 'i') + { + if (canon[1] == 'b' && canon[2] == 'm' && isdigit (canon[3])) + { + /* Change "ibmXXX" to "cpXXX". */ + canon++; + canon[0] = 'c'; + canon[1] = 'p'; + } + else if (canon[1] == 's' && canon[2] == 'o') + { + /* Change "isoXXX" to "XXX". */ + canon += 3; + } + } + else if (p - canon > 2 + && canon[0] == 'c' && canon[1] == 'p' && isdigit (canon[2])) + { + /* Change "cpXXX" to "ibmXXX". */ + for (; p >= canon + 2; p--) + p[1] = p[0]; + canon[0] = 'i'; + canon[1] = 'b'; + canon[2] = 'm'; + } + + return msymbol (canon); +} + +MTextPropSerializeFunc msymbol__serializer = serialize_symbol; +MTextPropDeserializeFunc msymbol__deserializer = deserialize_symbol; + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/* External API */ + +/*** @addtogroup m17nSymbol */ +/*** @{ */ + +/*=*/ +/***en + @brief Symbol whose name is "nil". + + The symbol #Mnil has the name "nil" and, in general, + represents @e false or @e no. When coerced to "int", its value is + zero. #Mnil can't have any symbol property. */ + +/***ja + @brief ÄêµÁºÑ¥·¥ó¥Ü¥ë Mnil + + ¥·¥ó¥Ü¥ë #Mnil ¤Ï "nil" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢°ìÈ̤ˡֵ¶¡× + ¤ò°ÕÌ£¤¹¤ë¡£#Mnil ¼«¿È¤Ï¤¤¤«¤Ê¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤â»ý¤¿¤Ê¤¤¡£ */ + +MSymbol Mnil; + +/*=*/ + +/***en + @brief Symbol whose name is "t". + + The symbol #Mt has the name "t" and, in general, + represents @e true or @e yes. */ + +/***ja + @brief ÄêµÁºÑ¥·¥ó¥Ü¥ë Mt + + ¥·¥ó¥Ü¥ë #Mt ¤Ï "t" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢°ìÈ̤ˡֿ¿¡×¤ò°Õ + Ì£¤¹¤ë¡£ */ + +MSymbol Mt; + +/*=*/ + +/***en + @brief Symbol whose name is "string". + + The symbol #Mstring has the name "string" and is used + as an argument of the functions mchar_define_property (), + etc. */ + +/***ja + @brief "string" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë + + ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë #Mstring ¤Ï "string" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + ´Ø¿ô mchar_define_property () Åù¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Mstring; + +/*=*/ + +/***en + @brief Symbol whose name is "symbol". + + The symbol #Msymbol has the name "symbol" and is used + as an argument of the functions mchar_define_property (), + etc. */ + +/***ja + @brief "symbol" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë + + ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë #Msymbol ¤Ï "symbol" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢ + ´Ø¿ô mchar_define_property () Åù¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */ + +MSymbol Msymbol; + +/*=*/ + +/***en + @brief Get a symbol. + + The msymbol () function returns the canonical symbol whose name is + $NAME. If there is none, one is created. The created one is not + a managing key. + + Symbols whose name starts by two spaces are reserved by the m17n + library, and are used by the library only internally. + + @return + This function returns the found or created symbol. + + @errors + This function never fails. */ + +/***ja + @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë¤òÊÖ¤¹ + + ´Ø¿ô msymbol () ¤Ï $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü¥ë¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥· + ¥ó¥Ü¥ë¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¤Ë¤Ï¡¢¿·¤·¤¤¥·¥ó¥Ü¥ë¤¬¼«Æ°Åª¤Ëºî¤é¤ì¤ë¡£ + + @latexonly \IPAlabel{msymbol} @endlatexonly */ + +/*** + @seealso + msymbol_as_managing_key (), msymbol_name (), msymbol_exist () */ + +MSymbol +msymbol (const char *name) +{ + MSymbol sym; + int len; + unsigned hash; + + len = strlen (name); + if (len == 3 && name[0] == 'n' && name[1] == 'i' && name[2] == 'l') + return Mnil; + hash = hash_string (name, len); + len++; + for (sym = symbol_table[hash]; sym; sym = sym->next) + if (len == sym->length + && *name == *(sym->name) + && ! memcmp (name, sym->name, len)) + return sym; + + num_symbols++; + MTABLE_CALLOC (sym, 1, MERROR_SYMBOL); + MTABLE_MALLOC (sym->name, len, MERROR_SYMBOL); + memcpy (sym->name, name, len); + sym->length = len; + sym->next = symbol_table[hash]; + symbol_table[hash] = sym; + return sym; +} + +/***en + @brief Create a managing key. + + The msymbol_as_managing_key () function returns a newly created + managing key whose name is $NAME. It there already exists a + symbol of name $NAME, it returns #Mnil. + + Symbols whose name starts by two spaces are reserved by the m17n + library, and are used by the library only internally. + + @return + If the operation was successful, this function returns the created + symbol. Otherwise, it returns #Mnil. */ + +/*** + @errors + MERROR_SYMBOL + + @seealso + msymbol (), msymbol_exist () */ + +MSymbol +msymbol_as_managing_key (const char *name) +{ + MSymbol sym; + int len; + unsigned hash; + + len = strlen (name); + if (len == 3 && name[0] == 'n' && name[1] == 'i' && name[2] == 'l') + MERROR (MERROR_SYMBOL, Mnil); + hash = hash_string (name, len); + len++; + for (sym = symbol_table[hash]; sym; sym = sym->next) + if (len == sym->length + && *name == *(sym->name) + && ! memcmp (name, sym->name, len)) + MERROR (MERROR_SYMBOL, Mnil); + + num_symbols++; + MTABLE_CALLOC (sym, 1, MERROR_SYMBOL); + sym->managing_key = 1; + MTABLE_MALLOC (sym->name, len, MERROR_SYMBOL); + memcpy (sym->name, name, len); + sym->length = len; + sym->next = symbol_table[hash]; + symbol_table[hash] = sym; + return sym; +} + +/*=*/ + +/***en + @brief Search for a symbol that has a specified name. + + The msymbol_exist () function searches for the symbol whose name + is $NAME. + + @return + If such a symbol exists, msymbol_exist () returns that symbol. + Otherwise it returns the predefined symbol #Mnil. + + @errors + This function never fails. */ + +/***ja + @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë¤òõ¤¹ + + ´Ø¿ô msymbol_exist () ¤Ï $NAME ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü¥ë¤òõ¤¹¡£ + + @return + ¤â¤·¤½¤Î¤è¤¦¤Ê¥·¥ó¥Ü¥ë¤¬Â¸ºß¤¹¤ë¤Ê¤é¤Ð¤½¤Î¥·¥ó¥Ü¥ë¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê + ¤±¤ì¤Ð¡¢ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë #Mnil ¤òÊÖ¤¹¡£ */ + +/***@seealso + msymbol_name (), msymbol () */ + +MSymbol +msymbol_exist (const char *name) +{ + MSymbol sym; + int len; + unsigned hash; + + len = strlen (name); + if (len == 3 && name[0] == 'n' && name[1] == 'i' && name[2] == 'l') + return Mnil; + hash = hash_string (name, len); + len++; + for (sym = symbol_table[hash]; sym; sym = sym->next) + if (len == sym->length + && *name == *(sym->name) + && ! memcmp (name, sym->name, len)) + return sym; + return Mnil; +} + +/*=*/ + +/***en + @brief Get symbol name. + + The msymbol_name () function returns a pointer to a string + containing the name of $SYMBOL. + + @return + Name of the specified symbol. + + @errors + This function never fails. */ +/***ja + @brief ¥·¥ó¥Ü¥ë¤Î̾Á°¤òÊÖ¤¹ + + ´Ø¿ô msymbol_name () ¤Ï»ØÄꤵ¤ì¤¿¥·¥ó¥Ü¥ë $SYMBOL ¤Î̾Á°¤òÊÖ¤¹¡£ */ + +/***@seealso + msymbol (), msymbol_exist () */ + +char * +msymbol_name (MSymbol symbol) +{ + return (symbol == Mnil ? "nil" : symbol->name); +} + +/*=*/ +/***en + @brief Set the value of a symbol property. + + The msymbol_put () function assigns $VAL to the value of the + symbol property that belongs to $SYMBOL and whose key is $KEY. If + the symbol property already has a value, $VAL overwrites the old + one. Both $SYMBOL and $KEY must not be #Mnil. + + If $KEY is a managing key, $VAL must be a managed object. In this + case, the reference count of the old value, if not @c NULL, is + decremented by one, and that of $VAL is incremented by one. + + @return + If the operation was successful, msymbol_put () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ËÃͤòÀßÄꤹ¤ë + + ´Ø¿ô msymbol_put () ¤Ï¡¢¥·¥ó¥Ü¥ë $SYMBOL Ãæ¤Ç¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥· + ¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ÎÃͤò $VAL ¤ËÀßÄꤹ¤ë¡£¤½¤Î¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤Ë¤¹ + ¤Ç¤ËÃͤ¬¤¢¤ì¤Ð¾å½ñ¤­¤¹¤ë¡£$SYMBOL, $KEY ¤È¤â´û¤ËÀ¸À®¤µ¤ì¤¿¥·¥ó¥Ü + ¥ë¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢msymbol_put () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤ò + ÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_SYMBOL + + @seealso + msymbol_get () */ + +int +msymbol_put (MSymbol symbol, MSymbol key, void *val) +{ + if (symbol == Mnil || key == Mnil) + MERROR (MERROR_SYMBOL, -1); + mplist_put (&symbol->plist, key, val); + return 0; +} + +/*=*/ + +/***en + @brief Get the value of a symbol property. + + The msymbol_get () function searches for the value of the symbol + property that belongs to $SYMBOL and whose key is $KEY. If + $SYMBOL has such a symbol property, its value is returned. + Otherwise @c NULL is returned. + + + @return + If an error is detected, msymbol_get () returns @c NULL and + assigns an error code to the external variable #merror_code. */ + +/***ja + @brief ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòõ¤¹ + + ´Ø¿ô msymbol_get () ¤Ï¡¢¥·¥ó¥Ü¥ë $SYMBOL ¤¬»ý¤Ä¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ + ¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·³ºÅö¤¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ + ¤¬Â¸ºß¤¹¤ì¤Ð¡¢¤½¤ì¤ÎÃͤòÊÖ¤¹¡£¤â¤·³ºÅö¤¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤¬Â¸ºß + ¤»¤º¡¢¤«¤Ä $KEY ¤ËÂФ¹¤ë¥×¥í¥Ð¥¤¥À´Ø¿ô¤¬Â¸ºß¤¹¤ì¤Ð¡¢¤½¤Î´Ø¿ô¤ò¸Æ¤ó + ¤Ç¤½¤ÎÌá¤êÃͤòÊÖ¤¹¡£³ºÅö¤¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤â¥×¥í¥Ð¥¤¥À´Ø¿ô¤â¸ + ºß¤·¤Ê¤¤¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code ¤òÊѤ¨¤º¤Ë @c NULL ¤òÊÖ¤¹¡£ + + ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô + #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ + +/*** + @errors + @c MERROR_SYMBOL + + @seealso + msymbol_put () */ + +void * +msymbol_get (MSymbol symbol, MSymbol key) +{ + MPlist *plist; + + if (symbol == Mnil || key == Mnil) + return NULL; + plist = &symbol->plist; + MPLIST_FIND (plist, key); + return (MPLIST_TAIL_P (plist) ? NULL : MPLIST_VAL (plist)); +} + +/*** @} */ + +#include + +/*** @addtogroup m17nDebug */ +/*=*/ +/*** @{ */ + +/***en + @brief Dump a symbol. + + The mdebug_dump_symbol () function prints $SYMBOL in a human + readable way to the stderr. $INDENT specifies how many columns to + indent the lines but the first one. + + @return + This function returns $SYMBOL. + + @errors + MERROR_DEBUG */ + +MSymbol +mdebug_dump_symbol (MSymbol symbol, int indent) +{ + char *prefix; + MPlist *plist; + char *name; + + if (indent < 0) + MERROR (MERROR_DEBUG, Mnil); + prefix = (char *) alloca (indent + 1); + memset (prefix, 32, indent); + prefix[indent] = 0; + + if (symbol == Mnil) + plist = NULL, name = "nil"; + else + plist = &symbol->plist, name = symbol->name; + + fprintf (stderr, "%s%s", prefix, name); + while (plist && MPLIST_KEY (plist) != Mnil) + { + fprintf (stderr, ":%s", MPLIST_KEY (plist)->name); + plist = MPLIST_NEXT (plist); + } + return symbol; +} + +/***en + @brief Dump all symbol names. + + The mdebug_dump_all_symbols () function prints names of all + symbols to the stderr. $INDENT specifies how many columns to + indent the lines but the first one. + + @return + This function returns #Mnil. + + @errors + MERROR_DEBUG */ + + +MSymbol +mdebug_dump_all_symbols (int indent) +{ + char *prefix; + int i; + MSymbol sym; + + if (indent < 0) + MERROR (MERROR_DEBUG, Mnil); + prefix = (char *) alloca (indent + 1); + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "(symbol-list"); + for (i = 0; i < SYMBOL_TABLE_SIZE; i++) + if ((sym = symbol_table[i])) + { + fprintf (stderr, "\n%s (%4d", prefix, i); + for (; sym; sym = sym->next) + fprintf (stderr, " '%s'", sym->name); + fprintf (stderr, ")"); + } + fprintf (stderr, ")"); + return Mnil; +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/symbol.h b/src/symbol.h new file mode 100644 index 0000000..24238d9 --- /dev/null +++ b/src/symbol.h @@ -0,0 +1,56 @@ +/* symbol.h -- header file for the symbol module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_SYMBOL_H_ +#define _M17N_SYMBOL_H_ + +#include "plist.h" + +struct MSymbol +{ + /** 1 iff a value of property (including text-property) whose key is + the symbol is a managed object. */ + unsigned managing_key : 1; + + /* Name of the symbol. */ + char *name; + + /* Byte length of . */ + int length; + + /* Plist of the symbol. */ + MPlist plist; + + MSymbol next; +}; + +#define MSYMBOL_NAME(sym) ((sym)->name) +#define MSYMBOL_NAMELEN(sym) ((sym)->length - 1) + +extern MSymbol msymbol__with_len (const char *name, int len); + +extern MSymbol msymbol__canonicalize (MSymbol sym); + +extern MTextPropSerializeFunc msymbol__serializer; +extern MTextPropDeserializeFunc msymbol__deserializer; + +#endif /* _M17N_SYMBOL_H_ */ diff --git a/src/textprop.c b/src/textprop.c new file mode 100644 index 0000000..4f8a45f --- /dev/null +++ b/src/textprop.c @@ -0,0 +1,2855 @@ +/* textprop.c -- text property module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +/***en + @addtogroup m17nTextProperty + @brief Function to handle text properties. + + Each character in an M-text can have properties called @e text @e + properties. Text properties store various kinds of information + attached to parts of an M-text to provide application programs + with a unified view of those information. As rich information can + be stored in M-texts in the form of text properties, functions in + application programs can be simple. + + A text property consists of a @e key and @e values, where key is a + symbol and values are anything that can be cast to (void + *). Unlike other types of properties, a text property can + have multiple values. "The text property whose key is K" may be + shortened to "K property". */ + +/***ja + @addtogroup m17nTextProperty + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÁàºî¤¹¤ë¤¿¤á¤Î´Ø¿ô + + M-text Æâ¤Î³Æʸ»ú¤Ï¡¢@e ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤ò + »ý¤Ä¤³¤È¤¬¤Ç¤­¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ë¤è¤Ã¤Æ¡¢¥Æ¥­¥¹¥È¤Î³ÆÉô°Ì¤Ë´Ø + ¤¹¤ëÍÍ¡¹¤Ê¾ðÊó¤ò M-text Æâ¤ËÊÝ»ý¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î¤¿¤á¡¢¤½¤ì¤é + ¤Î¾ðÊó¤ò¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÆâ¤ÇÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£¤Þ + ¤¿¡¢M-text ¼«ÂΤ¬Ë­É٤ʾðÊó¤ò»ý¤Ä¤¿¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à + Ãæ¤Î³Æ´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + + ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥­¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢ + ¤ê¡¢ÃÍ¤Ï (void *) ·¿¤Ë¥­¥ã¥¹¥È¤Ç¤­¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£ + ¾¤Î¥¿¥¤¥×¤Î¥×¥í¥Ñ¥Æ¥£¤È°Û¤Ê¤ê¡¢Æ±°ì¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÏÊ£¿ô¤ÎÃÍ + ¤ò»ý¤Ä¤³¤È¤¬µö¤µ¤ì¤ë¡£¡Ö@c Mxxx ¤È¤¤¤¦¥­¡¼¤ò»ý¤Ä¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¡× + ¤Î¤³¤È¤ò´Êñ¤Ë¡Öxxx ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */ + +/*=*/ + +#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) +/*** @addtogroup m17nInternal + @{ */ + +#include +#include +#include +#include + +#ifdef HAVE_XML2 +#include +#include +#include +#include +#endif + +#include "m17n.h" +#include "m17n-misc.h" +#include "internal.h" +#include "symbol.h" +#include "mtext.h" +#include "textprop.h" + +#define TEXT_PROP_DEBUG + +#undef xassert +#ifdef TEXT_PROP_DEBUG +#define xassert(X) do {if (!(X)) mdebug_hook ();} while (0) +#else +#define xassert(X) (void) 0 +#endif /* not FONTSET_DEBUG */ + +/* Hierarchy of objects (MText, MTextPlist, MInterval, MTextProperty) + +MText + | key/a key/b key/x + +--> MTextPlist -> MTextPlist -> ... -> MTextPlist + | | + | +- tail <-----------------------------------------+ + | | | + | +- head <--> MInterval <--> ... <--> MInterval <--+ + | + +- tail --------------------------------------------------------+ + | | + +- head --> MInterval <--> MInterval <--> ... <--> MInterval <--+ + | | + +---------------+------------> MTextProperty + +--> MTextProperty + ... + + +Examples: + +MTextProperty a/A [AAAAAAAAAAAAAAAAAAAAA] +MTextProperty a/B [BBBBBBBBBBBBBBBBB] +MTextPlist a |--intvl1--|-intvl2-|-intvl3-|---intvl4---|-intvl5-| + + +MTextProperty b/A [AAAAAAAAAA] +MTextProperty b/B [BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB] +MTextPlist b |-intvl1-|--intvl2--|--intvl3--|-intvl4-|--intvl5--| + + M-text |--------------------------------------------------| + + (intvl == MInterval) + +*/ + +/* The structure MTextProperty is defined in textprop.h. */ + +/** MInterval is the structure for an interval that holds text + properties of the same key in a specific range of M-text. + All intervals are stored in MIntervalPool. */ + +typedef struct MInterval MInterval; + +struct MInterval +{ + /** Stack of pointers to text properties. If the interval does not + have any text properties, this member is NULL or contains random + values. */ + MTextProperty **stack; + + /** How many values are in . */ + int nprops; + + /** Length of . */ + int stack_length; + + /** Start and end character positions of the interval. If is + negative, this interval is not in use. */ + int start, end; + + /** Pointers to the previous and next intervals. If is 0, + is NULL and this interval is pointed by MTextPlist->head. + If is the size of the M-text, is NULL, and this + interval is pointed by MTextPlist->tail. */ + MInterval *prev, *next; +}; + +/** MTextPlist is a structure to hold text properties of an M-text by + chain. Each element in the chain is for a specific key. */ + +typedef struct MTextPlist MTextPlist; + +struct MTextPlist +{ + /** Key of the property. */ + MSymbol key; + + /** The head and tail intervals. ->start is always 0. + end is always MText->nchars. */ + MInterval *head, *tail; + + /** Lastly accessed interval. */ + MInterval *cache; + + /* Not yet implemented. */ + int (*modification_hook) (MText *mt, MSymbol key, int from, int to); + + /** Pointer to the next property in the chain, or NULL if the + property is the last one in the chain. */ + MTextPlist *next; +}; + + +/** How many intervals one interval-pool can contain. */ + +#define INTERVAL_POOL_SIZE 1024 + +typedef struct MIntervalPool MIntervalPool; + + +/** MIntervalPool is the structure for an interval-pool which store + intervals. Each interval-pool contains INTERVAL_POOL_SIZE number + of intervals, and is chained from the root #interval_pool. */ + +struct MIntervalPool +{ + /** Array of intervals. */ + MInterval intervals[INTERVAL_POOL_SIZE]; + + /** The smallest index to an unused interval. */ + int free_slot; + + /** Pointer to the next interval-pool. */ + MIntervalPool *next; +}; + + +/** Root of interval-pools. */ + +static MIntervalPool interval_pool_root; + +/* For debugging. */ + +static M17NObjectArray text_property_table; + +/** Return a newly allocated interval pool. */ + +static MIntervalPool * +new_interval_pool () +{ + MIntervalPool *pool; + int i; + + MSTRUCT_CALLOC (pool, MERROR_TEXTPROP); + for (i = 0; i < INTERVAL_POOL_SIZE; i++) + pool->intervals[i].end = -1; + pool->free_slot = 0; + pool->next = NULL; + return pool; +} + + +/** Return a new interval for the region START and END. */ + +static MInterval * +new_interval (int start, int end) +{ + MIntervalPool *pool; + MInterval *interval; + + for (pool = &interval_pool_root; + pool->free_slot >= INTERVAL_POOL_SIZE; + pool = pool->next) + { + if (! pool->next) + pool->next = new_interval_pool (); + } + + interval = &(pool->intervals[pool->free_slot]); + interval->stack = NULL; + interval->nprops = 0; + interval->stack_length = 0; + interval->prev = interval->next = NULL; + interval->start = start; + interval->end = end; + + pool->free_slot++; + while (pool->free_slot < INTERVAL_POOL_SIZE + && pool->intervals[pool->free_slot].end >= 0) + pool->free_slot++; + + return interval; +} + + +/** Free INTERVAL and return INTERVAL->next. It assumes that INTERVAL + has no properties. */ + +static MInterval * +free_interval (MInterval *interval) +{ + MIntervalPool *pool = &interval_pool_root; + int i; + + xassert (interval->nprops == 0); + if (interval->stack) + free (interval->stack); + while ((interval < pool->intervals + || interval >= pool->intervals + INTERVAL_POOL_SIZE) + && pool->next) + pool = pool->next; + + i = interval - pool->intervals; + interval->end = -1; + if (i < pool->free_slot) + pool->free_slot = i; + return interval->next; +} + + +/** If necessary, allocate a stack for INTERVAL so that it can contain + NUM number of text properties. */ + +#define PREPARE_INTERVAL_STACK(interval, num) \ + do { \ + if ((num) > (interval)->stack_length) \ + { \ + MTABLE_REALLOC ((interval)->stack, (num), MERROR_TEXTPROP); \ + (interval)->stack_length = (num); \ + } \ + } while (0) + + +/** Return a copy of INTERVAL. The copy still shares text properties + with INTERVAL. If MASK_BITS is not zero, don't copy such text + properties whose control flags contains bits in MASK_BITS. */ + +static MInterval * +copy_interval (MInterval *interval, int mask_bits) +{ + MInterval *new = new_interval (interval->start, interval->end); + int nprops = interval->nprops; + MTextProperty **props = alloca (sizeof (MTextProperty *) * nprops); + int i, n; + + for (i = n = 0; i < nprops; i++) + if (! (interval->stack[i]->control.flag & mask_bits)) + props[n++] = interval->stack[i]; + new->nprops = n; + if (n > 0) + { + PREPARE_INTERVAL_STACK (new, n); + memcpy (new->stack, props, sizeof (MTextProperty *) * n); + } + + return new; +} + + +/** Free text property OBJECT. */ + +static void +free_text_property (void *object) +{ + MTextProperty *prop = (MTextProperty *) object; + + if (prop->key->managing_key) + M17N_OBJECT_UNREF (prop->val); + M17N_OBJECT_UNREGISTER (text_property_table, prop); + free (object); +} + + +/** Return a newly allocated text property whose key is KEY and value + is VAL. */ + +static MTextProperty * +new_text_property (MText *mt, int from, int to, MSymbol key, void *val, + int control_bits) +{ + MTextProperty *prop; + + M17N_OBJECT (prop, free_text_property, MERROR_TEXTPROP); + prop->control.flag = control_bits; + prop->attach_count = 0; + prop->mt = mt; + prop->start = from; + prop->end = to; + prop->key = key; + prop->val = val; + if (key->managing_key) + M17N_OBJECT_REF (val); + M17N_OBJECT_REGISTER (text_property_table, prop); + return prop; +} + + +/** Return a newly allocated copy of text property PROP. */ + +#define COPY_TEXT_PROPERTY(prop) \ + new_text_property ((prop)->mt, (prop)->start, (prop)->end, \ + (prop)->key, (prop)->val, (prop)->control.flag) + + +/** Split text property PROP at position INTERVAL->start, and make all + the following intervals contain the copy of PROP instead of PROP. + It assumes that PROP starts before INTERVAL. */ + +static void +split_property (MTextProperty *prop, MInterval *interval) +{ + int end = prop->end; + MTextProperty *copy; + int i; + + prop->end = interval->start; + copy = COPY_TEXT_PROPERTY (prop); + copy->start = interval->start; + copy->end = end; + /* Check all stacks of the following intervals, and if it contains + PROP, change it to the copy of it. */ + for (; interval && interval->start < end; interval = interval->next) + for (i = 0; i < interval->nprops; i++) + if (interval->stack[i] == prop) + { + interval->stack[i] = copy; + M17N_OBJECT_REF (copy); + copy->attach_count++; + prop->attach_count--; + M17N_OBJECT_UNREF (prop); + } + M17N_OBJECT_UNREF (copy); +} + +/** Divide INTERVAL of PLIST at POS if POS is in between the range of + INTERVAL. */ + +static void +divide_interval (MTextPlist *plist, MInterval *interval, int pos) +{ + MInterval *new; + int i; + + if (pos == interval->start || pos == interval->end) + return; + new = copy_interval (interval, 0); + interval->end = new->start = pos; + new->prev = interval; + new->next = interval->next; + interval->next = new; + if (new->next) + new->next->prev = new; + if (plist->tail == interval) + plist->tail = new; + for (i = 0; i < new->nprops; i++) + { + new->stack[i]->attach_count++; + M17N_OBJECT_REF (new->stack[i]); + } +} + + +/** Check if INTERVAL of PLIST can be merged with INTERVAL->next. If + mergeable, extend INTERVAL to the end of INTEVAL->next, free + INTERVAL->next, and return INTERVAL. Otherwise, return + INTERVAL->next. */ + +static MInterval * +maybe_merge_interval (MTextPlist *plist, MInterval *interval) +{ + int nprops = interval->nprops; + MInterval *next = interval->next; + int i, j; + + if (! next || nprops != next->nprops) + return next; + + for (i = 0; i < nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + MTextProperty *old = next->stack[i]; + + if (prop != old + && (prop->val != old->val + || prop->end != old->start + || prop->control.flag & MTEXTPROP_NO_MERGE + || old->control.flag & MTEXTPROP_NO_MERGE)) + return interval->next; + } + + + for (i = 0; i < nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + MTextProperty *old = next->stack[i]; + + if (prop != old) + { + MInterval *tail; + + for (tail = next->next; tail && tail->start < old->end; + tail = tail->next) + for (j = 0; j < tail->nprops; j++) + if (tail->stack[j] == old) + { + old->attach_count--; + xassert (old->attach_count); + tail->stack[j] = prop; + prop->attach_count++; + M17N_OBJECT_REF (prop); + } + xassert (old->attach_count == 1); + old->mt = NULL; + prop->end = old->end; + } + old->attach_count--; + M17N_OBJECT_UNREF (old); + } + + interval->end = next->end; + interval->next = next->next; + if (next->next) + next->next->prev = interval; + if (plist->tail == next) + plist->tail = interval; + plist->cache = interval; + next->nprops = 0; + free_interval (next); + return interval; +} + + +/*** Adjust start and end positions of intervals between HEAD and TAIL + (both inclusive) by diff. Adjust also start and end positions + of text properties belonging to those intervals. */ + +static void +adjust_intervals (MInterval *head, MInterval *tail, int diff) +{ + int i; + MTextProperty *prop; + + if (diff < 0) + { + /* Adjust end poistions of properties starting before HEAD. */ + for (i = 0; i < head->nprops; i++) + { + prop = head->stack[i]; + if (prop->start < head->start) + prop->end += diff; + } + + /* Adjust start and end positions of properties starting at + HEAD, and adjust HEAD itself. */ + while (1) + { + for (i = 0; i < head->nprops; i++) + { + prop = head->stack[i]; + if (prop->start == head->start) + prop->start += diff, prop->end += diff; + } + head->start += diff; + head->end += diff; + if (head == tail) + break; + head = head->next; + } + } + else + { + /* Adjust start poistions of properties ending after TAIL. */ + for (i = 0; i < tail->nprops; i++) + { + prop = tail->stack[i]; + if (prop->end > tail->end) + prop->start += diff; + } + + /* Adjust start and end positions of properties ending at + TAIL, and adjust TAIL itself. */ + while (1) + { + for (i = 0; i < tail->nprops; i++) + { + prop = tail->stack[i]; + if (prop->end == tail->end) + prop->start += diff, prop->end += diff; + } + tail->start += diff; + tail->end += diff; + if (tail == head) + break; + tail = tail->prev; + } + } +} + +/* Return an interval of PLIST that covers the position POS. */ + +static MInterval * +find_interval (MTextPlist *plist, int pos) +{ + MInterval *interval; + MInterval *highest; + + if (pos < plist->head->end) + return plist->head; + if (pos >= plist->tail->start) + return (pos < plist->tail->end ? plist->tail : NULL); + + interval = plist->cache; + + if (pos < interval->start) + highest = interval->prev, interval = plist->head->next; + else if (pos < interval->end) + return interval; + else + highest = plist->tail->prev, interval = interval->next; + + if (pos - interval->start < highest->end - pos) + { + while (interval->end <= pos) + /* Here, we are sure that POS is not included in PLIST->tail, + thus, INTERVAL->next always points a valid next + interval. */ + interval = interval->next; + } + else + { + while (highest->start > pos) + highest = highest->prev; + interval = highest; + } + plist->cache = interval; + return interval; +} + +/* Push text property PROP on the stack of INTERVAL. */ + +#define PUSH_PROP(interval, prop) \ + do { \ + int n = (interval)->nprops; \ + \ + PREPARE_INTERVAL_STACK ((interval), n + 1); \ + (interval)->stack[n] = (prop); \ + (interval)->nprops += 1; \ + (prop)->attach_count++; \ + M17N_OBJECT_REF (prop); \ + if ((prop)->start > (interval)->start) \ + (prop)->start = (interval)->start; \ + if ((prop)->end < (interval)->end) \ + (prop)->end = (interval)->end; \ + } while (0) + + +/* Pop the topmost text property of INTERVAL from the stack. If it + ends after INTERVAL->end, split it. */ + +#define POP_PROP(interval) \ + do { \ + MTextProperty *prop; \ + \ + (interval)->nprops--; \ + prop = (interval)->stack[(interval)->nprops]; \ + xassert (prop->control.ref_count > 0); \ + xassert (prop->attach_count > 0); \ + if (prop->start < (interval)->start) \ + { \ + if (prop->end > (interval)->end) \ + split_property (prop, (interval)->next); \ + prop->end = (interval)->start; \ + } \ + else if (prop->end > (interval)->end) \ + prop->start = (interval)->end; \ + prop->attach_count--; \ + if (! prop->attach_count) \ + prop->mt = NULL; \ + M17N_OBJECT_UNREF (prop); \ + } while (0) + + +#define REMOVE_PROP(interval, prop) \ + do { \ + int i; \ + \ + for (i = (interval)->nprops - 1; i >= 0; i--) \ + if ((interval)->stack[i] == (prop)) \ + break; \ + if (i < 0) \ + break; \ + (interval)->nprops--; \ + for (; i < (interval)->nprops; i++) \ + (interval)->stack[i] = (interval)->stack[i + 1]; \ + (prop)->attach_count--; \ + if (! (prop)->attach_count) \ + (prop)->mt = NULL; \ + M17N_OBJECT_UNREF (prop); \ + } while (0) + + +#ifdef TEXT_PROP_DEBUG +static int +check_plist (MTextPlist *plist, int start) +{ + MInterval *interval = plist->head; + MInterval *cache = plist->cache; + int cache_found = 0; + + if (interval->start != start + || interval->start >= interval->end) + return mdebug_hook (); + while (interval) + { + int i; + + if (interval == interval->next) + return mdebug_hook (); + + if (interval == cache) + cache_found = 1; + + if (interval->start >= interval->end) + return mdebug_hook (); + if ((interval->next + ? (interval->end != interval->next->start + || interval != interval->next->prev) + : interval != plist->tail)) + return mdebug_hook (); + for (i = 0; i < interval->nprops; i++) + { + if (interval->stack[i]->start > interval->start + || interval->stack[i]->end < interval->end) + return mdebug_hook (); + + if (! interval->stack[i]->attach_count) + return mdebug_hook (); + if (! interval->stack[i]->mt) + return mdebug_hook (); + if (interval->stack[i]->start == interval->start) + { + MTextProperty *prop = interval->stack[i]; + int count = prop->attach_count - 1; + MInterval *interval2; + + for (interval2 = interval->next; + interval2 && interval2->start < prop->end; + count--, interval2 = interval2->next) + if (count == 0) + return mdebug_hook (); + } + + if (interval->stack[i]->end > interval->end) + { + MTextProperty *prop = interval->stack[i]; + MInterval *interval2; + int j; + + for (interval2 = interval->next; + interval2 && interval2->start < prop->end; + interval2 = interval2->next) + { + for (j = 0; j < interval2->nprops; j++) + if (interval2->stack[j] == prop) + break; + if (j == interval2->nprops) + return mdebug_hook (); + } + } + if (interval->stack[i]->start < interval->start) + { + MTextProperty *prop = interval->stack[i]; + MInterval *interval2; + int j; + + for (interval2 = interval->prev; + interval2 && interval2->end > prop->start; + interval2 = interval2->prev) + { + for (j = 0; j < interval2->nprops; j++) + if (interval2->stack[j] == prop) + break; + if (j == interval2->nprops) + return mdebug_hook (); + } + } + } + interval = interval->next; + } + if (! cache_found) + return mdebug_hook (); + if (plist->head->prev || plist->tail->next) + return mdebug_hook (); + return 0; +} +#endif + + +/** Return a copy of plist that contains intervals between FROM and TO + of PLIST. The copy goes to the position POS of M-text MT. */ + +static MTextPlist * +copy_single_property (MTextPlist *plist, int from, int to, MText *mt, int pos) +{ + MTextPlist *new; + MInterval *interval1, *interval2; + MTextProperty *prop; + int diff = pos - from; + int i, j; + int mask_bits = MTEXTPROP_VOLATILE_STRONG | MTEXTPROP_VOLATILE_WEAK; + + MSTRUCT_CALLOC (new, MERROR_TEXTPROP); + new->key = plist->key; + new->next = NULL; + + interval1 = find_interval (plist, from); + new->head = copy_interval (interval1, mask_bits); + for (interval1 = interval1->next, interval2 = new->head; + interval1 && interval1->start < to; + interval1 = interval1->next, interval2 = interval2->next) + { + interval2->next = copy_interval (interval1, mask_bits); + interval2->next->prev = interval2; + } + new->tail = interval2; + new->head->start = from; + new->tail->end = to; + for (interval1 = new->head; interval1; interval1 = interval1->next) + for (i = 0; i < interval1->nprops; i++) + if (interval1->start == interval1->stack[i]->start + || interval1 == new->head) + { + prop = interval1->stack[i]; + interval1->stack[i] = COPY_TEXT_PROPERTY (prop); + interval1->stack[i]->mt = mt; + interval1->stack[i]->attach_count++; + if (interval1->stack[i]->start < from) + interval1->stack[i]->start = from; + if (interval1->stack[i]->end > to) + interval1->stack[i]->end = to; + for (interval2 = interval1->next; interval2; + interval2 = interval2->next) + for (j = 0; j < interval2->nprops; j++) + if (interval2->stack[j] == prop) + { + interval2->stack[j] = interval1->stack[i]; + interval1->stack[i]->attach_count++; + M17N_OBJECT_REF (interval1->stack[i]); + } + } + adjust_intervals (new->head, new->tail, diff); + new->cache = new->head; + for (interval1 = new->head; interval1 && interval1->next; + interval1 = maybe_merge_interval (new, interval1)); + xassert (check_plist (new, pos) == 0); + if (new->head == new->tail + && new->head->nprops == 0) + { + free_interval (new->head); + free (new); + new = NULL; + } + + return new; +} + +/** Return a newly allocated plist whose key is KEY on M-text MT. */ + +static MTextPlist * +new_plist (MText *mt, MSymbol key) +{ + MTextPlist *plist; + + MSTRUCT_MALLOC (plist, MERROR_TEXTPROP); + plist->key = key; + plist->head = new_interval (0, mtext_nchars (mt)); + plist->tail = plist->head; + plist->cache = plist->head; + plist->next = mt->plist; + mt->plist = plist; + return plist; +} + +/* Free PLIST and return PLIST->next. */ + +static MTextPlist * +free_textplist (MTextPlist *plist) +{ + MTextPlist *next = plist->next; + MInterval *interval = plist->head; + + while (interval) + { + while (interval->nprops > 0) + POP_PROP (interval); + interval = free_interval (interval); + } + free (plist); + return next; +} + +/** Return a plist that contains the property KEY of M-text MT. If + such a plist does not exist and CREATE is nonzero, create a new + plist and return it. */ + +static MTextPlist * +get_plist_create (MText *mt, MSymbol key, int create) +{ + MTextPlist *plist; + + plist = mt->plist; + while (plist && plist->key != key) + plist = plist->next; + + /* If MT does not have PROP, make one. */ + if (! plist && create) + plist = new_plist (mt, key); + return plist; +} + +/* Detach PROP. INTERVAL (if not NULL) contains PROP. */ + +static void +detach_property (MTextPlist *plist, MTextProperty *prop, MInterval *interval) +{ + MInterval *head; + int to = prop->end; + + xassert (prop->mt); + xassert (plist); + + M17N_OBJECT_REF (prop); + if (interval) + while (interval->start > prop->start) + interval = interval->prev; + else + interval = find_interval (plist, prop->start); + head = interval; + while (1) + { + REMOVE_PROP (interval, prop); + if (interval->end == to) + break; + interval = interval->next; + } + xassert (prop->attach_count == 0 && prop->mt == NULL); + M17N_OBJECT_UNREF (prop); + + while (head && head->end <= to) + head = maybe_merge_interval (plist, head); + xassert (check_plist (plist, 0) == 0); +} + +/* Delete text properties of PLIST between FROM and TO. MASK_BITS + specifies what kind of properties to delete. If DELETING is + nonzero, delete such properties too that are completely included in + the region. + + If the resulting PLIST still has any text properties, return 1, + else return 0. */ + +static int +delete_properties (MTextPlist *plist, int from, int to, + int mask_bits, int deleting) +{ + MInterval *interval; + int modified = 0; + int modified_from = from; + int modified_to = to; + int i; + + retry: + for (interval = find_interval (plist, from); + interval && interval->start < to; + interval = interval->next) + for (i = 0; i < interval->nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + + if (prop->control.flag & mask_bits) + { + if (prop->start < modified_from) + modified_from = prop->start; + if (prop->end > modified_to) + modified_to = prop->end; + detach_property (plist, prop, interval); + modified++; + goto retry; + } + else if (deleting && prop->start >= from && prop->end <= to) + { + detach_property (plist, prop, interval); + modified++; + goto retry; + } + } + + if (modified) + { + interval = find_interval (plist, modified_from); + while (interval && interval->start < modified_to) + interval = maybe_merge_interval (plist, interval); + } + + return (plist->head != plist->tail || plist->head->nprops > 0); +} + +static void +pop_interval_properties (MInterval *interval) +{ + while (interval->nprops > 0) + POP_PROP (interval); +} + + +MInterval * +pop_all_properties (MTextPlist *plist, int from, int to) +{ + MInterval *interval; + + /* Be sure to have interval boundary at TO. */ + interval = find_interval (plist, to); + if (interval && interval->start < to) + divide_interval (plist, interval, to); + + /* Be sure to have interval boundary at FROM. */ + interval = find_interval (plist, from); + if (interval->start < from) + { + divide_interval (plist, interval, from); + interval = interval->next; + } + + pop_interval_properties (interval); + while (interval->end < to) + { + MInterval *next = interval->next; + + pop_interval_properties (next); + interval->end = next->end; + interval->next = next->next; + if (interval->next) + interval->next->prev = interval; + if (next == plist->tail) + plist->tail = interval; + if (plist->cache == next) + plist->cache = interval; + free_interval (next); + } + return interval; +} + + +/* Delete volatile text properties between FROM and TO. If KEY is + Mnil, we are going to delete text, thus both strongly and weakly + volatile properties must be deleted. Otherwise we are going to + modify a text property KEY, thus only strongly volatile properties + whose key is not KEY must be deleted. */ + +static void +prepare_to_modify (MText *mt, int from, int to, MSymbol key) +{ + MTextPlist *plist = mt->plist, *prev = NULL; + int mask_bits = MTEXTPROP_VOLATILE_STRONG; + int deleting = (key == Mnil) && (from < to); + + if (key == Mnil) + mask_bits |= MTEXTPROP_VOLATILE_WEAK; + while (plist) + { + if (plist->key != key + && ! delete_properties (plist, from, to, mask_bits, deleting)) + { + if (prev) + plist = prev->next = free_textplist (plist); + else + plist = mt->plist = free_textplist (plist); + } + else + prev = plist, plist = plist->next; + } +} + +void +extract_text_properties (MText *mt, int from, int to, MSymbol key, + MPlist *plist) +{ + MPlist *top; + MTextPlist *list = get_plist_create (mt, key, 0); + MInterval *interval; + + if (! list) + return; + interval = find_interval (list, from); + if (interval->nprops == 0 + && interval->start <= from && interval->end >= to) + return; + top = plist; + while (interval && interval->start < to) + { + if (interval->nprops == 0) + top = mplist_find_by_key (top, Mnil); + else + { + MPlist *current = top, *place; + int i; + + for (i = 0; i < interval->nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + + place = mplist_find_by_value (current, prop); + if (place) + current = MPLIST_NEXT (place); + else + { + place = mplist_find_by_value (top, prop); + if (place) + { + mplist_pop (place); + if (MPLIST_NEXT (place) == MPLIST_NEXT (current)) + current = place; + } + mplist_push (current, Mt, prop); + current = MPLIST_NEXT (current); + } + } + } + interval = interval->next; + } + return; +} + +#define XML_TEMPLATE "\n\ +\n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + ]>\n\ +\n\ +" + + +/* for debugging... */ +#include + +void +dump_interval (MInterval *interval, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + int i; + + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "(interval %d-%d (%d)", interval->start, interval->end, + interval->nprops); + for (i = 0; i < interval->nprops; i++) + fprintf (stderr, "\n%s (%d %d/%d %d-%d 0x%x)", + prefix, i, + interval->stack[i]->control.ref_count, + interval->stack[i]->attach_count, + interval->stack[i]->start, interval->stack[i]->end, + (unsigned) interval->stack[i]->val); + fprintf (stderr, ")"); +} + +void +dump_textplist (MTextPlist *plist, int indent) +{ + char *prefix = (char *) alloca (indent + 1); + + memset (prefix, 32, indent); + prefix[indent] = 0; + + fprintf (stderr, "(properties"); + if (! plist) + fprintf (stderr, ")\n"); + else + { + fprintf (stderr, "\n"); + while (plist) + { + MInterval *interval = plist->head; + + fprintf (stderr, "%s (%s", prefix, msymbol_name (plist->key)); + while (interval) + { + fprintf (stderr, " (%d %d", interval->start, interval->end); + if (interval->nprops > 0) + { + int i; + + for (i = 0; i < interval->nprops; i++) + fprintf (stderr, " 0x%x", (int) interval->stack[i]->val); + } + fprintf (stderr, ")"); + interval = interval->next; + } + fprintf (stderr, ")\n"); + xassert (check_plist (plist, 0) == 0); + plist = plist->next; + } + } +} + + +/* Internal API */ + +int +mtext__prop_init () +{ + text_property_table.count = 0; + Mtext_prop_serializer = msymbol ("text-prop-serializer"); + Mtext_prop_deserializer = msymbol ("text-prop-deserializer"); + return 0; +} + +void +mtext__prop_fini () +{ + MIntervalPool *pool = interval_pool_root.next; + + while (pool) + { + MIntervalPool *next = pool->next; + free (pool); + pool = next; + } + interval_pool_root.next = NULL; + mdebug__report_object ("Text property", &text_property_table); +} + + +/** Free all plists. */ + +void +mtext__free_plist (MText *mt){ + + MTextPlist *plist = mt->plist; + + while (plist) + plist = free_textplist (plist); + mt->plist = NULL; +} + + +/** Extract intervals between FROM and TO of all properties (except + for volatile ones) in PLIST, and make a new plist from them for + M-text MT. */ + +MTextPlist * +mtext__copy_plist (MTextPlist *plist, int from, int to, MText *mt, int pos) +{ + MTextPlist *copy, *this; + + if (from == to) + return NULL; + for (copy = NULL; plist && ! copy; plist = plist->next) + copy = copy_single_property (plist, from, to, mt, pos); + if (! plist) + return copy; + for (; plist; plist = plist->next) + if ((this = copy_single_property (plist, from, to, mt, pos))) + { + this->next = copy; + copy = this; + } + + return copy; +} + +void +mtext__adjust_plist_for_delete (MText *mt, int pos, int len) +{ + MTextPlist *plist; + int to; + + if (len == 0 || pos == mt->nchars) + return; + if (len == mt->nchars) + { + mtext__free_plist (mt); + return; + } + + to = pos + len; + prepare_to_modify (mt, pos, to, Mnil); + for (plist = mt->plist; plist; plist = plist->next) + { + MInterval *interval = pop_all_properties (plist, pos, to); + MInterval *prev = interval->prev, *next = interval->next; + + if (prev) + prev->next = next; + else + plist->head = next; + if (next) + { + adjust_intervals (next, plist->tail, -len); + next->prev = prev; + } + else + plist->tail = prev; + if (prev && next) + next = maybe_merge_interval (plist, prev); + plist->cache = next ? next : prev; + free_interval (interval); + xassert (check_plist (plist, 0) == 0); + } +} + +void +mtext__adjust_plist_for_insert (MText *mt, int pos, int nchars, + MTextPlist *plist) +{ + MTextPlist *pl, *pl_last, *pl2, *p; + int i; + MInterval *interval; + + if (mt->nchars == 0) + { + mtext__free_plist (mt); + mt->plist = plist; + return; + } + if (pos > 0 && pos < mtext_nchars (mt)) + prepare_to_modify (mt, pos, pos, Mnil); + + for (pl_last = NULL, pl = mt->plist; pl; pl_last = pl, pl = pl->next) + { + MInterval *interval, *prev, *next, *head, *tail; + + if (pos == 0) + prev = NULL, next = pl->head; + else if (pos == mtext_nchars (mt)) + prev = pl->tail, next = NULL; + else + { + next = find_interval (pl, pos); + if (next->start < pos) + { + divide_interval (pl, next, pos); + next = next->next; + } + for (i = 0; i < next->nprops; i++) + if (next->stack[i]->start < pos) + split_property (next->stack[i], next); + prev = next->prev; + } + + xassert (check_plist (pl, 0) == 0); + for (p = NULL, pl2 = plist; pl2 && pl->key != pl2->key; + p = pl2, pl2 = p->next); + if (pl2) + { + xassert (check_plist (pl2, pl2->head->start) == 0); + if (p) + p->next = pl2->next; + else + plist = plist->next; + + head = pl2->head; + tail = pl2->tail; + free (pl2); + } + else + { + head = tail = new_interval (pos, pos + nchars); + } + head->prev = prev; + tail->next = next; + if (prev) + prev->next = head; + else + pl->head = head; + if (next) + next->prev = tail; + else + pl->tail = tail; + if (next) + adjust_intervals (next, pl->tail, nchars); + + xassert (check_plist (pl, 0) == 0); + if (prev && prev->nprops > 0) + { + for (interval = prev; + interval->next != next && interval->next->nprops == 0; + interval = interval->next) + for (i = 0; i < interval->nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + + if (prop->control.flag & MTEXTPROP_REAR_STICKY) + PUSH_PROP (interval->next, prop); + } + } + xassert (check_plist (pl, 0) == 0); + if (next && next->nprops > 0) + { + for (interval = next; + interval->prev != prev && interval->prev->nprops == 0; + interval = interval->prev) + for (i = 0; i < interval->nprops; i++) + { + MTextProperty *prop = interval->stack[i]; + + if (prop->control.flag & MTEXTPROP_FRONT_STICKY) + PUSH_PROP (interval->prev, prop); + } + } + + interval = prev ? prev : pl->head; + pl->cache = interval; + while (interval && interval->start <= pos + nchars) + interval = maybe_merge_interval (pl, interval); + xassert (check_plist (pl, 0) == 0); + } + + if (pl_last) + pl_last->next = plist; + else + mt->plist = plist; + + for (; plist; plist = plist->next) + { + plist->cache = plist->head; + if (pos > 0) + { + if (plist->head->nprops) + { + interval = new_interval (0, pos); + interval->next = plist->head; + plist->head->prev = interval; + plist->head = interval; + } + else + plist->head->start = 0; + } + if (pos < mtext_nchars (mt)) + { + if (plist->tail->nprops) + { + interval = new_interval (pos + nchars, + mtext_nchars (mt) + nchars); + interval->prev = plist->tail; + plist->tail->next = interval; + plist->tail = interval; + } + else + plist->tail->end = mtext_nchars (mt) + nchars; + } + xassert (check_plist (plist, 0) == 0); + } +} + +/*** @} */ +#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ + + +/** External API */ + +/*** @addtogroup m17nTextProperty */ +/*** @{ */ + +/*=*/ +/***en + @brief Get the value of the topmost text property. + + The mtext_get_prop () function searches the character at $POS in + M-text $MT for the text property whose key is $KEY. + + @return + If a text property is found, mtext_get_prop () returns the value + of the property. If the property has multiple values, it returns + the topmost one. If no such property is found, it returns @c NULL + without changing the external variable #merror_code. + + If an error is detected, mtext_get_prop () returns @c NULL and + assigns an error code to the external variable #merror_code. + + @note If @c NULL is returned without an error, there are two + possibilities: + + @li the character at $POS does not have a property whose key is $KEY, or + + @li the character does have such a property and its value is @c NULL. + + If you need to distinguish these two cases, use the + mtext_get_prop_values () function instead. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ìÈÖ¾å¤ÎÃͤòÆÀ¤ë + + ´Ø¿ô mtext_get_prop () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃ֤ˤ¢¤ëʸ + »ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£ + + @return + ¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬Â¸ºß¤¹¤ë¤Ê¤é¡¢mtext_get_prop () ¤Ï¤½¤ÎÃͤòÊÖ + ¤¹¡£Ãͤ¬Ê£¿ô¸ºß¤¹¤ë¤È¤­¤Ï¡¢°ìÈÖ¾å¤ÎÃͤòÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð³°Éô + ÊÑ¿ô #merror_code ¤òÊѹ¹¤¹¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£ + + ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë + ¥¨¥é¡¼ ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @note ¥¨¥é¡¼¤Ê¤·¤Ç @c NULL ¤¬ÊÖ¤µ¤ì¤¿¾ì¹ç¤Ë¤ÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë¡£ + + @li $POS ¤Î°ÌÃÖ¤Îʸ»ú¤Ï $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¡£ + + @li ¤½¤Îʸ»ú¤Ï $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤¬¡¢¤½¤ÎÃͤ¬ @c + NULL ¤Ç¤¢¤ë¡£ + + ¤³¤ÎÆó¤Ä¤ò¶èÊ̤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢´Ø¿ô mtext_get_prop_values () + ¤ò»ÈÍѤ¹¤ë¤³¤È¡£ + + @latexonly \IPAlabel{mtext_get_prop} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_get_prop_values (), mtext_put_prop (), mtext_put_prop_values (), + mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ + +void * +mtext_get_prop (MText *mt, int pos, MSymbol key) +{ + MTextPlist *plist; + MInterval *interval; + void *val; + + M_CHECK_POS (mt, pos, NULL); + + plist = get_plist_create (mt, key, 0); + if (! plist) + return NULL; + + interval = find_interval (plist, pos); + val = (interval->nprops + ? interval->stack[interval->nprops - 1]->val : NULL); + return val; +} + +/*=*/ + +/***en + @brief Get multiple values of a text property + + The mtext_get_prop_values () function searches the character at + $POS in M-text $MT for the property whose key is $KEY. If such + a property is found, its values are stored in the memory area + pointed to by $VALUES. $NUM limits the maximum number of stored + values. + + @return + If the operation was successful, mtext_get_prop_values () returns + the number of actually stored values. If the character at $POS + does not have a property whose key is $KEY, the return value is + 0. If an error is detected, mtext_get_prop_values () returns -1 and + assigns an error code to the external variable #merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊ£¿ôÆÀ¤ë + + ´Ø¿ô mtext_get_prop_values () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃÖ + ¤Ë¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·¤½ + ¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ì¤¬»ý¤ÄÃÍ(Ê£¿ô²Ä)¤ò $VALUES ¤Î + »Ø¤¹¥á¥â¥êÎΰè¤Ë³ÊǼ¤¹¤ë¡£$NUM ¤Ï³ÊǼ¤¹¤ëÃͤοô¤Î¾å¸Â¤Ç¤¢¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_prop_values () ¤Ï¼ÂºÝ¤Ë¥á¥â¥ê¤Ë³ÊǼ¤µ + ¤ì¤¿Ãͤοô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬ $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£ + ¤ò»ý¤¿¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô + #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_get_prop_values} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (), + mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ + +int +mtext_get_prop_values (MText *mt, int pos, MSymbol key, + void **values, int num) +{ + MTextPlist *plist; + MInterval *interval; + int nprops; + int i; + int offset; + + M_CHECK_POS (mt, pos, -1); + + plist = get_plist_create (mt, key, 0); + if (! plist) + return 0; + + interval = find_interval (plist, pos); + /* It is assured that INTERVAL is not NULL. */ + nprops = interval->nprops; + if (nprops == 0 || num <= 0) + return 0; + if (nprops == 1 || num == 1) + { + values[0] = interval->stack[nprops - 1]->val; + return 1; + } + + if (nprops <= num) + num = nprops, offset = 0; + else + offset = nprops - num; + for (i = 0; i < num; i++) + values[i] = interval->stack[offset + i]->val; + return num; +} + +/*=*/ + +/***en + @brief Get list of text property keys at a position of an M-text. + + The mtext_get_prop_keys () function creates an array whose + elements are the keys of text properties found at position $POS in + M-text $MT, and sets *$KEYS to the address of the created array. + The user is responsible to free the memory allocated for + the array. + + @returns + + If the operation was successful, mtext_get_prop_keys () returns + the length of the key list. Otherwise it returns -1 and assigns + an error code to the external variable #merror_code. + +*/ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Î¥ê¥¹¥È¤òÆÀ¤ë + + ´Ø¿ô mtext_get_prop_keys () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤Î°ÌÃ֤ˤ¢¤ë + ʸ»ú¤¬»ý¤Ã¤Æ¤¤¤ë¤¹¤Ù¤Æ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤«¤é¤Ê¤ëÇÛÎó¤òºî¤ê¡¢ + ¤½¤ÎÇÛÎó¤Î¥¢¥É¥ì¥¹¤ò *$KEYS ¤Ë¥»¥Ã¥È¤¹¤ë¡£¤³¤ÎÇÛÎó¤Î¤¿¤á¤Ë³ÎÊݤµ¤ì + ¤¿¥á¥â¥ê¤ò²òÊü¤¹¤ë¤Î¤Ï¥æ¡¼¥¶¤ÎÀÕǤ¤Ç¤¢¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_get_prop_keys () ¤ÏÆÀ¤é¤ì¤¿¥ê¥¹¥È¤ÎŤµ¤òÊÖ + ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò + ÀßÄꤹ¤ë¡£ +*/ + +/*** + @errors + @c MERROR_RANGE + + @seealso + mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (), + mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop () */ + +int +mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys) +{ + MTextPlist *plist; + int i; + + M_CHECK_POS (mt, pos, -1); + for (i = 0, plist = mt->plist; plist; i++, plist = plist->next); + if (i == 0) + { + *keys = NULL; + return 0; + } + MTABLE_MALLOC (*keys, i, MERROR_TEXTPROP); + for (i = 0, plist = mt->plist; plist; plist = plist->next) + { + MInterval *interval = find_interval (plist, pos); + + if (interval->nprops) + (*keys)[i++] = plist->key; + } + return i; +} + +/*=*/ + +/***en + @brief Set a text property + + The mtext_put_prop () function sets a text property to the + characters between $FROM (inclusive) and $TO (exclusive) in M-text + $MT. $KEY and $VAL specify the key and the value of the text + property. + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------ OLD_VAL --------------------> +@endverbatim + + becomes + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL--> +@endverbatim + + @return + If the operation was successful, mtext_put_prop () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë + + ´Ø¿ô mtext_put_prop () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é + $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¤è + ¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥»¥Ã¥È¤¹¤ë¡£ + + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP: <------------------ OLD_VAL --------------------> +@endverbatim + +¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£ + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP: <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL--> +@endverbatim + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 + ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_put_prop} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_put_prop_values (), mtext_get_prop (), + mtext_get_prop_values (), mtext_push_prop (), + mtext_pop_prop (), mtext_prop_range () */ + +int +mtext_put_prop (MText *mt, int from, int to, MSymbol key, void *val) +{ + MTextPlist *plist; + MTextProperty *prop; + MInterval *interval; + + M_CHECK_RANGE (mt, from, to, -1, 0); + + prepare_to_modify (mt, from, to, key); + plist = get_plist_create (mt, key, 1); + interval = pop_all_properties (plist, from, to); + prop = new_text_property (mt, from, to, key, val, 0); + PUSH_PROP (interval, prop); + M17N_OBJECT_UNREF (prop); + if (interval->next) + maybe_merge_interval (plist, interval); + if (interval->prev) + maybe_merge_interval (plist, interval->prev); + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/*=*/ + +/***en + @brief Set multiple text properties with the same key + + The mtext_put_prop_values () function sets a text property to the + characters between $FROM (inclusive) and $TO (exclusive) in M-text + $MT. $KEY and $VALUES specify the key and the values of the text + property. $NUM specifies the number of property values to be set. + + @return + If the operation was successful, mtext_put_prop_values () returns + 0. Otherwise it returns -1 and assigns an error code to the + external variable #merror_code. */ + +/***ja + @brief Ʊ¤¸¥­¡¼¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ôÀßÄꤹ¤ë + + ´Ø¿ô mtext_put_prop_values () ¤Ï¡¢M-Text $MT ¤Î$FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë + ¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥»¥Ã¥È + ¤¹¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Ï$KEY¤Ë¤è¤Ã¤Æ¡¢ÃÍ(Ê£¿ô²Ä)¤Ï$VALUES + ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ë¡£$NUM ¤ÏÀßÄꤵ¤ì¤ëÃͤθĿô¤Ç¤¢¤ë¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_put_prop_values () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤± + ¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_put_prop_values} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_put_prop (), mtext_get_prop (), mtext_get_prop_values (), + mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ + +int +mtext_put_prop_values (MText *mt, int from, int to, + MSymbol key, void **values, int num) +{ + MTextPlist *plist; + MInterval *interval; + int i; + + M_CHECK_RANGE (mt, from, to, -1, 0); + + prepare_to_modify (mt, from, to, key); + plist = get_plist_create (mt, key, 1); + interval = pop_all_properties (plist, from, to); + if (num > 0) + { + PREPARE_INTERVAL_STACK (interval, num); + for (i = 0; i < num; i++) + { + MTextProperty *prop + = new_text_property (mt, from, to, key, values[i], 0); + PUSH_PROP (interval, prop); + M17N_OBJECT_UNREF (prop); + } + } + if (interval->next) + maybe_merge_interval (plist, interval); + if (interval->prev) + maybe_merge_interval (plist, interval->prev); + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/*=*/ + +/***en + @brief Push a text property + + The mtext_push_prop () function pushes a text property whose key + is $KEY and value is $VAL to the characters between $FROM + (inclusive) and $TO (exclusive) in $MT. + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------ OLD_VAL --------------------> +@endverbatim + + becomes + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------- OLD_VAL -------------------> +PROP : <-------- VAL -------> +@endverbatim + + @return + If the operation was successful, mtext_push_prop () returns 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë + + ´Ø¿ô mtext_push_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¥Æ¥­¥¹¥È + ¥×¥í¥Ñ¥Æ¥£¤ò¡¢M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê + ¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£ + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------ OLD_VAL --------------------> +@endverbatim + ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£ +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------- OLD_VAL -------------------> +PROP : <-------- VAL -------> +@endverbatim + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_push_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð + -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_push_prop} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_put_prop (), mtext_put_prop_values (), + mtext_get_prop (), mtext_get_prop_values (), + mtext_pop_prop (), mtext_prop_range () */ + +int +mtext_push_prop (MText *mt, int from, int to, + MSymbol key, void *val) +{ + MTextPlist *plist; + MInterval *head, *tail, *interval; + MTextProperty *prop; + int check_head, check_tail; + + M_CHECK_RANGE (mt, from, to, -1, 0); + + prepare_to_modify (mt, from, to, key); + plist = get_plist_create (mt, key, 1); + + /* Find an interval that covers the position FROM. */ + head = find_interval (plist, from); + + /* If the found interval starts before FROM, divide it at FROM. */ + if (head->start < from) + { + divide_interval (plist, head, from); + head = head->next; + check_head = 0; + } + else + check_head = 1; + + /* Find an interval that ends at TO. If TO is not at the end of an + interval, make one that ends at TO. */ + if (head->end == to) + { + tail = head; + check_tail = 1; + } + else if (head->end > to) + { + divide_interval (plist, head, to); + tail = head; + check_tail = 0; + } + else + { + tail = find_interval (plist, to); + if (! tail) + { + tail = plist->tail; + check_tail = 0; + } + else if (tail->start == to) + { + tail = tail->prev; + check_tail = 1; + } + else + { + divide_interval (plist, tail, to); + check_tail = 0; + } + } + + prop = new_text_property (mt, from, to, key, val, 0); + + /* Push PROP to the current values of intervals between HEAD and TAIL + (both inclusive). */ + for (interval = head; ; interval = interval->next) + { + PUSH_PROP (interval, prop); + if (interval == tail) + break; + } + + M17N_OBJECT_UNREF (prop); + + /* If there is a possibility that TAIL now has the same value as the + next one, check it and concatenate them if necessary. */ + if (tail->next && check_tail) + maybe_merge_interval (plist, tail); + + /* If there is a possibility that HEAD now has the same value as the + previous one, check it and concatenate them if necessary. */ + if (head->prev && check_head) + maybe_merge_interval (plist, head->prev); + + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/*=*/ + +/***en + @brief Pop a text property + + The mtext_pop_prop () function removes the topmost text property + whose key is $KEY from the characters between $FROM (inclusive) + and and $TO (exclusive) in $MT. + + This function does nothing if characters in the region have no + such text property. + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------ OLD_VAL --------------------> +@endverbatim + + becomes + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <--OLD_VAL-->| |<--OLD_VAL-->| +@endverbatim + + @return + If the operation was successful, mtext_pop_prop () return 0. + Otherwise it returns -1 and assigns an error code to the external + variable #merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ý¥Ã¥×¤¹¤ë + + ´Ø¿ô mtext_pop_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î + ¤¦¤Á°ìÈÖ¾å¤Î¤â¤Î¤ò¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO¡Ê´Þ¤Þ + ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤«¤é¼è¤ê½ü¤¯¡£ + + »ØÄêÈϰϤÎʸ»ú¤¬¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï²¿ + ¤â¤·¤Ê¤¤¡£ + +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <------------------ OLD_VAL --------------------> +@endverbatim + ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£ +@verbatim + FROM TO +M-text: |<------------|-------- MT ---------|------------>| +PROP : <--OLD_VAL-->| |<--OLD_VAL-->| +@endverbatim + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_pop_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 + ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_pop_prop} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_put_prop (), mtext_put_prop_values (), + mtext_get_prop (), mtext_get_prop_values (), + mtext_push_prop (), mtext_prop_range () */ + +int +mtext_pop_prop (MText *mt, int from, int to, MSymbol key) +{ + MTextPlist *plist; + MInterval *head, *tail; + int check_head = 1; + + if (key == Mnil) + MERROR (MERROR_TEXTPROP, -1); + M_CHECK_RANGE (mt, from, to, -1, 0); + plist = get_plist_create (mt, key, 0); + if (! plist) + return 0; + + /* Find an interval that covers the position FROM. */ + head = find_interval (plist, from); + if (head->end >= to + && head->nprops == 0) + /* No property to pop. */ + return 0; + + prepare_to_modify (mt, from, to, key); + + /* If the found interval starts before FROM and has value(s), divide + it at FROM. */ + if (head->start < from) + { + if (head->nprops > 0) + { + divide_interval (plist, head, from); + check_head = 0; + } + else + from = head->end; + head = head->next; + } + + /* Pop the topmost text property from each interval following HEAD. + Stop at an interval that ends after TO. */ + for (tail = head; tail && tail->end <= to; tail = tail->next) + if (tail->nprops > 0) + POP_PROP (tail); + + if (tail) + { + if (tail->start < to) + { + if (tail->nprops > 0) + { + divide_interval (plist, tail, to); + POP_PROP (tail); + } + to = tail->start; + } + else + to = tail->end; + } + else + to = plist->tail->start; + + /* If there is a possibility that HEAD now has the same text + properties as the previous one, check it and concatenate them if + necessary. */ + if (head->prev && check_head) + head = head->prev; + while (head && head->end <= to) + head = maybe_merge_interval (plist, head); + + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/*=*/ + +/***en + @brief Find the range where the value of a text property is the same. + + The mtext_prop_range () function investigates the extent where all + characters have the same value for a text property. It first + finds the value of the property specified by $KEY of the character + at $POS in M-text $MT. Then it checks if adjacent characters have + the same value for the property $KEY. The beginning and the end + of the found range are stored to the variable pointed to by $FROM + and $TO. The character position stored in $FROM is inclusive but + that in $TO is exclusive; this fashion is compatible with the + range specification in the mtext_put_prop () function, etc. + + If $DEEPER is not 0, not only the topmost but also all the stacked + properties whose key is $KEY are compared. + + If $FROM is @c NULL, the beginning of range is not searched for. If + $TO is @c NULL, the end of range is not searched for. + + @return + + If the operation was successful, mtext_prop_range () returns the + number of values the property $KEY has at pos. Otherwise it + returns -1 and assigns an error code to the external variable @c + merror_code. */ + +/***ja + @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Æ±¤¸Ãͤò¤È¤ëÈϰϤòÄ´¤Ù¤ë. + + ´Ø¿ô mtext_prop_range () ¤Ï¡¢Ï¢Â³¤·¤¿Ê¸»ú¤¬Æ±¤¸¥×¥í¥Ñ¥Æ¥£¤ÎÃͤò»ý¤Ã + ¤Æ¤¤¤ëÈϰϤòÄ´¤Ù¤ë¡£¤Þ¤º M-text $MT ¤Î $POS ¤Î°ÌÃ֤ˤ¢¤ëʸ»ú¤Î¥×¥í + ¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼ $KEY ¤Ç»ØÄꤵ¤ì¤¿¤â¤Î¤ÎÃͤò¸«¤Ä¤±¤ë¡£¤½¤·¤ÆÁ°¸å + ¤Îʸ»ú¤â$KEY ¤Î¥×¥í¥Ñ¥Æ¥£¤ËƱ¤¸Ãͤò¤â¤Ã¤Æ¤¤¤ë¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£¸« + ¤Ä¤±¤¿ÈϰϤκǽé¤ÈºÇ¸å¤ò¡¢¤½¤ì¤¾¤ì $FROM ¤È $TO ¤ò¥Ý¥¤¥ó¥¿¤È¤¹¤ëÊÑ + ¿ô¤ËÊݸ¤¹¤ë¡£$FROM ¤ËÊݸ¤µ¤ì¤ëʸ»ú¤Î°ÌÃ֤ϸ«¤Ä¤±¤¿ÈϰϤ˴ޤޤì¤ë + ¤¬¡¢$TO ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£¡Ê$TO ¤ÎÁ°¤ÇƱ¤¸Ãͤò¤È¤ëÈϰϤϽª¤ï¤ë¡£¡Ë¤³¤Î + ÈÏ°Ï»ØÄê¤ÎÊýË¡¤Ï¡¢´Ø¿ô mtext_put_prop () ¤Ê¤É¤È¶¦Ä̤Τâ¤Î¤Ç¤¢¤ë¡£ + + $DEEPER ¤¬ 0 ¤Ç¤Ê¤±¤ì¤Ð¡¢$KEY ¤È¤¤¤¦¥­¡¼¤ò»ý¤Ä¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á°ìÈÖ + ¾å¤Î¤â¤Î¤À¤±¤Ç¤Ê¤¯¡¢¥¹¥¿¥Ã¥¯Ãæ¤Î¤¹¤Ù¤Æ¤Î¤â¤Î¤¬Èæ³Ó¤µ¤ì¤ë¡£ + + $FROM ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤλϤޤê¤Ïõº÷¤·¤Ê¤¤¡£$TO ¤¬ @c NULL + ¤Ê¤é¤Ð¡¢ÈϰϤνª¤ê¤Ïõº÷¤·¤Ê¤¤¡£ + + @return + ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_prop_range () ¤Ï $KEY ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤοô¤ò + ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1 ¤òÊÖ¤·¡¢ ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ + ¥É¤òÀßÄꤹ¤ë¡£ + + @latexonly \IPAlabel{mtext_prop_range} @endlatexonly */ + +/*** + @errors + @c MERROR_RANGE, @c MERROR_SYMBOL + + @seealso + mtext_put_prop (), mtext_put_prop_values (), + mtext_get_prop (), mtext_get_prop_values (), + mtext_pop_prop (), mtext_push_prop () */ + +int +mtext_prop_range (MText *mt, MSymbol key, int pos, + int *from, int *to, int deeper) +{ + MTextPlist *plist; + MInterval *interval, *temp; + void *val; + int nprops; + + M_CHECK_POS (mt, pos, -1); + + plist = get_plist_create (mt, key, 0); + if (! plist) + { + if (from) *from = 0; + if (to) *to = mtext_nchars (mt); + return 0; + } + + interval = find_interval (plist, pos); + nprops = interval->nprops; + if (deeper || ! nprops) + { + if (from) *from = interval->start; + if (to) *to = interval->end; + return interval->nprops; + } + + val = nprops ? interval->stack[nprops - 1] : NULL; + + if (from) + { + for (temp = interval; + temp->prev + && (temp->prev->nprops + ? (nprops + && (val == temp->prev->stack[temp->prev->nprops - 1])) + : ! nprops); + temp = temp->prev); + *from = temp->start; + } + + if (to) + { + for (temp = interval; + temp->next + && (temp->next->nprops + ? (nprops + && val == temp->next->stack[temp->next->nprops - 1]) + : ! nprops); + temp = temp->next); + *to = temp->end; + } + + return nprops; +} + +/***en + @brief Create a text property. + + The mtext_property () function returns a newly allocated text + property whose key is $KEY and value is $VAL. The text created + property is not attached to any M-text, i.e. it is detached. + + $CONTROL_BITS must be 0 or logical OR of @c enum @c + MTextPropertyControl. */ + +MTextProperty * +mtext_property (MSymbol key, void *val, int control_bits) +{ + return new_text_property (NULL, 0, 0, key, val, control_bits); +} + +/***en + @brief Return the M-text of a text property. + + The mtext_property_mtext () function returns the M-text to which + text property $PROP is attached. If $PROP is currently detached, + NULL is returned. */ + +MText * +mtext_property_mtext (MTextProperty *prop) +{ + return prop->mt; +} + +/***en + @brief Return the key of a text property. + + The mtext_property_key () function returns the key (symbol) of + text property $PROP. */ + +MSymbol +mtext_property_key (MTextProperty *prop) +{ + return prop->key; +} + +/***en + @brief Return the value of a text property. + + The mtext_property_value () function returns the value of text + property $PROP. */ + +void * +mtext_property_value (MTextProperty *prop) +{ + return prop->val; +} + +/***en + @brief Return the start position of a text property. + + The mtext_property_start () function returns the start position of + text property $PROP. The start position is a character position + of an M-text where $PROP begins. If $PROP is detached, it returns + -1. */ + +int +mtext_property_start (MTextProperty *prop) +{ + return (prop->mt ? prop->start : -1); +} + +/***en + @brief Return the end position of a text property. + + The mtext_property_end () function returns the end position of + text property $PROP. The end position is a character position of + an M-text where $PROP ends. If $PROP is detached, it returns + -1. */ + +int +mtext_property_end (MTextProperty *prop) +{ + return (prop->mt ? prop->end : -1); +} + +/***en + @brief Get the topmost text property. + + The mtext_get_property () function searches the character at $POS + in M-text $MT for a text property whose key is $KEY. + + @return + If a text property is found, mtext_get_property () returns it. If + there are multiple text properties, it returns the topmost one. + If no such property is found, it returns @c NULL without changing + the external variable #merror_code. + + If an error is detected, mtext_get_property () returns @c NULL and + assigns an error code to the external variable #merror_code. */ + +MTextProperty * +mtext_get_property (MText *mt, int pos, MSymbol key) +{ + MTextPlist *plist; + MInterval *interval; + + M_CHECK_POS (mt, pos, NULL); + + plist = get_plist_create (mt, key, 0); + if (! plist) + return NULL; + + interval = find_interval (plist, pos); + if (! interval->nprops) + return NULL; + return interval->stack[interval->nprops - 1]; +} + +/***en + @brief Get multiple text properties. + + The mtext_get_properties () function searches the character at + $POS in M-text $MT for properties whose key is $KEY. If such + properties are found, they are stored in the memory area pointed + to by $PROPS. $NUM limits the maximum number of stored + properties. + + @return + If the operation was successful, mtext_get_properties () returns + the number of actually stored properties. If the character at + $POS does not have a property whose key is $KEY, the return value + is 0. If an error is detected, mtext_get_properties () returns -1 + and assigns an error code to the external variable #merror_code. */ + +int +mtext_get_properties (MText *mt, int pos, MSymbol key, + MTextProperty **props, int num) +{ + MTextPlist *plist; + MInterval *interval; + int nprops; + int i; + int offset; + + M_CHECK_POS (mt, pos, -1); + + plist = get_plist_create (mt, key, 0); + if (! plist) + return 0; + + interval = find_interval (plist, pos); + /* It is assured that INTERVAL is not NULL. */ + nprops = interval->nprops; + if (nprops == 0 || num <= 0) + return 0; + if (nprops == 1 || num == 1) + { + props[0] = interval->stack[nprops - 1]; + return 1; + } + + if (nprops <= num) + num = nprops, offset = 0; + else + offset = nprops - num; + for (i = 0; i < num; i++) + props[i] = interval->stack[offset + i]; + return num; +} + +/***en + @brief Attach a text property to an M-text. + + The mtext_attach_property () function attaches text property $PROP + to the range between $FROM and $TO in M-text $MT. If $PROP is + already attached to an M-text, it is detached before attached to + $MT. + + @return + If the operation was successful, mtext_attach_property () returns + 0. Otherwise it returns -1 and assigns an error code to the + external variable #merror_code. */ + +int +mtext_attach_property (MText *mt, int from, int to, MTextProperty *prop) +{ + MTextPlist *plist; + MInterval *interval; + + M_CHECK_RANGE (mt, from, to, -1, 0); + + M17N_OBJECT_REF (prop); + if (prop->mt) + mtext_detach_property (prop); + prepare_to_modify (mt, from, to, prop->key); + plist = get_plist_create (mt, prop->key, 1); + xassert (check_plist (plist, 0) == 0); + interval = pop_all_properties (plist, from, to); + xassert (check_plist (plist, 0) == 0); + prop->mt = mt; + prop->start = from; + prop->end = to; + PUSH_PROP (interval, prop); + M17N_OBJECT_UNREF (prop); + xassert (check_plist (plist, 0) == 0); + if (interval->next) + maybe_merge_interval (plist, interval); + if (interval->prev) + maybe_merge_interval (plist, interval->prev); + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/***en + @brief Detach a text property from an M-text. + + The mtext_detach_property () function makes text property $PROP + detached. + + @return + This function always returns 0. */ + +int +mtext_detach_property (MTextProperty *prop) +{ + MTextPlist *plist; + int start = prop->start, end = prop->end; + + if (! prop->mt) + return 0; + prepare_to_modify (prop->mt, start, end, prop->key); + plist = get_plist_create (prop->mt, prop->key, 0); + xassert (plist); + detach_property (plist, prop, NULL); + return 0; +} + +/***en + @brief Push a text property onto an M-text. + + The mtext_push_property () function attaches text property $PROP on + M-text MT by the "push" manner. + + @return + If the operation was successful, mtext_push_property () returns + 0. Otherwise it returns -1 and assigns an error code to the + external variable #merror_code. */ + +int +mtext_push_property (MText *mt, int from, int to, MTextProperty *prop) +{ + MTextPlist *plist; + MInterval *head, *tail, *interval; + int check_head, check_tail; + + M_CHECK_RANGE (mt, from, to, -1, 0); + + M17N_OBJECT_REF (prop); + if (prop->mt) + mtext_detach_property (prop); + prepare_to_modify (mt, from, to, prop->key); + plist = get_plist_create (mt, prop->key, 1); + prop->mt = mt; + prop->start = from; + prop->end = to; + + /* Find an interval that covers the position FROM. */ + head = find_interval (plist, from); + + /* If the found interval starts before FROM, divide it at FROM. */ + if (head->start < from) + { + divide_interval (plist, head, from); + head = head->next; + check_head = 0; + } + else + check_head = 1; + + /* Find an interval that ends at TO. If TO is not at the end of an + interval, make one that ends at TO. */ + if (head->end == to) + { + tail = head; + check_tail = 1; + } + else if (head->end > to) + { + divide_interval (plist, head, to); + tail = head; + check_tail = 0; + } + else + { + tail = find_interval (plist, to); + if (! tail) + { + tail = plist->tail; + check_tail = 0; + } + else if (tail->start == to) + { + tail = tail->prev; + check_tail = 1; + } + else + { + divide_interval (plist, tail, to); + check_tail = 0; + } + } + + /* Push PROP to the current values of intervals between HEAD and TAIL + (both inclusive). */ + for (interval = head; ; interval = interval->next) + { + PUSH_PROP (interval, prop); + if (interval == tail) + break; + } + + /* If there is a possibility that TAIL now has the same value as the + next one, check it and concatenate them if necessary. */ + if (tail->next && check_tail) + maybe_merge_interval (plist, tail); + + /* If there is a possibility that HEAD now has the same value as the + previous one, check it and concatenate them if necessary. */ + if (head->prev && check_head) + maybe_merge_interval (plist, head->prev); + + M17N_OBJECT_UNREF (prop); + xassert (check_plist (plist, 0) == 0); + return 0; +} + +/***en + @brief Symbol for specifying serializer functions. + + To serialize a text property, the user must supply a serializer + function for that text property. This is done by giving a symbol + property whose key is #Mtext_prop_serializer and value is a + pointer to an appropriate serializer function. + + @seealso Mtext_prop_serializer (), MTextPropSerializeFunc + */ +MSymbol Mtext_prop_serializer; + +/***en + @brief Symbol for specifying deserializer functions. + + To deserialize a text property, the user must supply a deserializer + function for that text property. This is done by giving a symbol + property whose key is #Mtext_prop_deserializer and value is a + pointer to an appropriate deserializer function. + + @seealso Mtext_prop_serializer (), MTextPropSerializeFunc + */ +MSymbol Mtext_prop_deserializer; + +/***en + @brief Serialize text properties in an M-text. + + The mtext_serialize () function serializes the text between $FROM + and $TO in M-text $MT. The serialized result is an M-text in the + form of XML. $PROPERTY_LIST limits the text properties to be + serialized. If a symbol 1) appears as the value of an element in + $PROPERTY_LIST (the key must be @c Mt ) and 2) has the symbol + property #Mtext_prop_serializer, a text property having that + symbol as its key is turned into the "property" element in the + resulting XML representation. + + The DTD of the generated XML is as follows: + +@verbatim + + + + + + + + + ]> +@endverbatim + + This function depends on the libxml2 library. If the m17n library + is configured without libxml2, this function always fails. + + @return + If the operation was successful, mtext_serialize () returns an + M-text in the form of XML. Otherwise it returns @c NULL and assigns an + error code to the external variable #merror_code. + + @seealso + mtext_deserialize (), Mtext_prop_serializer */ + +MText * +mtext_serialize (MText *mt, int from, int to, MPlist *property_list) +{ +#ifdef HAVE_XML2 + MPlist *plist, *pl; + MTextPropSerializeFunc func; + MText *work; + xmlDocPtr doc; + xmlNodePtr node; + unsigned char *ptr; + int n; + + M_CHECK_RANGE (mt, from, to, NULL, NULL); + doc = xmlParseMemory (XML_TEMPLATE, strlen (XML_TEMPLATE) + 1); + node = xmlDocGetRootElement (doc); + + plist = mplist (); + MPLIST_DO (pl, property_list) + { + MSymbol key = MPLIST_VAL (pl); + + func = (MTextPropSerializeFunc) msymbol_get (key, Mtext_prop_serializer); + if (func) + extract_text_properties (mt, from, to, key, plist); + } + + work = mtext (); + MPLIST_DO (pl, plist) + { + MTextProperty *prop = MPLIST_VAL (pl); + char buf[256]; + MPlist *serialized_plist; + xmlNodePtr child; + + func = (MTextPropSerializeFunc) msymbol_get (prop->key, + Mtext_prop_serializer); + serialized_plist = (func) (prop->val); + if (! serialized_plist) + continue; + mtext_reset (work); + mplist__serialize (work, serialized_plist); + child = xmlNewChild (node, NULL, (xmlChar *) "property", NULL); + xmlSetProp (child, (xmlChar *) "key", + (xmlChar *) MSYMBOL_NAME (prop->key)); + xmlSetProp (child, (xmlChar *) "value", (xmlChar *) MTEXT_DATA (work)); + sprintf (buf, "%d", prop->start - from); + xmlSetProp (child, (xmlChar *) "from", (xmlChar *) buf); + sprintf (buf, "%d", prop->end - from); + xmlSetProp (child, (xmlChar *) "to", (xmlChar *) buf); + sprintf (buf, "%d", prop->control.flag); + xmlSetProp (child, (xmlChar *) "control", (xmlChar *) buf); + xmlAddChild (node, xmlNewText ((xmlChar *) "\n")); + + M17N_OBJECT_UNREF (serialized_plist); + } + M17N_OBJECT_UNREF (plist); + + if (from > 0 || to < mtext_nchars (mt)) + mtext_copy (work, 0, mt, from, to); + else + { + M17N_OBJECT_UNREF (work); + work = mt; + } + for (from = 0, to = mtext_nchars (mt); from <= to; from++) + { + ptr = MTEXT_DATA (mt) + POS_CHAR_TO_BYTE (mt, from); + xmlNewTextChild (node, NULL, (xmlChar *) "body", (xmlChar *) ptr); + from = mtext_character (mt, from, to, 0); + if (from < 0) + from = to; + } + + xmlDocDumpMemoryEnc (doc, (xmlChar **) &ptr, &n, "UTF-8"); + if (work == mt) + work = mtext (); + mtext__cat_data (work, ptr, n, MTEXT_FORMAT_UTF_8); + return work; +#else /* not HAVE_XML2 */ + MERROR (MERROR_TEXTPROP, NULL); +#endif /* not HAVE_XML2 */ +} + +/***en + @brief Deserialize text properties in an M-text. + + The mtext_deserialize () function deserializes M-text $MT. $MT + must be an XML having the followng DTD. + +@verbatim + + + + + + + + + ]> +@endverbatim + + This function depends on the libxml2 library. If the m17n library + is configured without libxml2, this function always fail. + + @return + If the operation was successful, mtext_deserialize () returns the + resulting M-text. Otherwise it returns @c NULL and assigns an error + code to the external variable #merror_code. + + @seealso + mtext_serialize (), Mtext_prop_deserializer */ + +MText * +mtext_deserialize (MText *mt) +{ +#ifdef HAVE_XML2 + xmlDocPtr doc; + xmlNodePtr node; + xmlXPathContextPtr context; + xmlXPathObjectPtr result; + xmlChar *body_str, *key_str, *val_str, *from_str, *to_str, *ctl_str; + int i; + + if (mt->format > MTEXT_FORMAT_UTF_8) + MERROR (MERROR_TEXTPROP, NULL); + doc = xmlParseMemory ((char *) MTEXT_DATA (mt), mtext_nbytes (mt)); + if (! doc) + MERROR (MERROR_TEXTPROP, NULL); + node = xmlDocGetRootElement (doc); + if (! node) + { + xmlFreeDoc (doc); + MERROR (MERROR_TEXTPROP, NULL); + } + if (xmlStrcmp (node->name, (xmlChar *) "mtext")) + { + xmlFreeDoc (doc); + MERROR (MERROR_TEXTPROP, NULL); + } + + context = xmlXPathNewContext (doc); + result = xmlXPathEvalExpression ((xmlChar *) "//body", context); + if (xmlXPathNodeSetIsEmpty (result->nodesetval)) + { + xmlFreeDoc (doc); + MERROR (MERROR_TEXTPROP, NULL); + } + for (i = 0, mt = mtext (); i < result->nodesetval->nodeNr; i++) + { + if (i > 0) + mtext_cat_char (mt, 0); + node = (xmlNodePtr) result->nodesetval->nodeTab[i]; + body_str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1); + if (body_str) + { + mtext__cat_data (mt, body_str, strlen ((char *) body_str), + MTEXT_FORMAT_UTF_8); + xmlFree (body_str); + } + } + + result = xmlXPathEvalExpression ((xmlChar *) "//property", context); + if (! xmlXPathNodeSetIsEmpty (result->nodesetval)) + for (i = 0; i < result->nodesetval->nodeNr; i++) + { + MSymbol key; + MTextPropDeserializeFunc func; + MTextProperty *prop; + MPlist *plist; + int from, to, control; + void *val; + + key_str = xmlGetProp (result->nodesetval->nodeTab[i], + (xmlChar *) "key"); + val_str = xmlGetProp (result->nodesetval->nodeTab[i], + (xmlChar *) "value"); + from_str = xmlGetProp (result->nodesetval->nodeTab[i], + (xmlChar *) "from"); + to_str = xmlGetProp (result->nodesetval->nodeTab[i], + (xmlChar *) "to"); + ctl_str = xmlGetProp (result->nodesetval->nodeTab[i], + (xmlChar *) "control"); + + key = msymbol ((char *) key_str); + func = ((MTextPropDeserializeFunc) + msymbol_get (key, Mtext_prop_deserializer)); + if (! func) + continue; + plist = mplist__from_string (val_str, strlen ((char *) val_str)); + if (! plist) + continue; + if (sscanf ((char *) from_str, "%d", &from) != 1 + || from < 0 || from >= mtext_nchars (mt)) + continue; + if (sscanf ((char *) to_str, "%d", &to) != 1 + || to <= from || to > mtext_nchars (mt)) + continue; + if (sscanf ((char *) ctl_str, "%d", &control) != 1 + || control < 0 || control > MTEXTPROP_CONTROL_MAX) + continue; + val = (func) (plist); + M17N_OBJECT_UNREF (plist); + prop = mtext_property (key, val, control); + if (key->managing_key) + M17N_OBJECT_UNREF (val); + mtext_push_property (mt, from, to, prop); + M17N_OBJECT_UNREF (prop); + + xmlFree (key_str); + xmlFree (val_str); + xmlFree (from_str); + xmlFree (to_str); + xmlFree (ctl_str); + } + xmlXPathFreeContext (context); + xmlFreeDoc (doc); + return mt; +#else /* not HAVE_XML2 */ + MERROR (MERROR_TEXTPROP, NULL); +#endif /* not HAVE_XML2 */ +} + +/*** @} */ + +/* + Local Variables: + coding: euc-japan + End: +*/ diff --git a/src/textprop.h b/src/textprop.h new file mode 100644 index 0000000..30e75b6 --- /dev/null +++ b/src/textprop.h @@ -0,0 +1,69 @@ +/* textproc.h -- header file for the text property module. + Copyright (C) 2003, 2004 + National Institute of Advanced Industrial Science and Technology (AIST) + Registration Number H15PRO112 + + This file is part of the m17n library. + + The m17n library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + The m17n library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the m17n library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifndef _M17N_TEXTPROP_H_ +#define _M17N_TEXTPROP_H_ + +/** MTextProperty is the structure for a text property object. While + attached, it is stored in the stacks of intervals covering the + range from MTextProperty->start to MTextProperty->end. */ + +struct MTextProperty +{ + /** Common header for a managed object. */ + M17NObject control; + + /** Number of intervals the property is attached. When it becomes + zero, the property is detached. */ + unsigned attach_count; + + /** M-text to which the property is attaced. The value NULL means + that the property is detached. */ + MText *mt; + + /** Region of if the property is attached to it. */ + int start, end; + + /** Key of the property. */ + MSymbol key; + + /** Value of the property. */ + void *val; +}; + + +extern struct MTextPlist *mtext__copy_plist (struct MTextPlist *, + int from, int to, + MText *mt, int pos); + +extern void mtext__free_plist (MText *mt); + +extern void mtext__adjust_plist_for_delete (MText *, int, int); + +extern void mtext__adjust_plist_for_insert (MText *, int, int, + struct MTextPlist *); + +extern void mtext__adjust_plist_for_change (MText *mt, int from, int to); + +extern void dump_textplist (struct MTextPlist *plist, int indent); + +#endif /* _M17N_TEXTPROP_H_ */