--- /dev/null
+stamp-h*
+aclocal.m4
+autoscan.log
+configure
+configure.scan
+*.gz
+autom4te.cache
+config.*
+libtool
+m17n-config
+Makefile.in
+Makefile
+.deps
+.libs
--- /dev/null
+Kenichi Handa <handa@m17n.org>
+ Core developper.
+
+Mikiko Nishikimi <nisikimi@m17n.org>
+ Core developper.
+
+Naoto Takahashi <ntakahas@m17n.org>
+ Core developper.
+
+Satoru Tomura <tomura@m17n.org>
+ Core developper.
--- /dev/null
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null
+2004-03-01 Kenichi Handa <handa@m17n.org>
+
+ * Version 1.0 released.
+
+\f
+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.
--- /dev/null
+# 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
--- /dev/null
+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.
+
+\f
+* Changes in the m17n library 0.9
+
+** Released.
+
+\f
+* 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.
--- /dev/null
+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 <m17n-core.h>, for SHELL API, include <m17n.h>, and
+for GUI API, include <m17n-gui.h> and <m17n-X.h>. 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.
--- /dev/null
+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.
+
+\f
+* soon
+
+** Explicitely list up managed objects and describe them.
+
+** Allow properties to M-text itself, not to a specific region.
+
+\f
+* later
+
+** Add <plist> 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.
+
+\f
+* done
+
+\f
+* 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.
--- /dev/null
+#!/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.
--- /dev/null
+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:
--- /dev/null
+*.lo
+*.la
+mconv
+mdate
+mdump
+medit
+mview
+a.out
+stamp-h*
+config.h
+config.h.in
+Makefile.in
+Makefile
+.deps
+.libs
--- /dev/null
+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
--- /dev/null
+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"
--- /dev/null
+2004-03-01 Kenichi Handa <handa@m17n.org>
+
+ * Version 1.0 released.
+
+\f
+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.
--- /dev/null
+ヨーロッパ
+ 英語 (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
+ タイ語 (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ
+ ラオ語 (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ
+
+東アジア
+ 日本語 こんにちは
+ 中国語 (汉语,普通话) 你好
+ 韓国語 (한글) 안녕하세요
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mtext [
+<!ELEMENT mtext (property* , body+)>
+<!ELEMENT property EMPTY>
+<!ELEMENT body (#PCDATA)>
+<!ATTLIST property key CDATA #REQUIRED>
+<!ATTLIST property value CDATA #REQUIRED>
+<!ATTLIST property from CDATA #REQUIRED>
+<!ATTLIST property to CDATA #REQUIRED>
+<!ATTLIST property control CDATA #REQUIRED>
+]>
+<mtext>
+<property key="language" value="en" from="11" to="29" control="0"/>
+<property key="language" value="fr" from="37" to="64" control="0"/>
+<property key="language" value="de" from="72" to="104" control="0"/>
+<property key="language" value="sk" from="114" to="135" control="0"/>
+<property key="language" value="ru" from="143" to="168" control="0"/>
+<property key="language" value="el" from="177" to="196" control="0"/>
+<property key="language" value="tr" from="214" to="232" control="0"/>
+<property key="language" value="ka" from="241" to="263" control="0"/>
+<property key="language" value="ar" from="272" to="314" control="0"/>
+<property key="language" value="fa" from="323" to="344" control="0"/>
+<property key="language" value="he" from="353" to="374" control="0"/>
+<property key="language" value="am" from="383" to="394" control="0"/>
+<property key="language" value="hi" from="413" to="439" control="0"/>
+<property key="language" value="ml" from="448" to="466" control="0"/>
+<property key="language" value="ta" from="475" to="491" control="0"/>
+<property key="language" value="bo" from="501" to="550" control="0"/>
+<property key="language" value="km" from="559" to="591" control="0"/>
+<property key="language" value="vi" from="601" to="623" control="0"/>
+<property key="language" value="th" from="630" to="663" control="0"/>
+<property key="language" value="lo" from="670" to="701" control="0"/>
+<property key="language" value="ja" from="708" to="724" control="0"/>
+<property key="language" value="zh" from="730" to="742" control="0"/>
+<property key="language" value="ko" from="749" to="762" control="0"/>
+<body>ヨーロッパ
+ 英語 (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
+ タイ語 (ภาษาไทย) สวัสดีครับ, สวัสดีค่ะ
+ ラオ語 (ພາສາລາວ) ສະບາຍດີ, ຂໍໃຫ້ໂຊກດີ
+
+東アジア
+ 日本語 こんにちは
+ 中国語 (汉语,普通话) 你好
+ 韓国語 (한글) 안녕하세요
+</body></mtext>
--- /dev/null
+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 (한글) 안녕하세요
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mtext [
+<!ELEMENT mtext (property* , body+)>
+<!ELEMENT property EMPTY>
+<!ELEMENT body (#PCDATA)>
+<!ATTLIST property key CDATA #REQUIRED>
+<!ATTLIST property value CDATA #REQUIRED>
+<!ATTLIST property from CDATA #REQUIRED>
+<!ATTLIST property to CDATA #REQUIRED>
+<!ATTLIST property control CDATA #REQUIRED>
+]>
+<mtext>
+<property key="language" value="el" from="175" to="204" control="0"/>
+<property key="language" value="ru" from="139" to="175" control="0"/>
+<property key="language" value="sk" from="106" to="139" control="0"/>
+<property key="language" value="de" from="64" to="106" control="0"/>
+<property key="language" value="fr" from="27" to="64" control="0"/>
+<property key="language" value="en" from="7" to="27" control="0"/>
+<property key="language" value="am" from="408" to="431" control="0"/>
+<property key="language" value="he" from="377" to="408" control="0"/>
+<property key="language" value="fa" from="345" to="377" control="0"/>
+<property key="language" value="ar" from="292" to="345" control="0"/>
+<property key="language" value="ka" from="258" to="292" control="0"/>
+<property key="language" value="tr" from="229" to="258" control="0"/>
+<property key="language" value="lo" from="746" to="785" control="0"/>
+<property key="language" value="th" from="704" to="746" control="0"/>
+<property key="language" value="vi" from="668" to="704" control="0"/>
+<property key="language" value="km" from="626" to="668" control="0"/>
+<property key="language" value="bo" from="565" to="626" control="0"/>
+<property key="language" value="ta" from="538" to="565" control="0"/>
+<property key="language" value="ml" from="491" to="538" control="0"/>
+<property key="language" value="hi" from="454" to="491" control="0"/>
+<property key="language" value="ko" from="844" to="867" control="0"/>
+<property key="language" value="zh" from="821" to="844" control="0"/>
+<property key="language" value="ja" from="796" to="821" control="0"/>
+<body>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 (한글) 안녕하세요
+</body></mtext>
--- /dev/null
+! -*- 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:¥º¡¼¥ë¡¼¸ì
--- /dev/null
+# 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
+
--- /dev/null
+#include <m17n-gui.h>
+
+#include <config.h>
+
+#ifdef HAVE_WORDCUT
+
+#define THAI_BEG 0x0E00
+#define THAI_END 0x0E6F
+
+static int wordcut_initiazlied = 0;
+
+#include <wordcut/wordcut.h>
+#include <wordcut/xmalloc.h>
+#include <wordcut/path.h>
+
+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;
+}
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> -f FROMCODE
+
+ FROMCODE is the encoding of INFILE (defaults to UTF-8).
+
+ <li> -t TOCODE
+
+ TOCODE is the encoding of OUTFILE (defaults to UTF-8).
+
+ <li> -k
+
+ Do not stop conversion on error.
+
+ <li> -s
+
+ Suppress warnings.
+
+ <li> -v
+
+ Print progress information.
+
+ <li> -l
+
+ List available encodings.
+
+ <li> --version
+
+ Print version number.
+
+ <li> -h, --help
+
+ Print this message.
+
+ </ul>
+*/
+
+#ifndef FOR_DOXYGEN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <m17n.h>
+#include <m17n-misc.h>
+
+#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 */
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> --version
+
+ Print version number.
+
+ <li> -h, --help
+
+ Print this message.
+ </ul>
+*/
+
+#ifndef FOR_DOXYGEN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <locale.h>
+
+#include <m17n.h>
+#include <m17n-misc.h>
+
+#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 */
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> -s SIZE
+
+ SIZE is the font size in point. The default font size is 12 point.
+
+ <li> -d DPI
+
+ DPI is the resolution in dots per inch. The default resolution is
+ 300 dpi.
+
+ <li> -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.
+
+ <li> -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.
+
+ <li> -c POS
+
+ POS is the character position of cursor to draw. By default,
+ cursor is not drawn.
+
+ <li> -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.
+
+ <li> -w
+
+ Each line is broken at word boundary.
+
+ <li> -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.
+
+ <li> -q
+
+ Quiet mode. Don't print any messages.
+
+ <li> --version
+
+ Print the version number.
+
+ <li> -h, --help
+
+ Print this message.
+
+ </ul>
+*/
+
+#ifndef FOR_DOXYGEN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+#include <X11/Xlib.h>
+
+#include <m17n-gui.h>
+#include <m17n-misc.h>
+
+#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 */
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> --version
+
+ Print version number.
+
+ <li> -h, --help
+
+ Print this message.
+
+ </ul>
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <locale.h>
+
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Scrollbar.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/MenuButton.h>
+
+#include <m17n-gui.h>
+#include <m17n-misc.h>
+#include <m17n-X.h>
+
+#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 = "<EnterWindow>: highlight() MenuHelp(%s)\n\
+ <LeaveWindow>: reset() MenuHelp()\n\
+ <BtnDown>: reset() PopupMenu()\n\
+ <BtnUp>: 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>: Expose()\n\
+ <Configure>: Configure()\n\
+ <Key>: Key()\n\
+ <KeyUp>: Key()\n\
+ <Btn1Down>: ButtonPress()\n\
+ <Btn1Up>: ButtonRelease()\n\
+ <Btn1Motion>: ButtonMotion()\n\
+ <Btn2Down>: Button2Press()";
+ /* Translation table for the top form widget. */
+ String trans2 = "<Key>: Key()\n\
+ <KeyUp>: Key()";
+ String pop_face_trans
+ = "<EnterWindow>: MenuHelp(Pop face property) highlight()\n\
+ <LeaveWindow>: MenuHelp() reset()\n\
+ <Btn1Down>: set()\n\
+ <Btn1Up>: notify() unset()";
+ String pop_lang_trans
+ = "<EnterWindow>: MenuHelp(Pop language property) highlight()\n\
+ <LeaveWindow>: MenuHelp() reset()\n\
+ <Btn1Down>: set()\n\
+ <Btn1Up>: 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 */
--- /dev/null
+/* 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 <ja, anthy>
+
+ @section mimx-anthy-description DESCRIPTION
+
+ The shared library mimx-anthy.so is an external module used by the
+ input method <ja, anthy>. It exports these functions.
+
+ <ul>
+ <li> init
+
+ Initialize this module.
+
+ <li> fini
+
+ Finalize this module.
+
+ <li> convert
+
+ Convert the current preedit text (Hiragana sequence) into
+ Kana-Kanji mixed text.
+
+ <li> change
+
+ Record the change of candidate of the current segment.
+
+ <li> resize
+
+ Enlarge or shorten the length of the current segment.
+
+ <li> commit
+
+ Commit the lastly selected candidates of all the segments.
+
+ </ul>
+
+ @section mimx-anthy-seealso See also
+ @ref mdbIM
+*/
+
+#ifndef FOR_DOXYGEN
+
+#include <stdio.h>
+#include <string.h>
+#include <config.h>
+#include <m17n.h>
+
+#ifdef HAVE_ANTHY
+
+#include <anthy/anthy.h>
+
+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 */
--- /dev/null
+/* 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 <en, ispell>
+
+ @section mimx-ispell-description DESCRIPTION
+
+ The shared library mimx-ispell.so is an external module used by the
+ input method <en, ispell>. It exports these functions.
+
+ <ul>
+ <li> init
+
+ Initialize this library.
+
+ <li> fini
+
+ Finalize this library.
+
+ <li> ispell_word
+
+ Check the spell of the current preedit text (English) and, if the
+ spell is incorrect, return a list of candidates.
+
+ </ul>
+
+ 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 <stdio.h>
+#include <string.h>
+#include <config.h>
+#include <m17n-gui.h>
+
+#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 */
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> -e ENCODING
+
+ ENCODING is the encoding of FILE (defaults to UTF-8).
+
+ <li> -s FONTSIZE
+
+ FONTSIZE is the fontsize in point. If ommited, it defaults to the
+ size of the default font defined in X resource.
+
+ <li> --version
+
+ Print version number.
+
+ <li> -h, --help
+
+ Print this message.
+
+ </ul>
+*/
+#ifndef FOR_DOXYGEN
+
+#include <stdio.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Viewport.h>
+
+#include <m17n-gui.h>
+#include <m17n-misc.h>
+#include <m17n-X.h>
+
+#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 = "<KeyPress>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>: 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 */
--- /dev/null
+#!/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 <<EOF
+/***en @page m17n-config Print compile/link options of the m17n library */
+/***ja @page m17n-config m17n ¥é¥¤¥Ö¥é¥ê¤Î¥³¥ó¥Ñ¥¤¥ë¡¦¥ê¥ó¥¯¥ª¥×¥·¥ç¥ó¤Îɽ¼¨ */
+
+/***
+@section synopsis SYNOPSIS
+
+m17n-config [CORE | X] [--cflags | --libs | --libtool] [--version]
+
+@section description DESCRIPTION
+
+The shell script m17n-config prints compile and link options for a
+program that uses the m17n library.
+
+By default, the printed options are for such a program that uses SHELL
+API of the libray. But, if the first argument is "CORE", the options
+are for a program that uses only CORE API, if the first argument "X",
+the options are for a program that uses GUI API on X window.
+
+The other arguments are as follows.
+
+- --cflags\n
+ Print compile option (e.g. -I/usr/local/include)
+- --libs\n
+ Print link option (e.g. -L/usr/local/lib -lm17n)
+- --libtool\n
+ Print libtool option (e.g. /usr/local/lib/libm17n.la)
+- --version\n
+ Print version number of the m17n library.
+*/
+EOF
+
+# 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.
--- /dev/null
+*.lo
+*.la
+a.out
+stamp-h*
+config.h
+config.h.in
+Makefile.in
+Makefile
+linkcore
+linkshell
+linkgui
+.deps
+.libs
--- /dev/null
+2004-03-01 Kenichi Handa <handa@m17n.org>
+
+ * Version 1.0 released.
+
+\f
+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.
--- /dev/null
+# 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}
--- /dev/null
+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 `<member_name>',
+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:
+ `<member_name.sub_name>'
+ or `<member_name->sub_name>'
+ or `MStruct->member_name.subname'
+ or `MStruct->member_name->subname'
--- /dev/null
+/* 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 <tt>(void *)</tt>. "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 ÃÍ ¤«¤é¤Ê¤ë¡£¥¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢
+ ¤ê¡¢ÃÍ¤Ï <tt>(void *)</tt> ·¿¤Ë¥¥ã¥¹¥È¤Ç¤¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£
+ ¡Öʸ»ú C ¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¥¡¼¤¬ @c K ¤Ç¤¢¤ë¤â¤Î¡×¤ò´Êñ¤Ë
+ ¡Öʸ»ú C ¤Î K ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */
+/*=*/
+
+#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
+/*** @addtogroup m17nInternal
+ @{ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#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);
+}
+
+\f
+/* 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 */
+\f
+/* 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 <tt>"script"</tt> 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 <em>Unicode Technical Report #24</em>. */
+
+/***ja
+ @brief ¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥¡¼
+
+ ¥·¥ó¥Ü¥ë #Mscript ¤Ï <tt>"script"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×
+ ¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢¤³¤Îʸ»ú¤Î°¤¹
+ ¤ë¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
+
+ ¥¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢<em>Unicode Technical Report
+ #24</em> ¤Ë¥ê¥¹¥È¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£ */
+
+MSymbol Mscript;
+
+/*=*/
+
+/***en
+ @brief Key for character name.
+
+ The symbol #Mname has the name <tt>"name"</tt> 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 ¤Ï <tt>"name"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ
+ ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃÍ¤Ï C-string ¤Ç¤¢¤ê¡¢¤½
+ ¤Îʸ»ú¤Î̾Á°¤òɽ¤ï¤¹¡£ */
+
+MSymbol Mname;
+
+/*=*/
+
+/***en
+ @brief Key for general category.
+
+ The symbol #Mcategory has the name <tt>"category"</tt> and is
+ used as the key of a character property. The value of such a
+ property is a symbol representing the <em>general category</em> of
+ the character.
+
+ Each symbol that represents a general category has one of the
+ names listed as abbreviations for <em>General Category</em> in
+ Unicode. */
+
+/***ja
+ @brief °ìÈÌ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥¡¼
+
+ ¥·¥ó¥Ü¥ë #Mcategory ¤Ï <tt>"category"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸
+ »ú¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Âбþ¤¹¤ë
+ <em>°ìÈÌ¥«¥Æ¥´¥ê</em> ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
+
+ °ìÈÌ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢<em>General Category</em>¤Î
+ ¾Êά·Á¤È¤·¤Æ Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¢¤ë¡£ */
+
+MSymbol Mcategory;
+
+/*=*/
+
+/***en
+ @brief Key for canonical combining class.
+
+ The symbol #Mcombining_class has the name
+ <tt>"combining-class"</tt> and is used as the key of a character
+ property. The value of such a property is an integer that
+ represents the <em>canonical combining class</em> 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 ¤Ï <tt>"combining-class"</tt> ¤È¤¤¤¦
+ ̾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃÍ
+ ¤Ï¡¢Âбþ¤¹¤ë @e ɸ½à·ë¹ç¥¯¥é¥¹ ¤òɽ¤ï¤¹ÈóÉéÀ°¿ô¤Ç¤¢¤ë¡£
+
+ ɸ½à·ë¹ç¥¯¥é¥¹¤òɽ¤ï¤¹ÈóÉéÀ°¿ô¤Î°ÕÌ£¤Ï¡¢Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î
+ ¤ÈƱ¤¸¤Ç¤¢¤ë¡£ */
+
+MSymbol Mcombining_class;
+/*=*/
+
+/***en
+ @brief Key for bidi category.
+
+ The symbol #Mbidi_category has the name <tt>"bidi-category"</tt>
+ and is used as the key of a character property. The value of such
+ a property is a symbol that represents the <em>bidirectional
+ category</em> of the character.
+
+ Each symbol that represents a bidirectional category has one of
+ the names listed as types of <em>Bidirectional Category</em> in
+ Unicode. */
+
+/***ja
+ @brief ÁÐÊý¸þ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥¡¼
+
+ ¥·¥ó¥Ü¥ë #Mbidi_category ¤Ï <tt>"bidi-category"</tt> ¤È¤¤¤¦Ì¾Á°
+ ¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢
+ Âбþ¤¹¤ë @e ÁÐÊý¸þ¥«¥Æ¥´¥ê ¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
+
+ ÁÐÊý¸þ¥«¥Æ¥´¥ê¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤Î̾Á°¤Ï¡¢<em>Bidirectional
+ Category</em> ¤Î·¿¤È¤·¤Æ Unicode ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¢¤ë¡£ */
+
+MSymbol Mbidi_category;
+/*=*/
+
+/***en
+ @brief Key for corresponding single lowercase character.
+
+ The symbol #Msimple_case_folding has the name
+ <tt>"simple-case-folding"</tt> 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 ¤Ï <tt>"simple-case-folding"</tt>
+ ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£
+ ¤ÎÃͤϡ¢Âбþ¤¹¤ë¾®Ê¸»ú°ìʸ»ú¤Ç¤¢¤ê¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤¿
+ ʸ»úÎóÈæ³Ó¤ÎºÝ¤Ë»È¤ï¤ì¤ë¡£
+
+ ¤â¤·¤½¤Î¤è¤¦¤ÊÈæ³Ó¤Ë»ÈÍѤ·ÆÀ¤ëñ°ì¤Î¾®Ê¸»ú¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¡¢¤³¤Î¥×
+ ¥í¥Ñ¥Æ¥£¤ÎÃÍ¤Ï 0xFFFF ¤Ë¤Ê¤ë¡£¤³¤Î¾ì¹ç¤½¤Îʸ»ú¤Ï¡¢
+ #Mcomplicated_case_folding ¤È¤¤¤¦¥¡¼¤Îʸ»ú¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£ */
+
+MSymbol Msimple_case_folding;
+/***en
+ @brief Key for corresponding multiple lowercase characters
+
+ The symbol #Mcomplicated_case_folding has the name
+ <tt>"complicated-case-folding"</tt> 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 ¤Ï
+ <tt>"complicated-case-folding"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢Ê¸»ú¥×¥í¥Ñ¥Æ¥£
+ ¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£¤³¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢Âбþ¤¹¤ë¾®Ê¸»úÎ󤫤é¤Ê
+ ¤ë 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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#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;
+}
+
+\f
+/* 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 */
+
+\f
+/* 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
+ <tt>"charset"</tt>. */
+
+/***ja
+ @brief ¥·¥ó¥Ü¥ë @c Mcharset
+
+ ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥¡¼¤¬ @c Mcharset ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥¥¹¥È
+ ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¥·¥ó¥Ü¥ë @c Mcharset ¤Ï <tt>"charset"</tt> ¤È¤¤
+ ¤¦Ì¾Á°¤Ç¤¢¤é¤«¤¸¤áÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ */
+
+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 <tt>"ascii"</tt> and represents
+ the charset ISO 646, USA Version X3.4-1968 (ISO-IR-6). */
+/***ja
+ @brief ISO 646, USA Version ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë #Mcharset_ascii ¤Ï <tt>"ascii"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ 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 <tt>"iso-8859-1"</tt>
+ and represents the charset ISO/IEC 8859-1:1998. */
+/***ja
+ @brief ISO/IEC 8859-1:1998 ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë #Mcharset_iso_8859_1 ¤Ï <tt>"iso-8859-1"</tt> ¤È¤¤¤¦Ì¾
+ Á°¤ò»ý¤Á¡¢ISO/IEC 8859-1:1998 ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë
+ »È¤ï¤ì¤ë¡£ */
+
+MSymbol Mcharset_iso_8859_1;
+
+/***en
+ @brief Symbol representing the charset Unicode.
+
+ The symbol #Mcharset_unicode has name <tt>"unicode"</tt> and
+ represents the charset Unicode. */
+/***ja
+ @brief Unicode ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë #Mcharset_unicode ¤Ï <tt>"unicode"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý
+ ¤Á¡¢Unicode ¤ËÂбþ¤¹¤ëʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mcharset_unicode;
+
+/*=*/
+/***en
+ @brief Symbol representing the largest charset.
+
+ The symbol #Mcharset_m17n has name <tt>"m17n"</tt> and
+ represents the charset that contains all characters supported by
+ the m17n library. */
+/***ja
+ @brief Á´Ê¸»ú¤ò´Þ¤àʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë #Mcharset_m17n ¤Ï <tt>"m17n"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ m17n ¥é¥¤¥Ö¥é¥ê¤¬°·¤¦Á´¤Æ¤Îʸ»ú¤ò´Þ¤àʸ»ú¥»¥Ã¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»È
+ ¤ï¤ì¤ë¡£ */
+
+MSymbol Mcharset_m17n;
+
+/*=*/
+/***en
+ @brief Symbol representing the charset for ill-decoded characters.
+
+ The symbol #Mcharset_binary has name <tt>"binary"</tt> 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 <tt>"offset"</tt> 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 ¤Ï <tt>"offset"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ mchar_define_charset () ¤Ç¥ª¥Õ¥»¥Ã¥È·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú
+ ¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£*/
+
+MSymbol Moffset;
+/*=*/
+
+/***en @brief Symbol for the map type method of charset.
+
+ The symbol #Mmap has the name <tt>"map"</tt> 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 ¤Ï <tt>"map"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ mchar_define_charset () ¤Ç¥Þ¥Ã¥×·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú¿ô¤È
+ ¤·¤ÆÍѤ¤¤é¤ì¤ë¡£*/
+
+MSymbol Mmap;
+/*=*/
+
+/***en @brief Symbol for the unify type method of charset.
+
+ The symbol #Munify has the name <tt>"unify"</tt> 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 ¤Ï <tt>"inherit"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ mchar_define_charset () ¤ÇÁê³·¿¤Î¥á¥½¥Ã¥É¤ò»ØÄꤹ¤ë¾ì¹ç¤Î°ú¿ô¤È¤·
+ ¤ÆÍѤ¤¤é¤ì¤ë¡£*/
+
+MSymbol Munify;
+/*=*/
+
+/***en
+ @brief Symbol for the subset type method of charset.
+
+ The symbol #Msubset has the name <tt>"subset"</tt> 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 <tt>"superset"</tt> 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:
+
+ <ul>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> Key is #Mmin_char, value is an integer
+
+ The value specifies the minimum character code of the charset.
+ The default value is 0.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ </ul>
+
+ @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:
+*/
--- /dev/null
+/* 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. <code_range>[4N] is a
+ minimum byte code of the (N+1)th dimension, <code_range>[4N+1]
+ is a maximum byte code of the (N+1)th dimension,
+ <code_range>[4N+2] is (<code_range>[4N+1] - <code_range>[4N] +
+ 1), <code_range>[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 <code_range>. It may be
+ smaller than <min_code>. */
+ int code_range_min_code;
+
+ /** Nonzero means there is no gap in code points of the charset. If
+ <dimension> is 1, <no_code_gap> is always 1. Otherwise,
+ <no_code_gap> is 1 iff <code_range>[4N] is zero and
+ <code_range>[4N+1] is 256 for N = 0..<dimension>-2. If
+ <no_code_gap> is nonzero, "char-index" is "code-point" -
+ <min_code>. */
+ int no_code_gap;
+
+ /** If the byte code B is valid in the (N+1)th dimension,
+ (<code_range_mask>[B] & (1 << N)) is 1. Otherwise,
+ (<code_range_mask>[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
+ <ascii_compatible> is nonzero, <min_char> 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
+ <final_byte> 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
+ <method> 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 <method> is Mmap or Munify. */
+ MCharTable *encoder;
+
+ int unified_max;
+
+ /** Array of pointers to parent charsets. Used only when <method>
+ 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_ */
--- /dev/null
+/* 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 <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#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 ((<depth> << 24) | <min_char>). */
+ 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 <default_value>. */
+ 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;
+};
+
+
+\f
+
+/* 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 <stdio.h>
+
+/* 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, ")");
+}
+
+\f
+/* 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 */
+
+\f
+/* External API */
+
+/*** @addtogroup m17nChartable */
+/*** @{ */
+/*=*/
+
+/***en
+ @brief Symbol whose name is "char-table".
+
+ The symbol @c Mchar_table has the name <tt>"char-table"</tt>. */
+
+/***ja
+ @brief "char-table" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë
+
+ ÊÑ¿ô @c Mchar_table ¤Ï̾Á° <tt>"char-table"</tt> ¤ò»ý¤ÄÄêµÁºÑ¤ß¥·¥ó
+ ¥Ü¥ë¤Ç¤¢¤ë¡£ */
+
+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:
+*/
--- /dev/null
+/* 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_ */
+
--- /dev/null
+/* 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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#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 <type>. */
+ void *extra_info;
+
+ /** Pointer to information referred on conversion. The meaning
+ depends on <type>. 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;
+
+
+\f
+/* 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 ? "<U+%04X>"
+ : c < 0xE000 ? "<M+%04X>"
+ : c < 0x10000 ? "<U+%04X>"
+ : c < 0x110000 ? "<U+%06X>"
+ : "<M+%06X>");
+ 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);
+}
+
+
+\f
+/* 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);
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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);
+}
+
+\f
+/* 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
+
+\f
+/* 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 */
+\f
+/* 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 <tt>"us-ascii"</tt> and
+ represents a coding system for the CES US-ASCII. */
+
+/***ja
+ @brief MIME charset "US-ASCII" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë @c Mcoding_us_ascii ¤Ï <tt>"us-ascii"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ MIME charset <tt>"US-ASCII"</tt> ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ¤ë¤¿¤á
+ ¤Ë»È¤ï¤ì¤ë¡£
+ */
+MSymbol Mcoding_us_ascii;
+/*=*/
+
+/***en
+ @brief Symbol for the coding system ISO-8859-1
+
+ The symbol #Mcoding_iso_8859_1 has name <tt>"iso-8859-1"</tt> and
+ represents a coding system for the CES ISO-8859-1. */
+
+/***ja
+ @brief MIME charset "ISO-8859-1" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë @c Mcoding_iso_8859_1 ¤Ï <tt>"iso-8859-1"</tt> ¤È¤¤¤¦Ì¾Á°
+ ¤ò»ý¤Á¡¢MIME charset <tt>"ISO-8859-1"</tt> ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»Ø
+ Äꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mcoding_iso_8859_1;
+/*=*/
+
+/***en
+ @brief Symbol for the coding system UTF-8
+
+ The symbol #Mcoding_utf_8 has name <tt>"utf-8"</tt> and represents
+ a coding system for the CES UTF-8. */
+
+/***ja
+ @brief RFC 2279 ¤Î "UTF-8" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë¡ÊUnicode ÍÑ¡Ë
+
+ ¥·¥ó¥Ü¥ë @c Mcoding_utf_8 ¤Ï <tt>"utf-8"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë<tt>"UTF-8"</tt> ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ¤ë
+ ¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£
+ */
+
+MSymbol Mcoding_utf_8;
+/*=*/
+
+/***en
+ @brief UTF-8-FULL
+
+
+ The symbol #Mcoding_utf_8_full has name <tt>"utf-8-full"</tt> 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 ¤Ï <tt>"utf-8-full"</tt> ¤È¤¤¤¦Ì¾Á°
+ ¤ò»ý¤Á¡¢RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë<tt>"UTF-8"</tt> ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò
+ »ØÄꤹ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï m17n ¥é¥¤¥Ö¥é¥ê¤¬°·¤¦Á´¤Æ¤Î
+ ʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹¤ë¡£ */
+
+MSymbol Mcoding_utf_8_full;
+/*=*/
+
+/***en
+ @brief UTF-16
+
+ The symbol #Mcoding_utf_16 has name <tt>"utf-16"</tt> and
+ represents a coding system for the CES UTF-16 (RFC 2279). */
+/***ja
+ @brief RFC 2781 ¤Î "UTF-16" ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤Î¥·¥ó¥Ü¥ë
+
+ ¥·¥ó¥Ü¥ë @c Mcoding_utf_16 ¤Ï <tt>"utf-16"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ RFC 2279 ¤ÇÄêµÁ¤µ¤ì¤ë<tt>"UTF-16"</tt> ¤ËÂбþ¤¹¤ë¥³¡¼¥É·Ï¤ò»ØÄꤹ
+ ¤ë¤¿¤á¤Ë»È¤ï¤ì¤ë¡£¤³¤Î¥³¡¼¥É·Ï¤Ï Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤ò¥µ¥Ý¡¼¥È¤¹
+ ¤ë¡£ */
+
+MSymbol Mcoding_utf_16;
+/*=*/
+
+/***en
+ @brief UTF-16BE
+
+ The symbol #Mcoding_utf_16be has name <tt>"utf-16be"</tt> 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 <tt>"utf-16le"</tt> 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 <tt>"utf-32"</tt> 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 <tt>"utf-32be"</tt> 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 <tt>"utf-32le"</tt> 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 <tt>"sjis"</tt> 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 <tt>"maybe"</tt>. 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
+ <tt>"coding"</tt>. */
+
+/***ja
+ @brief ¥·¥ó¥Ü¥ë @c Mcoding
+
+ ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥¡¼¤¬ @c Mcoding ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥¥¹¥È¥×
+ ¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£¥·¥ó¥Ü¥ë @c Mcoding ¤Ï <tt>"coding"</tt> ¤È¤¤¤¦Ì¾
+ Á°¤Ç¤¢¤é¤«¤¸¤áÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£ */
+
+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:
+
+ <ul>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <ul>
+
+ <li> #Mreset_at_eol
+
+ If this flag exits, designation and invocation status is reset to
+ the initial state at the end of line.
+
+ <li> #Mreset_at_cntl
+
+ If this flag exists, designation and invocation status is reset to
+ the initial state at a control character.
+
+ <li> #Meight_bit
+
+ If this flag exists, the graphic plane right is used.
+
+ <li> #Mlong_form
+
+ If this flag exists, the over-long escape sequences (ESC '$' '('
+ <final_byte>) are used for designating the charsets JISX0208.1978,
+ GB2312, and JISX0208.
+
+ <li> #Mdesignation_g0
+
+ If this flag and #Mfull_support exists, designates charsets not
+ listed in the charset list to the graphic register G0.
+
+ <li> #Mdesignation_g1
+
+ If this flag and #Mfull_support exists, designates charsets not
+ listed in the charset list to the graphic register G1.
+
+ <li> #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.
+
+ <li> #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.
+
+ <li> #Mlocking_shift
+
+ If this flag exists, use locking shift.
+
+ <li> #Msingle_shift
+
+ If this flag exists, use single shift.
+
+ <li> #Msingle_shift_7
+
+ If this flag exists, use 7-bit single shift code (0x19).
+
+ <li> #Meuc_tw_shift;
+
+ If this flag exists, use a special shifting according to EUC-TW.
+
+ <li> #Miso_6429
+
+ This flag is currently ignored.
+
+ <li> #Mrevision_number
+
+ If this flag exists, use a revision number escape sequence to
+ designate a charset that has a revision number.
+
+ <li> #Mfull_support
+
+ If this flag exists, support all charsets registered in the
+ International Registry.
+
+ </ul>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ </ul>
+
+ $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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 <i> m17n database</i>. 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.
+
+ <ul>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ </ul>
+
+ Below, the notation \<TAG1, TAG2, TAG3, TAG4\> 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 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë
+ ¤Ç¤è¤¤¡£
+
+ °Ê²¼¡¢ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG1, TAG2, TAG3, TAG4\> ¤È¤¤
+ ¤¦·Á¼°¤Çɽ¤ï¤¹¡£
+
+ ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () »È¤Ã¤Æ¥Ç¡¼
+ ¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase ·¿¡Ë¤Ø¤Î
+ ¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () ¤Ë¤è¤Ã¤Æ¼ÂºÝ
+ ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂÎ #MDatabase ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì
+ ¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
+
+ @latexonly \IPAlabel{database} @endlatexonly
+*/
+
+/*=*/
+
+#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
+/*** @addtogroup m17nInternal
+ @{ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+#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. <tag>[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 <loader>. If <loader>
+ 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;
+}
+
+\f
+/* 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 */
+
+\f
+/* 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:
+
+ <ul>
+
+ <li> If the tag is #Msymbol, the default value is #Mnil.
+ <li> If the tag is #Minteger, the default value is -1.
+ <li> Otherwise, the default value is @c NULL.
+
+ </ul>
+
+ 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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#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 <fribidi/fribidi.h>
+#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;
+
+\f
+/* 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 <type>, <rface>, <c>, <pos>, <to>,
+ <code> 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 <enabled> 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;
+}
+
+\f
+/* 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, ")");
+}
+\f
+
+/* 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 */
+
+\f
+/* 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:
+
+ <ol>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ </ol>
+
+ 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 <tt>XDrawString ()</tt>,
+ <tt>XmbDrawString ()</tt>, and <tt>XwcDrawString ()</tt> 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 ¥×¥í¥Ñ¥Æ¥£¤Î
+ Ãͤ«¤é°Ê²¼¤Î¥¢¥ë¥´¥ê¥º¥à¤ÇÁª¤Ð¤ì¤ë¡£
+
+ <ol>
+
+ <li> ¤½¤Îʸ»ú¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ @c Mlanguage ¤Ç¤¢
+ ¤ë¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£¤³¤ÎÃͤϸÀ¸ì¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil ¤Î¤¤
+ ¤º¤ì¤«¤Ç¤¢¤ë¡£¼¡¤Ë¥¡¼¤¬ @c Mscript ¤Ç¤¢¤ë¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£
+ ¤³¤ÎÃͤϥ¹¥¯¥ê¥×¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil ¤Î¤É¤Á¤é¤«¤Ç¤¢¤ë¡£
+
+ ¤É¤Á¤é¤â @c Mnil ¤Ê¤é¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£
+
+ ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤Î¸À¸ì¤È¥¹¥¯¥ê¥×¥È¤ÎÁȤ߹ç¤ï¤»¤ò»È¤Ã
+ ¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬
+ ¤ß¤Ä¤«¤ê¡¢¤«¤Ä¤½¤Î¥Õ¥©¥ó¥È¤Ç¸½ºß¤Îʸ»ú¤¬É½¼¨¤Ç¤¤ì¤Ð¡¢¤½¤Î¥Õ¥©
+ ¥ó¥È¤ò»È¤¦¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£
+
+ <li> ¤½¤Îʸ»ú¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ @c Mcharset ¤Ç¤¢¤ë
+ ¤â¤Î¤ÎÃͤòÄ´¤Ù¤ë¡£¤³¤ÎÃͤÏʸ»ú¥»¥Ã¥È¤òɽ¤ï¤¹¥·¥ó¥Ü¥ë¤« @c Mnil
+ ¤Î¤É¤Á¤é¤«¤Ç¤¢¤ë¡£@c Mnil ¤Ê¤é¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£
+
+ ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¸«¤Ä¤«¤Ã¤¿Ê¸»ú¥»¥Ã¥È»È¤Ã¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã
+ ¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬¤ß¤Ä¤«¤ê¡¢¤«¤Ä¤½¤Î¥Õ¥©
+ ¥ó¥È¤Ç¸½ºß¤Îʸ»ú¤¬É½¼¨¤Ç¤¤ì¤Ð¡¢¤½¤Î¥Õ¥©¥ó¥È¤ò»È¤¦¡£¤½¤¦¤Ç¤Ê
+ ¤±¤ì¤Ð¡¢¼¡¤Î¥¹¥Æ¥Ã¥×¤Ë¿Ê¤à¡£
+
+ <li> ¸½ºß¤Îʸ»ú¼«¿È¤ò»È¤Ã¤Æ¡¢¤½¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤Î¥Þ¥Ã¥Ô¥ó¥°¥Æ¡¼¥Ö
+ ¥ë¤ò¤Ò¤¯¡£¥Õ¥©¥ó¥È¤¬¸«¤Ä¤«¤ì¤Ð¤½¤ì¤ò»È¤¦¡£
+
+ </ol>
+
+ °Ê¾å¤Î¥¢¥ë¥´¥ê¥º¥à¤Ç¥Õ¥©¥ó¥È¤¬¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï¤½¤Îʸ»ú
+ ¤È¤·¤Æ¶õ¤Î»Í³Ñ·Á¤òɽ¼¨¤¹¤ë¡£
+
+ ¤³¤Î´Ø¿ô¤¬ÉÁ²è¤¹¤ë¤Î¤Ï¥°¥ê¥Õ¤ÎÁ°·Ê¿§¤À¤±¤Ç¤¢¤ë¡£ÇØ·Ê¿§¤ò»ØÄꤹ¤ë¤Ë
+ ¤Ï¡¢´Ø¿ô mdraw_image_text () ¤ò»È¤¦¤³¤È¡£
+
+ ¤³¤Î´Ø¿ô¤Ï¡¢X ¥¦¥£¥ó¥É¥¦¤Ë¤ª¤±¤ë <tt>XDrawString ()</tt>,
+ <tt>XmbDrawString ()</tt>, <tt>XwcDrawString ()</tt> ¤ËÁêÅö¤¹¤ë¡£
+
+ @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 <tt>XDrawImageString ()</tt>,
+ <tt>XmbDrawImageString ()</tt>, and <tt>XwcDrawImageString ()</tt>
+ 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 ¥¦¥£¥ó¥É¥¦¤Ë¤ª¤±¤ë <tt>XDrawImageString ()</tt>,
+ <tt>XmbDrawImageString ()</tt>, <tt>XwcDrawImageString ()</tt> ¤Ë
+ ÁêÅö¤¹¤ë¡£
+
+ @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 <two_dimensional> 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:
+*/
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#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;
+}
+
+\f
+
+/* 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 */
+
+\f
+/* 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"black"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"white"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"red"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"green"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"blue"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"cyan"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"yellow"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ<tt>"magenta"</tt> ¤È¤¤¤¦Ì¾Á°¤Î¥·¥ó¥Ü
+ ¥ë¤ò»ý¤Ä¡£Â¾¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϲ¼¤Ë¼¨¤¹¤È¤ª¤ê¤Ç¤¢¤ë¡£¤³¤Î
+ ¥Õ¥§¡¼¥¹¤ò»È¤Ã¤Æɽ¼¨¤µ¤ì¤¿ 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 <tt>"face"</tt>. 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 ¤Ï <tt>"face"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¡£¥¡¼¤¬ @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:
+*/
--- /dev/null
+/* 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 <frame>->realized_font_list. */
+ MRealizedFont *rfont;
+
+ /* Realized fontset, one of <frame>->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_ */
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+
+#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:
+;; <http://www.microsoft.om/typograph/otspec/scripttags.htm>
+LANGSYS ::= OTF-TAG
+;; OTF's Language System name (four letters) listed at:
+;; <http://www.microsoft.om/typograph/otspec/languagetags.htm>
+
+GSUB-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
+GPOS-FEATURES ::= [FEATURE[,FEATURE]*] | ' '
+FEATURE ::= OTF-TAG
+;; OTF's Feature name (four letters) listed at:
+;; <http://www.microsoft.om/typograph/otspec/???.htm>
+
+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;
+ /* <encoded>[GIDX - <encoded_offset>] 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);
+}
+
+\f
+/* 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 <pos> and <to> 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;
+}
+
+\f
+/* 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, ")");
+}
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#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 <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef HAVE_OTF
+#include <otf.h>
+#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;
+}
+
+\f
+
+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 */
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> 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.
+
+ <li> 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.
+
+ <li> 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.
+
+ </ul>
+
+ */
+
+/***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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
+
+
+\f
+/* 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 <font encoding>.
+ 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 <font size>.
+ 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;
+}
+
+
+\f
+/* 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 */
+
+\f
+
+/* 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 <tt>"foundry"</tt> 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 <tt>"family"</tt> 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 ¤Ï <tt>"family"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó
+ ¥È¤Î family ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú
+ ¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î family ¤ò»ØÄê
+ ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mfamily;
+
+/***en
+ @brief Key of font property specifying weight.
+
+ The variable #Mweight is a symbol of name <tt>"weight"</tt> 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 ¤Ï <tt>"weight"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó
+ ¥È¤Î weight ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú
+ ¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î weight ¤ò»ØÄê
+ ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mweight;
+
+/***en
+ @brief Key of font property specifying style.
+
+ The variable #Mstyle is a symbol of name <tt>"style"</tt> 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 ¤Ï <tt>"style"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó¥È
+ ¤Î style ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú¿ô¤È
+ ¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î style ¤ò»ØÄê
+ ¤¹¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mstyle;
+
+/***en
+ @brief Key of font property specifying stretch.
+
+ The variable #Mstretch is a symbol of name <tt>"stretch"</tt> 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 ¤Ï <tt>"stretch"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©
+ ¥ó¥È¤Î stretch ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î
+ °ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î stretch ¤ò»Ø
+ Äꤹ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ¤â»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mstretch;
+
+/***en
+ @brief Key of font property specifying additional style.
+
+ The variable #Madstyle is a symbol of name <tt>"adstyle"</tt> 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 ¤Ï <tt>"adstyle"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©
+ ¥ó¥È¤Î adstyle ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î
+ °ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î adstyle ¤ò»Ø
+ Äꤹ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */
+
+MSymbol Madstyle;
+
+/***en
+ @brief Key of font property specifying registry.
+
+ The variable #Mregistry is a symbol of name <tt>"registry"</tt>
+ 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 ¤Ï <tt>"registry"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©
+ ¥ó¥È¤Î registry ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë¡¢´Ø¿ô mfont_get_prop ()
+ ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mregistry;
+
+/***en
+ @brief Key of font property specifying size.
+
+ The variable #Msize is a symbol of name <tt>"size"</tt> 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 ¤Ï <tt>"size"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢¥Õ¥©¥ó¥È¤Î size ¥×¥í
+ ¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë¤È¤¤Ë´Ø¿ô mfont_get_prop () ¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£
+
+ ¤Þ¤¿¤³¤Î¥·¥ó¥Ü¥ë¤Ï¡¢¥Õ¥§¡¼¥¹Á´ÂΤˤª¤±¤ë¥Ç¥Õ¥©¥ë¥È¤Î size ¤ò»ØÄꤹ
+ ¤ëºÝ¤Ë¡¢¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */
+
+MSymbol Msize;
+
+/***en
+ @brief Key of font property specifying resolution.
+
+ The variable #Mresolution is a symbol of name <tt>"size"</tt> 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 ¤Ï <tt>"resolution"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ ¥Õ¥©¥ó¥È¤Î 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:
+*/
--- /dev/null
+/* 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 <font_list> of MDisplay.
+
+ FONT-OPENED: To store information of an opened font. Instances
+ appear only in <opend_list> of MDisplay.
+
+ FONT-SPEC: To store specifications of a font. Instances appear
+ only in <spec_list> 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).
+
+ <property>[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.
+
+ <prperty>[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 <font> matches with <request>. */
+ 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 <driver>->select or <driver>->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_ */
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#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 <fontset>->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);
+}
+
+\f
+
+/* 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 */
+
+\f
+/* 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 <i>fallback
+ fonts</i>.
+
+ 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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 <string.h>
+#include <ctype.h>
+
+#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;
+ /* <geometry>.x and <geometry>.y are not used. */
+ MInputGUIWinInfo client;
+ /* In the following members, <geometry> is relative to <client>. */
+ 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;
+}
+
+\f
+
+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 */
+
+\f
+/* 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:
+*/
--- /dev/null
+/* 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.
+
+ <ul>
+
+ <li> Internal Input Method
+
+ An internal input method has non @c Mnil LANGUAGE, and the body is
+ defined in the m17n database by the tag <Minput_method, LANGUAGE,
+ NAME>. 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.
+
+ <li> 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.
+
+ </ul>
+
+ 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 ¸À¸ì¾ðÊó¥Ù¡¼¥¹¤Ë <Minput_method, LANGUAGE, NAME>¤È¤¤¤¦¥¿¥°ÉÕ
+ ¤¤ÇÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤ËÂФ·¤Æ¡¢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 <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#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;
+}
+
+\f
+
+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, ")");
+}
+
+\f
+
+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 */
+
+\f
+/* 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 ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤΥǥե©¥ë
+ ¥È¤ÎÆþÎϥɥ饤¥Ð¤Ç¤¢¤ë¡£
+
+ ¤³¤Î¥É¥é¥¤¥Ð¤Î <callback> ¥á¥ó¥Ð¤Ï @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 <m17n.h> is included) or to #minput_gui_driver (if
+ <m17n-gui.h> 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 ¥¦¥£¥ó¥É¥¦¤Î <tt>XLookupString ()</tt> ¡¢
+ <tt>XmbLookupString ()</tt> ¡¢<tt>XwcLookupString ()</tt> ¤ËÂбþ¤¹
+ ¤ë¡£
+
+ @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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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
+
+\f
+/** 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))
+
+\f
+/** 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
+
+\f
+
+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 <ref_count_extended> is zero, a function to free the
+ object. */
+ void (*freer) (void *);
+ /**en If <ref_count_extended> 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);
+
+\f
+
+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))
+
+\f
+
+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:
+*/
--- /dev/null
+/* 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 <config.h>
+#include <stdlib.h>
+#include "m17n.h"
+#include "m17n-misc.h"
+#include "internal.h"
+#include "language.h"
+#include "symbol.h"
+
+\f
+/* 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)
+{
+}
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 <stdlib.h>
+#include "m17n-core.h"
+
+int
+main ()
+{
+ M17N_INIT ();
+ M17N_FINI ();
+ exit (0);
+}
--- /dev/null
+/* 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 <stdlib.h>
+#include "m17n-gui.h"
+#include "m17n-misc.h"
+
+int
+main ()
+{
+ M17N_INIT ();
+ M17N_FINI ();
+ exit (0);
+}
--- /dev/null
+/* 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 <stdlib.h>
+#include <m17n.h>
+
+int
+main ()
+{
+ M17N_INIT ();
+ M17N_FINI ();
+ exit (0);
+}
--- /dev/null
+/* 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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#include <time.h>
+
+#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 <str>. */
+ 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;
+}
+
+\f
+/* 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 */
+
+\f
+/* 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 ¤ò¸½ºß¤Î¥í¥±¡¼¥ë¤È¤¹¤ë¡£¤³¤Î´Ø
+ ¿ô¤Ï¥·¥¹¥Æ¥à´Ø¿ô <tt>setlocale ()</tt> ¤ò¸Æ¤Ó¡¢³°ÉôÊÑ¿ô @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:
+*/
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xlocale.h>
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
+#include <X11/Xatom.h>
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+
+#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, <display> 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;
+}
+
+\f
+/** 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);
+ }
+ }
+}
+
+\f
+
+/* 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;
+}
+
+\f
+
+#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
+
+\f
+
+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 */
+\f
+/* 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:
+*/
--- /dev/null
+/* 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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
+
+#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 <preedit_attrs> and
+ <status_attrs> are set to @c NULL. */
+ /***ja @c XCreateIC ¤Î @c XNInputStyle ¤Ë³¤¯°ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£
+ ¥¼¥í¤Ê¤é¤Ð¡¢ ( @c XIMPreeditNothing | @c XIMStatusNothing) ¤¬ÍÑ
+ ¤¤¤é¤ì¡¢ <preedit_attrs> ¤È <status_attrs> ¤Ï @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:
+*/
--- /dev/null
+/* 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
+
+ <em>API LEVELS</em>
+
+ The API of the m17n library is divided into these four.
+
+ <ol>
+ <li> 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 <m17n-core.h> and be linked by -lm17n-core.
+
+ <li> 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 <m17n.h> and be linked by
+ -lm17n-core -lm17n. With that, CORE API is also available.
+
+ <li> 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 <m17n-gui.h> and
+ <m17n-X.h>, and be linked by -lm17n-core -lm17n -lm17n-X. With
+ that, CORE and SHELL APIs are also available.
+
+ <li> 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 <m17n-misc.h> in addition to one of <m17n-core.h>,
+ <m17n.h>, and <m17n-gui.h>.
+
+ </ol>
+
+ See also the section @ref m17n-config "m17n-config(1)".
+
+ <em>ENVIRONMENT VARIABLE</em>
+
+ The m17n library pays attention to these environment variables.
+
+ <ul>
+ <li> @c M17NDIR
+
+ Name of a directory that contains data of the m17n database. See
+ @ref m17nDatabase for the detail.
+
+ <li> @c MDEBUG_XXXX
+
+ Environment variables whose name start by "MDEBUG_" controls
+ printing of debug information. See @ref m17nDebug for the detail.
+
+ </ul>
+
+ <em>API NAMING CONVENTION</em>
+
+ 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).
+
+ <ul>
+
+ <li> functions -- mobject () or mobject_xxx ()
+
+ They start with 'm' followed by lower case object name. For
+ example, msymbol (), mtext_ref_char (), mdraw_text ().
+
+ <li> non-symbol variables -- mobject, or mobject_xxx
+
+ The naming convention is the same as functions (e.g. mface_large).
+
+ <li> 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").
+
+ <li> macross -- MOBJECT_XXX
+
+ They start by 'M' followed by upper case object names.
+
+ <li> types -- MObject or MObjectXxx
+
+ They start by 'M' followed by capitalized object names (e.g.
+ MConverter, MInputDriver).
+
+ </ul>
+
+ */
+
+/***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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#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;
+
+\f
+/* 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)
+
+
+\f
+/* 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 ¤¬»Ø¤¹´Ø¿ô¤ò¸Æ¤Ö¡£¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢Ã±¤Ë
+ <tt>exit ()</tt> ¤ò¸Æ¤Ö¤³¤È¤Ë¤Ê¤Ã¤Æ¤¤¤ë¡£
+*/
+
+/*** @{ */
+
+/*=*/
+
+/***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
+
+ ½é´üÀßÄê¤Ç¤Ï¡¢¤³¤ÎÊÑ¿ô¤Ïñ¤Ë <tt>exit ()</tt> ¤ò $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.
+
+ <ul>
+
+ <li> Environment variables to control printing of various
+ information.
+
+ <ul>
+
+ <li> MDEBUG_INIT -- If set to 1, print information about the
+ library initialization on the call of M17N_INIT ().
+
+ <li> MDEBUG_FINI -- If set to 1, print counts of objects that are
+ not yet freed on the call of M17N_FINI ().
+
+ <li> MDEBUG_CHARSET -- If set to 1, print information about
+ charsets being loaded from the m17n database.
+
+ <li> MDEBUG_CODING -- If set to 1, print information about coding
+ systems being loaded from the m17n database.
+
+ <li> MDEBUG_DATABASE -- If set to 1, print information about
+ data being loaded from the m17n database.
+
+ <li> MDEBUG_FONT -- If set to 1, print information about fonts
+ being selected and opened.
+
+ <li> MDEBUG_FONT_FLT -- If set to 1, print information about which
+ command of Font Layout Table are being executed.
+
+ <li> MDEBUG_FONT_OTF -- If set to 1, print information about which
+ feature of OpenType Layout Table are being executed.
+
+ <li> MDEBUG_INPUT -- If set to 1, print information about how an
+ input method is running.
+
+ <li> MDEBUG_ALL -- Setting this variable to 1 is equivalent to
+ setting all the above variables to 1.
+
+ </ul>
+
+ <li> Functions to print various objects in a human readable way.
+ See the documentation of mdebug_dump_XXXX () functions.
+
+ <li> The hook function called on an error. See the documentation
+ of mdebug_hook ().
+
+ </ul>
+*/
+
+/*=*/
+/*** @{ */
+/*=*/
+
+/***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:
+*/
--- /dev/null
+/* 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:
+*/
--- /dev/null
+/* 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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);
+}
+
+\f
+/* Internal API */
+
+/*** @} */
+#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
+
+\f
+/* 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.
+
+ <ul>
+
+ <li> #Mface, the value type must be <tt>(MFace *)</tt>.
+
+ The value is used as the default face of the frame.
+
+ </ul>
+
+ 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.
+
+ <ul>
+
+ <li> #Mdrawable, the value type must be <tt>Drawable</tt>
+
+ 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.
+
+ <li> #Mwidget, the value type must be <tt>Widget</tt>.
+
+ 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.
+
+ <li> #Mdepth, the value type must be <tt>unsigned</tt>.
+
+ The created frame can be used for drawables of the specified
+ depth.
+
+ <li> #Mscreen, the value type must be <tt>(Screen *)</tt>.
+
+ 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.
+
+ <li> #Mdisplay, the value type must be <tt>(Display *)</tt>.
+
+ 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.
+
+ <li> #Mcolormap, the value type must be <tt>(Colormap)</tt>.
+
+ The created frame uses the specified colormap.
+
+ </ul>
+
+ @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
+ ¤Ï³Æ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î <tt>main ()</tt> ´Ø¿ô¤ËÍ¿¤¨¤é¤ì¤ë¤â¤Î¤ÈƱ¤¸
+ ¤â¤Î¤Ç¤¢¤ë¡£¤Ä¤Þ¤ê¥³¥Þ¥ó¥É¥é¥¤¥ó°ú¿ô¤ò´Þ¤ó¤Ç¤¤¤ëɬÍפ¬¤¢¤ë¡£»ÈÍѤÇ
+ ¤¤ë°ú¿ô¤Ï¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Ë°Í¸¤¹¤ë¡£
+
+ m17n-X ¥é¥¤¥Ö¥é¥ê¤Ë¤ª¤±¤ë¤³¤Î´Ø¿ô¤Ï¡¢<tt>XOpenDisplay (NULL)</tt>
+ ¤ò»È¤Ã¤Æ¥Ç¥Õ¥©¥ë¥È¤Î¥Ç¥£¥¹¥×¥ì¥¤¤ò³«¤¡¢¼¡¤¤¤Ç <tt>DefaultScreen
+ (DISPLAY)</tt> ¤ò»È¤Ã¤Æ¥Ç¥Õ¥©¥ë¥È¤Î¥¹¥¯¥ê¡¼¥ó¤òÆÀ¤¿¸å¡¢ºî¤é¤ì¤¿¥Õ¥ì¡¼¥à
+ ¤Ë¤³¤Îξ¼Ô¤ò´ØÏ¢ÉÕ¤±¤ë¡£
+
+ 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:
+*/
--- /dev/null
+/* 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 <m17n.h>
+#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 <format> 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 <format> 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
+ <orientation_reversed> 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 <two_dimensional> 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 <format>.
+
+ 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 <cursor_width> is nonzero, cursor is not
+ displayed. */
+ int cursor_pos;
+
+ /***en If nonzero, display a cursor at the character position
+ <cursor_pos>. 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 <cursor_width> is also nonzero, display double
+ bar cursors; at the character position <cursor_pos> 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 <mt>. */
+ /***ja ¥Õ¥©¥ó¥È¥»¥Ã¥È¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¡£¤³¤ì¤Ï M-text Æâ¤Ç»Ø
+ Äꤵ¤ì¤¿¥Õ¥§¡¼¥¹¤Î¥Õ¥©¥ó¥È¥»¥Ã¥È¤ËÍ¥À褹¤ë*/
+ MFace *face;
+
+ /***en Pointer to a draw control object. The M-text <mt> 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:
+*/
--- /dev/null
+/* 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:
+*/
--- /dev/null
+/* 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 <config.h>
+#include <stdio.h>
+
+#include "m17n.h"
+#include "m17n-misc.h"
+#include "internal.h"
+#include "charset.h"
+#include "coding.h"
+
+static int shell_initialized;
+
+\f
+/* Internal API */
+
+\f
+/* 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:
+*/
--- /dev/null
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifndef _M17N_CORE_H_
+#include <m17n-core.h>
+#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 "<U+XXXX>" (if the character is a Unicode character) or
+ with "<M+XXXXXX>" (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 <code_unit_bits> 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 <code_unit_bits> 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 <info>
+ 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 ¤Î<info>°Ê³°¤ÎÁ´
+ ¥á¥ó¥Ð¡¼¤¬¥»¥Ã¥È¤µ¤ì¤¿¸å¤Ç¡¢´Ø¿ô 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
+ <info> 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 ¤Î <info>
+ °Ê³°¤ÎÁ´¥á¥ó¥Ð¡¼¤¬¥»¥Ã¥È¤µ¤ì¤¿¸å¤Ç¡¢´Ø¿ô 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 <callback>.
+
+ 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 ¤òÊÖ¤¹¡£
+
+ ¥á¥ó¥Ð <callback> ¤ËɬÍפǤ¢¤ì¤Ð¡¢$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 <driver>.open_im ()
+ setups. */
+ /***ja <driver>.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 <mt> 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
+ <im>->driver.filter (). */
+ /***ja °Ê²¼¤Î¥á¥ó¥Ð¤Î»ÈÍÑË¡¤ÏÆþÎϥɥ饤¥Ð¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£°Ê²¼¤ÎÀâÌÀ
+ ¤Ï¡¢ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤÎÆþÎϥɥ饤¥Ð¤ËÂФ¹¤ë¤â¤Î¤Ç¤¢¤ë¡£ */
+
+ /***en Pointer to extra information that <im>->driver.create_ic ()
+ setups. It is used to record the internal state of the input
+ context. */
+ /***ja <im>->driver.create_ic () ¤¬ÀßÄꤹ¤ëÄɲþðÊó¤Ø¤Î¥Ý¥¤¥ó¥¿¡£Æþ
+ ÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ÎÆâÉô¾õÂÖ¤òµÏ¿¤¹¤ë¤¿¤á¤ËÍѤ¤¤é¤ì¤ë¡£ */
+ void *info;
+
+ /***en M-text describing the current status of the input
+ context. */
+ /***ja ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¸½ºß¤Î¾õ¶·¤òɽ¤¹ M-text */
+ MText *status;
+
+ /***en The function <im>->driver.filter () sets the value to 1 when
+ it changes <status>. */
+ int status_changed;
+
+ /***en M-text containing the current preedit text. The function
+ <im>->driver.filter () sets the value. */
+ /***ja ¸½ºß¤Î preedit ¥Æ¥¥¹¥È¤ò´Þ¤à M-text */
+ MText *preedit;
+
+ /***en The function <im>->driver.filter () sets the value to 1 when
+ it changes <preedit>. */
+ int preedit_changed;
+
+ /***en Cursor position of <preedit>. */
+ /***ja <preedit>¤Î¥«¡¼¥½¥ë°ÌÃÖ */
+ 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 <im>->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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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 (<tt>char *</tt> or <tt>unsigned
+ char *</tt>). 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¡Ê<tt>char *</tt> ¤ä <tt>unsigned
+ char *</tt>¡Ë¤Ç¤Ï¤Ê¤¯ 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 <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#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);
+}
+
+\f
+/* 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 */
+
+\f
+/* 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 <stdio.h>
+
+/*** @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:
+*/
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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
+ <tt>(void *)</tt>.
+
+ 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 <stdio.h>
+#include <string.h>
+
+#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);
+}
+
+\f
+
+/* 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, ")");
+}
+
+\f
+/* 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, <tt>"abc\ def"</tt>
+ 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 */
+
+\f
+/* External API */
+
+/*** @addtogroup m17nPlist */
+/*** @{ */
+/*=*/
+
+/***en
+ @brief Symbol whose name is "integer".
+
+ The symbol @c Minteger has the name <tt>"integer"</tt>. 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 <tt>"plist"</tt>. 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 <tt>"mtext"</tt>. 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 ¤Ï <tt>"text"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä´ÉÍý
+ ¥¡¼¤Ç¤¢¤ë¡£ */
+
+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 <tt>(void *)</tt>. 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:
+*/
--- /dev/null
+/* 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, <val> and <next> is
+ NULL. If the value is a managing key, <val> 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_ */
--- /dev/null
+/* 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 <tt>(void
+ *)</tt>. "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 ÃÍ ¤«¤é¤Ê¤ë¡£¥¡¼¤Ï¤½
+ ¤ì¼«ÂÎ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢ÃÍ¤Ï <tt>(void *)</tt> ·¿¤Ë¥¥ã¥¹¥È¤Ç¤¤ë¤â
+ ¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£¡Ö¥·¥ó¥Ü¥ë 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 <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#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);
+}
+
+\f
+/* 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 */
+
+\f
+/* External API */
+
+/*** @addtogroup m17nSymbol */
+/*** @{ */
+
+/*=*/
+/***en
+ @brief Symbol whose name is "nil".
+
+ The symbol #Mnil has the name <tt>"nil"</tt> 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 ¤Ï <tt>"nil"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢°ìÈ̤ˡֵ¶¡×
+ ¤ò°ÕÌ£¤¹¤ë¡£#Mnil ¼«¿È¤Ï¤¤¤«¤Ê¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤â»ý¤¿¤Ê¤¤¡£ */
+
+MSymbol Mnil;
+
+/*=*/
+
+/***en
+ @brief Symbol whose name is "t".
+
+ The symbol #Mt has the name <tt>"t"</tt> and, in general,
+ represents @e true or @e yes. */
+
+/***ja
+ @brief ÄêµÁºÑ¥·¥ó¥Ü¥ë Mt
+
+ ¥·¥ó¥Ü¥ë #Mt ¤Ï <tt>"t"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢°ìÈ̤ˡֿ¿¡×¤ò°Õ
+ Ì£¤¹¤ë¡£ */
+
+MSymbol Mt;
+
+/*=*/
+
+/***en
+ @brief Symbol whose name is "string".
+
+ The symbol #Mstring has the name <tt>"string"</tt> and is used
+ as an argument of the functions mchar_define_property (),
+ etc. */
+
+/***ja
+ @brief "string" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë
+
+ ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë #Mstring ¤Ï <tt>"string"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ ´Ø¿ô mchar_define_property () Åù¤Î°ú¿ô¤È¤·¤Æ»È¤ï¤ì¤ë¡£ */
+
+MSymbol Mstring;
+
+/*=*/
+
+/***en
+ @brief Symbol whose name is "symbol".
+
+ The symbol #Msymbol has the name <tt>"symbol"</tt> and is used
+ as an argument of the functions mchar_define_property (),
+ etc. */
+
+/***ja
+ @brief "symbol" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë
+
+ ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë #Msymbol ¤Ï <tt>"symbol"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Á¡¢
+ ´Ø¿ô 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 <stdio.h>
+
+/*** @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:
+*/
--- /dev/null
+/* 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 <name>. */
+ 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_ */
--- /dev/null
+/* 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 <tt>(void
+ *)</tt>. 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 ÃÍ ¤«¤é¤Ê¤ë¡£¥¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢
+ ¤ê¡¢ÃÍ¤Ï <tt>(void *)</tt> ·¿¤Ë¥¥ã¥¹¥È¤Ç¤¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£
+ ¾¤Î¥¿¥¤¥×¤Î¥×¥í¥Ñ¥Æ¥£¤È°Û¤Ê¤ê¡¢Æ±°ì¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÏÊ£¿ô¤ÎÃÍ
+ ¤ò»ý¤Ä¤³¤È¤¬µö¤µ¤ì¤ë¡£¡Ö@c Mxxx ¤È¤¤¤¦¥¡¼¤ò»ý¤Ä¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¡×
+ ¤Î¤³¤È¤ò´Êñ¤Ë¡Öxxx ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */
+
+/*=*/
+
+#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
+/*** @addtogroup m17nInternal
+ @{ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_XML2
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xpath.h>
+#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 <stack>. */
+ int nprops;
+
+ /** Length of <stack>. */
+ int stack_length;
+
+ /** Start and end character positions of the interval. If <end> is
+ negative, this interval is not in use. */
+ int start, end;
+
+ /** Pointers to the previous and next intervals. If <start> is 0,
+ <prev> is NULL and this interval is pointed by MTextPlist->head.
+ If <end> is the size of the M-text, <next> 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. <head>->start is always 0.
+ <tail->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 "<?xml version=\"1.0\" ?>\n\
+<!DOCTYPE mtext [\n\
+ <!ELEMENT mtext (property*,body+)>\n\
+ <!ELEMENT property EMPTY>\n\
+ <!ELEMENT body (#PCDATA)>\n\
+ <!ATTLIST property key CDATA #REQUIRED>\n\
+ <!ATTLIST property value CDATA #REQUIRED>\n\
+ <!ATTLIST property from CDATA #REQUIRED>\n\
+ <!ATTLIST property to CDATA #REQUIRED>\n\
+ <!ATTLIST property control CDATA #REQUIRED>\n\
+ ]>\n\
+<mtext>\n\
+</mtext>"
+
+\f
+/* for debugging... */
+#include <stdio.h>
+
+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;
+ }
+ }
+}
+
+\f
+/* 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 */
+
+\f
+/** 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
+<!DOCTYPE mtext [
+ <!ELEMENT mtext (property*,body+)>
+ <!ELEMENT property EMPTY>
+ <!ELEMENT body (#PCDATA)>
+ <!ATTLIST property key CDATA #REQUIRED>
+ <!ATTLIST property value CDATA #REQUIRED>
+ <!ATTLIST property from CDATA #REQUIRED>
+ <!ATTLIST property to CDATA #REQUIRED>
+ <!ATTLIST property control CDATA #REQUIRED>
+ ]>
+@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
+<!DOCTYPE mtext [
+ <!ELEMENT mtext (property*,body+)>
+ <!ELEMENT property EMPTY>
+ <!ELEMENT body (#PCDATA)>
+ <!ATTLIST property key CDATA #REQUIRED>
+ <!ATTLIST property value CDATA #REQUIRED>
+ <!ATTLIST property from CDATA #REQUIRED>
+ <!ATTLIST property to CDATA #REQUIRED>
+ <!ATTLIST property control CDATA #REQUIRED>
+ ]>
+@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:
+*/
--- /dev/null
+/* 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 <mt> 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_ */